缓存数据来源
本章提供了用于缓存数据源使用作为临时记录系统的连贯性。本章包括样品和实施注意事项。
本章包含以下各节:
的缓存数据来源概述
选择一个高速缓存策略
创建一个缓存存储实现
在缓存存储实施堵漏
样品的缓存存储
可控的缓存存储范例
实施注意事项
14.1缓存数据源概述
Coherence支持透明,读/写缓存的任何数据源,包括数据库,Web服务,打包应用程序和文件系统,数据库是最常见的使用案例。作为速记, “数据库”是用来形容任何后端数据源。有效的缓存必须同时支持密集只读和读/写操作,缓存和数据库进行读/写操作,必须保持完全同步。为了实现高速缓存的数据源, Coherence支持读,写,刷新预后写缓存。
注意事项:
缓存Read-through/write-through (及变种)拟使用分区(分布式)缓存拓扑(通过扩展,近缓存) 。本地缓存支持这个功能的一个子集。复制的,不应该被使用和乐观的高速缓存的。
在本节包括以下主题:
可插拔缓存存储
通过读缓存
通过写高速缓存
后写缓存
缓存刷新超前的
14.1.1可插拔缓存存储
缓存存储是一个特定于应用程序的适配器,用于连接到基础数据源的缓存。缓存存储实现访问数据源,使用数据访问机制(例如时,Hibernate , JPA , TopLink Essentials中的特定于应用程序的JDBC调用另一个应用程序,主机,另一个缓存,等等) 。缓存存储了解如何建立一个使用Java对象检索的数据从数据源,地图和一个对象写入到数据源,从数据源中删除一个对象。
数据源架构,应用程序类的布局,以及经营环境的特定数据源连接策略和数据源到应用程序对象的映射信息。因此,该映射信息,必须提供由应用程序开发缓存存储区实现的形式。请参阅“创建一个缓存存储区实施”的详细信息。
14.1.2读取高速缓存
当一个应用程序请求的缓存条目,例如key的X , X是不在缓存中,coherence自动缓存存储的代表,并要求它载入X,从基础数据源。如果X中存在的数据源,缓存存储负载,返回它的连贯性,连贯性的地方在缓存中以供将来使用,最后返回X的请求的应用程序代码。这就是所谓的读通缓存。刷新Ahead Cache的功能可能会进一步提高读取性能(降低感知延迟) 。参见“刷新预缓存”的详细信息。
图14-1通读缓存
14.1.3通过写高速缓存
coherence可以处理到数据源更新两种不同的方式,第一个是写式。在这种情况下,当应用程序更新的一块缓存中的数据(即调用put(...)来改变缓存条目)操作不完整(即认沽不返回),直到连贯性已经走过了缓存存储区,并成功地将数据存储到底层数据源。这不,提高写入性能,因为你还在处理的延迟写入到数据源。背后写缓存功能的目的是提高写入性能
图14-2写通过缓存
14.1.4 Write-Behind缓存
写在后面的情况下,修改后的缓存条目被异步写入到数据源配置的延迟后,无论是后10秒,20分钟,一天,一个星期甚至更长的时间。请注意,这仅适用于高速缓存插入和更新 - 从数据源同步缓存条目被删除。对于后写缓存,Coherence维护必须更新数据源中的数据,写队列后面。当缓存的应用程序更新的X , X是写后队列(如果它不存在,否则,它会被替换) ,并在指定的写延迟连贯后面调用缓存存储,更新基础数据源与X.注的最新状态,写后延迟相对于第一换句话说,在数据源中的数据不会滞后缓存写后延迟超过了一系列的修改。
其结果是一个“读一次,并写在一个配置的时间间隔(即,情况少得多)业务情景。这种类型的体系结构主要有四个好处:
的应用程序的性能有提高的,因为用户不必等待数据被写入到基础数据源。 (这些数据被写入后,由不同的执行线程。)
应用体验大幅降低数据库负载:由于量两者之和读取和写入操作是减少的,所以数据库负载。减少读取缓存,与任何其他的缓存方法。写道,这是典型的更昂贵的操作,通常被简化,因为写后间隔内多次更改同一个对象“联合起来”,只能写一次到基础数据源( write-coalescing“写凝聚” ) 。此外,写入多个缓存条目可以合并成一个单一的数据库事务( write-combining“写结合”)通过使用CacheStore.storeAll ()方法。
该应用程序是有点从数据库故障绝缘:在这样一种方式,写入失败导致对象被重新排队写,写功能可以配置。如果应用程序正在使用的数据在相干缓存,应用程序可以继续运行而不数据库。这是很容易实现的,当使用分区的高速缓存一致性问题,这在所有参与的群集节点(启用本地存储)分区整个缓存,从而使巨大的缓存。
线性可扩展性:对于一个应用程序来处理更多的并发用户,你只需要在集群中的节点数量增加;负载对数据库的影响,通过增加写后间隔可调谐。
图14-3后写缓存
14.1.4.1 Write-Behind要求
虽然启用后写缓存是一个简单的调整一项配置设置,确保写后面的工程,预期更多地参与。具体来说,应用程序的设计必须解决几个设计问题。
最直接影响的后写高速缓存数据库更新发生的缓存交易之外,通常高速缓存交易完成数据库事务(S)开始前。这意味着,在数据库事务必须永远不会失败,如果这不能被保证,那么必须容纳回滚。
后写可能会重新排序数据库更新,参照完整性约束必须允许序更新。从概念上讲,这是类似ISAM式存储(基于主键的访问没有冲突的更新与保证)使用数据库。如果其他应用程序共享数据库,这引入了一个新的挑战,也没有办法保证,背后写交易不冲突的外部更新。这意味着,必须将处理后写冲突启发式或由操作员手动调整升级。
作为一个经验法则,每个缓存条目更新映射到逻辑数据库交易是理想的,因为这保证了简单的数据库事务。
因为背后写有效地使缓存系统记录(直到背后写队列已被写入到磁盘),业务规则必须允许群集耐用(而非耐用磁盘)的存储数据和交易。
14.1.5刷新预缓存
在未来刷新的情况下,相干允许开发人员配置缓存和异步自动重新载入(刷新)最近访问的缓存条目从缓存加载器届满前。其结果是,已经进入了一个频繁访问的条目缓存后,应用程序不觉得对一个潜在的慢读缓存存储的影响因过期条目时重新加载。异步刷新时触发足够接近其到期时间是访问一个对象,如果对象的访问后,其到期时间,连贯执行同步读取缓存存储刷新其价值。
刷新预时间表示作为条目的期满时间的百分比。例如,假设在高速缓存中的条目的到期时间被设置为60秒,刷新预因子设定为0.5 。如果缓存的对象的访问后60秒,相干执行同步读取缓存存储, ,刷新其价值。但是,如果一个请求执行的条目超过30岁,但不到60秒,缓存中的当前值返回与连贯时间表异步重载缓存存储。
刷新提前是特别有用的,如果对象是由大量的用户访问。值在缓存中保持新鲜,避免重新加载缓存存储过多,可能导致延迟。
刷新提前因子指定的一致性高速缓存-config.xml文件中的<read-write-backing-map-scheme>元素的子元素的<refresh-ahead-factor>中的价值。刷新反超假设,你也缓存中的条目设置一个到期时间( <expiry-delay> ) 。
例14-1配置刷新反超的系数为0.5 ,并在本地缓存中条目的到期时间为20秒。如果访问一个条目,其到期时间在10秒内,它计划从缓存存储异步重载。
例14-1指定刷新预因子
<distributed-scheme>
<scheme-name>categories-cache-all-scheme</scheme-name>
<service-name>DistributedCache</service-name>
<backing-map-scheme>
<read-write-backing-map-scheme>
<scheme-name>categoriesLoaderScheme</scheme-name>
<internal-cache-scheme>
<local-scheme>
<scheme-ref>categories-eviction</scheme-ref>
</local-scheme>
</internal-cache-scheme>
<cachestore-scheme>
<class-scheme>
<class-name>
com.demo.cache.coherence.categories.CategoryCacheLoader
</class-name>
</class-scheme>
</cachestore-scheme>
<refresh-ahead-factor>0.5</refresh-ahead-factor>
</read-write-backing-map-scheme>
</backing-map-scheme>
<autostart>true</autostart>
</distributed-scheme>
<local-scheme>
<scheme-name>categories-eviction</scheme-name>
<expiry-delay>20s</expiry-delay>
</local-scheme>
14.2选择一个缓存策略
本节比较和对比几种缓存策略的好处。
Read-Through/Write-Through与Cache-Aside
Refresh-Ahead 与Read-Through
Write-Behind 与 Write-Through
14.2.1 Read-Through/Write-Through 与Cache-Aside
缓存预留模式在集群环境中有两种常见的方法。一个涉及检查高速缓存未命中,然后查询数据库,填充缓存,并继续应用处理。如果不同的应用程序线程在同一时间执行该处理,这可能会导致多库访问。另外,应用程序可以执行双重检查锁定(其中工程由于检查是原子的缓存条目) 。然而,这结果高速缓存未命中或数据库更新(集群锁,额外的读,集群解锁 - 多达10个额外的网络跳数,或6 - 8ms的一个典型的千兆以太网连接的大量开销,加上额外的处理开销,并增加了“锁定时间”的缓存条目) 。
通过使用内嵌的缓存条目已被锁定2网络跳数(而数据复制到备份服务器容错) 。此外,该锁被保持在本地分区上的雇主。此外,应用程序代码完全管理的高速缓存服务器上,这意味着只有一个控制节点子集直接访问数据库(导致更可预测的负载和安全) 。此外,这种去耦缓存客户端从数据库逻辑。
14.2.2 Refresh-Ahead 与Read-Through
刷新提前提供通读相比减少延迟,但只有当缓存缓存项可能会在未来需要,可以准确预测。随着这些预测的准确性,刷新提前提供降低延迟并没有增加额外的开销。不准确的预测率越高,影响越大吞吐量(更多不必要的请求被发送到数据库) - 甚至可能有负面影响的数据库延迟请求处理开始落后。
14.2.3 Write-Behind 与Write-Through
如果能满足要求后写缓存后写缓存可能会提供更高的吞吐量和写通过缓存相比,减少延迟。此外,后写高速缓存降低对数据库的负载(少写)的缓存服务器(缓存值减少反序列化) 。
14.3创建的CacheStore实现
缓存存储的实现是可插拔的,这取决于缓存的使用的数据源必须实现两个接口之一:
CacheLoader for read-only caches
CacheStore which extends CacheLoader to support read/write caches
在这些接口都位于在com.tangosol.net.cache包。 CacheLoader接口有两个主要的方法: load(Object key)和loadAll(Collection keys), 缓存存储接口添加方法store(Object key, Object value), storeAll(Map mapEntries), erase(Object key), 和eraseAll(Collection colKeys).
14.4插入缓存存储实现
插入一个 CacheStore模块,缓存配置元素内指定CacheStore的实现类的名称。
distributed-scheme, backing-map-scheme, cachestore-scheme, or read-write-backing-map-scheme
read-write-backing-map-scheme 配置com.tangosol.net.cache.ReadWriteBackingMap的。这个后盾地图是由两个关键要素:内部地图,实际上缓存数据(见计划内部缓存)和缓存存储模块与数据库(见的缓存存储方案) 。
例14-2说明了一个指定的缓存存储模块的缓存配置。的<init-params>元素包含到缓存存储的构造函数传递参数的有序列表。 {缓存名}配置宏被用来传递缓存名称缓存存储的实现,允许它被映射到数据库表。对于一个完整的可用宏的列表,请参阅“使用参数宏” 。
配置写后面刷新反超,更详细的信息,请参阅读写后盾地图计划,注意到写批处理因素,刷新反超因素,写重新排队阈值,和回滚缓存存储区失败的元素。
例14-2实例Cachestore 模块
<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config
coherence-cache-config.xsd">
<caching-scheme-mapping>
<cache-mapping>
<cache-name>com.company.dto.*</cache-name>
<scheme-name>distributed-rwbm</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<distributed-scheme>
<scheme-name>distributed-rwbm</scheme-name>
<backing-map-scheme>
<read-write-backing-map-scheme>
<internal-cache-scheme>
<local-scheme/>
</internal-cache-scheme>
<cachestore-scheme>
<class-scheme>
<class-name>com.company.MyCacheStore</class-name>
<init-params>
<init-param>
<param-type>java.lang.String</param-type>
<param-value>{cache-name}</param-value>
</init-param>
</init-params>
</class-scheme>
</cachestore-scheme>
</read-write-backing-map-scheme>
</backing-map-scheme>
</distributed-scheme>
</caching-schemes>
</cache-config>
注意事项:
Thread Count:缓存存储模块的使用大大增加了消费的高速缓存服务线程(即使是最快的数据库选择一个内存中的结构比更新慢几个数量级) 。因此,高速缓存服务的线程数必须增加(一般在10-100的范围内) 。线程池不足的最明显的症状是延迟增加缓存请求的支持数据库中没有相应的行为。
14.5 CacheStore样例
本节提供了一个非常基本的实施的com.tangosol.net.cache.CacheStore接口。实施例14-3使用一个单一的数据库使用JDBC连接,并且不使用批量操作。一个完整的实施将使用连接池,如果写后面的使用,实现CacheStore.storeAll ( )散装JDBC插入和更新。 “数据库缓存”提供了一个示例数据库缓存配置。
提示:
批量加载缓存的保存处理工作。下面的例子使用的put方法,值写入缓存店。通常情况下,执行批量负荷的putAll方法加工的努力和网络流量节省。批量装载欲了解更多信息,请参见第20章, “预加载缓存("Pre-Loading a Cache.) ”
例14-3实施的缓存存储接口
package com.tangosol.examples.coherence; import com.tangosol.net.cache.CacheStore; import com.tangosol.util.Base; import java.sql.DriverManager; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * 缓存存储接口实现的一个例子。 * * @author erm 2003.05.01 */ public class DBCacheStor extends Base implements CacheStore { // ----- constructors --------------------------------------------------- /** * 对于一个给定的数据库表结构DBCacheStore。 * * @param sTableName * the db table name */ public DBCacheStore(String sTableName) { m_sTableName = sTableName; configureConnection(); } /** * Set up the DB connection. */ protected void configureConnection() { try { Class.forName("org.gjt.mm.mysql.Driver"); m_con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD); m_con.setAutoCommit(true); } catch (Exception e) { throw ensureRuntimeException(e, "Connection failed"); } } // ---- accessors ------------------------------------------------------- /** * 获取的名称表缓存存储是持久的. * * @return the name of the table this CacheStore is persisting to */ public String getTableName() { return m_sTableName; } /** * 取得用于连接到数据库的连接。 * * @return the connection used to connect to the database */ public Connection getConnection() { return m_con; } // ----- CacheStore Interface -------------------------------------------- /** * 返回具有指定键,或者为null值,如果该键不底层存储中有一个相关的值。 * * @param oKey * key whose associated value is to be returned * * @return the value associated with the specified key, or <tt>null</tt> if * no value is available for that key */ public Object load(Object oKey) { Object oValue = null; Connection con = getConnection(); String sSQL = "SELECT id, value FROM " + getTableName() + " WHERE id = ?"; try { PreparedStatement stmt = con.prepareStatement(sSQL); stmt.setString(1, String.valueOf(oKey)); ResultSet rslt = stmt.executeQuery(); if (rslt.next()) { oValue = rslt.getString(2); if (rslt.next()) { throw new SQLException("Not a unique key: " + oKey); } } stmt.close(); } catch (SQLException e) { throw ensureRuntimeException(e, "Load failed: key=" + oKey); } return oValue; } /** * 根据具体的关键底层存储中存储指定的值。这种方法是为了支持一个特定的键的键/值创造与价值更新。 * * @param oKey * key to store the value under * @param oValue * value to be stored * * @throws UnsupportedOperationException * if this implementation or the underlying store is read-only */ public void store(Object oKey, Object oValue) { Connection con = getConnection(); String sTable = getTableName(); String sSQL; //下面的效率是非常低的,它是推荐使用的DB //特定的功能,取代MySQL或Oracle的合并 if (load(oKey) != null) { // key exists - update sSQL = "UPDATE " + sTable + " SET value = ? where id = ?"; } else { // new key - insert sSQL = "INSERT INTO " + sTable + " (value, id) VALUES (?,?)"; } try { PreparedStatement stmt = con.prepareStatement(sSQL); int i = 0; stmt.setString(++i, String.valueOf(oValue)); stmt.setString(++i, String.valueOf(oKey)); stmt.executeUpdate(); stmt.close(); } catch (SQLException e) { throw ensureRuntimeException(e, "Store failed: key=" + oKey); } } /** * 删除指定的键,如果存在从底层存储。. * * @param oKey * key whose mapping is to be removed from the map * * @throws UnsupportedOperationException * if this implementation or the underlying store is read-only */ public void erase(Object oKey) { Connection con = getConnection(); String sSQL = "DELETE FROM " + getTableName() + " WHERE id=?"; try { PreparedStatement stmt = con.prepareStatement(sSQL); stmt.setString(1, String.valueOf(oKey)); stmt.executeUpdate(); stmt.close(); } catch (SQLException e) { throw ensureRuntimeException(e, "Erase failed: key=" + oKey); } } /** * 删除指定的键从底层存储(如果存在). * * @param colKeys * keys whose mappings are being removed from the cache * * @throws UnsupportedOperationException * if this implementation or the underlying store is read-only */ public void eraseAll(Collection colKeys) { throw new UnsupportedOperationException(); } /** * 返回传递的集合每个指定的键关联的值。 * 如果一个关键底层存储中没有一个相关的值, * 然后返回地图不,关键有一个条目。 * * @param colKeys * a collection of keys to load * * @return a Map of keys to associated values for the specified keys */ public Map loadAll(Collection colKeys) { throw new UnsupportedOperationException(); } /** * 根据指定的键底层存储中存储指定的值。 * 这种方法的目的是支持指定键的键/值创造与价值更新。 * * @param mapEntries * a Map of any number of keys and values to store * * @throws UnsupportedOperationException * if this implementation or the underlying store is read-only */ public void storeAll(Map mapEntries) { throw new UnsupportedOperationException(); } /** * Iterate all keys in the underlying store. * * @return a read-only iterator of the keys in the underlying store */ public Iterator keys() { Connection con = getConnection(); String sSQL = "SELECT id FROM " + getTableName(); List list = new LinkedList(); try { PreparedStatement stmt = con.prepareStatement(sSQL); ResultSet rslt = stmt.executeQuery(); while (rslt.next()) { Object oKey = rslt.getString(1); list.add(oKey); } stmt.close(); } catch (SQLException e) { throw ensureRuntimeException(e, "Iterator failed"); } return list.iterator(); } // ----- data members --------------------------------------------------- /** * The connection. */ protected Connection m_con; /** * The db table name. */ protected String m_sTableName; /** * Driver class name. */ private static final String DB_DRIVER = "org.gjt.mm.mysql.Driver"; /** * Connection URL. */ private static final String DB_URL = "jdbc:mysql://localhost:3306/CacheStore"; /** * User name. */ private static final String DB_USERNAME = "root"; /** * Password. */ private static final String DB_PASSWORD = null; } |
14.6可控的CacheStore样例
本节说明了实施一个可控的缓存存储。在这种情况下,应用程序可以控制时,将更新后的值写入到数据存储。这种情况下最常见的使用情况是在启动时从数据存储在初始人口的高速缓存。在启动时,有没有要求写回的数据存储在缓存中的值。任何试图这样做将是一种资源浪费。
Main.java文件例14-4说明了一个可控的缓存存储交互的两种不同的方法:
使用一个可控的缓存(注意,它必须在不同的服务)来启用或禁用缓存存储。这示出由ControllableCacheStore1类别。
使用接口CacheStoreAware表示对象添加到缓存中,不需要需要存储。这说明由ControllableCacheStore2类。
双方ControllableCacheStore1 ControllableCacheStore2延长的com.tangosol.net.cache.AbstractCacheStore类。这个辅助类提供未优化的storeAll和eraseAll操作实现。
的CacheStoreAware.java文件是一个接口,它可以表明不应该被存储在数据库中的对象添加到缓存中。
请参阅“数据库”样本缓存配置缓存。
例14-4提供了一个上市的Main.java接口。
例14-4的Main.java - 一个可控的缓存存储交互
import java.io.Serializable; import java.util.Date; public class Main extends Base { /** * A cache controlled CacheStore implementation */ public static class ControllableCacheStore1 extends AbstractCacheStore { public static final String CONTROL_CACHE = "cachestorecontrol"; String m_sName; public static void enable(String sName) { CacheFactory.getCache(CONTROL_CACHE).put(sName, Boolean.TRUE); } public static void disable(String sName) { CacheFactory.getCache(CONTROL_CACHE).put(sName, Boolean.FALSE); } public void store(Object oKey, Object oValue) { Boolean isEnabled = (Boolean) CacheFactory.getCache(CONTROL_CACHE).get(m_sName); if (isEnabled != null && isEnabled.booleanValue()) { log("controllablecachestore1: enabled " + oKey + " = " + oValue); } else { log("controllablecachestore1: disabled " + oKey + " = " + oValue); } } public Object load(Object oKey) { log("controllablecachestore1: load:" + oKey); return new MyValue1(oKey); } public ControllableCacheStore1(String sName) { m_sName = sName; } } /** * a valued controlled CacheStore implementation that * implements the CacheStoreAware interface */ public static class ControllableCacheStore2 extends AbstractCacheStore { public void store(Object oKey, Object oValue) { boolean isEnabled = oValue instanceof CacheStoreAware ? !((CacheStoreAware) oValue).isSkipStore() : true; if (isEnabled) { log("controllablecachestore2: enabled " + oKey + " = " + oValue); } else { log("controllablecachestore2: disabled " + oKey + " = " + oValue); } } public Object load(Object oKey) { log("controllablecachestore2: load:" + oKey); return new MyValue2(oKey); } } public static class MyValue1 implements Serializable { String m_sValue; public String getValue() { return m_sValue; } public String toString() { return "MyValue1[" + getValue() + "]"; } public MyValue1(Object obj) { m_sValue = "value:" + obj; } } public static class MyValue2 extends MyValue1 implements CacheStoreAware { boolean m_isSkipStore = false; public boolean isSkipStore() { return m_isSkipStore; } public void skipStore() { m_isSkipStore = true; } public String toString() { return "MyValue2[" + getValue() + "]"; } public MyValue2(Object obj) { super(obj); } } public static void main(String[] args) { try { // example 1 NamedCache cache1 = CacheFactory.getCache("cache1"); // disable cachestore ControllableCacheStore1.disable("cache1"); for(int i = 0; i < 5; i++) { cache1.put(new Integer(i), new MyValue1(new Date())); } // enable cachestore ControllableCacheStore1.enable("cache1"); for(int i = 0; i < 5; i++) { cache1.put(new Integer(i), new MyValue1(new Date())); } // example 2 NamedCache cache2 = CacheFactory.getCache("cache2"); // add some values with cachestore disabled for(int i = 0; i < 5; i++) { MyValue2 value = new MyValue2(new Date()); value.skipStore(); cache2.put(new Integer(i), value); } // add some values with cachestore enabled for(int i = 0; i < 5; i++) { cache2.put(new Integer(i), new MyValue2(new Date())); } } catch(Throwable oops) { err(oops); } finally { CacheFactory.shutdown(); } } } |
例14-5提供一上市的CacheStoreAware.java接口。
例14-5 CacheStoreAware.java接口
public interface CacheStoreAware { public boolean isSkipStore(); } |
14.7实现意事项
当实现一个缓存存储区,请记住以下几点。
14.7.1 Idempotency幂等
所有的缓存存储操作的设计应是等幂的(即,可重复的,没有不必要的副作用) 。写和后写高速缓存,这使得提供低成本的容错部分更新重新尝试数据库故障转移处理过程中的缓存更新部分相干。后写缓存,幂等也允许连贯性,结合成一个单一的缓存存储调用多个缓存更新,而不会影响数据的完整性。
应用后写式高速缓存,但有一个要求,必须避免写结合(例如,对于审计的原因) ,应该创建一个“版本”缓存键(例如,通过结合自然主键与序列ID ) 。
14.7.2 Write-Through 局限性
Coherence不支持两阶段的缓存存储业务,跨多个缓存存储实例。换句话说,如果两个高速缓存条目的更新,触发呼叫坐在单独的缓存服务器的缓存存储模块,它是一个数据库更新可能成功,为其他失败。在这种情况下,它可能是优选使用的高速缓存预留与应用服务器的事务管理器的体系结构(作为两个单独的组件的一个单独的事务更新的高速缓存和数据库) 。在许多情况下,它有可能来设计数据库模式,以防止逻辑提交失败(但显然不是服务器故障) 。后写缓存避免了这个问题, “把”不影响数据库的行为(作为底层的问题已得到解决在设计过程中更早) 。
14.7.3缓存查询
缓存查询存储在缓存中的数据,并不会触发缓存存储区加载任何数据丢失(或潜在的缺失) 。因此,应用程序,查询的缓存存储备份缓存应确保所有必要的查询所需的数据已经预先加载。为了提高效率,大多数批量装载操作应在应用程序启动时流的数据直接从数据库到缓存中(配料到缓存中的数据块通过使用NamedCache.putAll ( ) 。装载机过程必须使用一个“可控的缓存存储“模式禁用循环更新回数据库的缓存存储区,可控制使用的调用服务(发送代理在整个集群中每个JVM中修改一个本地标志)或通过设置中的值复制缓存(不同的缓存服务)和读取的每个缓存存储方法调用(最小的开销相比,典型的数据库操作) ,也可用于自定义MBean ,一个简单的任务连贯性的集群JMX设施。
14.7.4 Re-entrant Calls
缓存存储的实现不能再打到托管缓存服务。这包括ORM解决方案,可能在内部引用相干缓存服务。请注意,调用到另一个缓存服务实例是允许的,但应小心避免深层嵌套调用(每次调用“消耗”一个缓存服务线程,并可能导致死锁,如果缓存服务线程池耗尽)
14.7.5 Cache Server Classpath
缓存项的类(也被称为值对象,数据传输对象,依此类推)必须在缓存服务器类路径中(作为缓存服务器必须序列化反序列化缓存条目与缓存存储模块。
14.7.6缓存存储区收集操作
CacheStore.storeAll方法是,如果缓存配置写背后, <write-batch-factor>的配置,最有可能被使用。的CacheLoader.loadAll方法也可用于的Coherence。出于类似的原因,第一次使用可能需要刷新提前被激活。
14.7.7连接池
从容器连接池(或第三方连接池) ,或通过使用线程局部延迟初始化模式,应该把数据库连接检索。由于没有管理集装箱专用缓存服务器通常部署,后者可能是最有吸引力的选项(虽然缓存服务线程池大小应限制同时避免过多的数据库连接) 。