Coherence Step by Step 第三篇 缓存(三)实现Storage和Backing Maps(翻译)
本章使用backing maps提供信息存储。
- Client View 这个客户端视图表示了一个虚拟的层,提供对底层分布式数据的访问。这一侧可以使用NamedCache接口来访问。在这个层,你能创建合成的数据结构,如NearCahce或者 ContinuousQueryCache。
- Storage Manager storage manager是服务端的层,用来处理从客户端层发来的和缓存相关的请求。它管理持有真是缓存数据(primary和backup副本)的锁信息、事件监听器、映射触发器等的数据结构。
- backing map backing map是服务端数据结构,持有真实数据。
- Safe HashMap: 这个默认的无损的实现。无损的实现是一个类似java的Hashtable类的,没有大小限制和自动过期。换句话说,它不会从自身中剔除任何缓存条目。这个特殊的HashMap的实现对非常高的线程级别的并发是最佳的。对于默认的实现,用com.tangosol.until.SafehaspMap类;当一个实现需要提供缓存事件时,用com.tangosol.util.ObservalbleHashMap。这些实现是线程安全的。
- Local Cache: 这是默认的size-limiting和atuo-expiring的实现。local cache下面会详细说,但是重点要记住的是他能够限制缓存的大小,它能够实现在一个确定周期后自动过期。对于默认的实现,使用com.tangosol.net.cache.LocalCache;这个实现是线程安全的,支持缓存事件,con.tangosol.net.CacheLoader,CacheStore和可配置的/插入式的逐出规则。
- 读/写 Backing map: 这是默认的backing map的实现,当一个缓存没有找到,从数据库中加载。他能配置成read-only cache(消费者模型)或者是一个write-through 或者是write-behind cache(消费者/生产者模型).write-through和write-behind模型专为distributed cache服务所设计。如果使用near cache,near cache必须和distributed cache保持同步,可能结合使用支持Seppuku-based near cache的backing map。对于默认的实现,使用com.tangosol.net.cache.ReadWriteBackingMap类。
- Binary Map(Java NIO):这个backing map的实现,能够在内存中存储它的信息,而不是在java heap之外或甚至在memory-mapped文件,这意味着它不会影响java 堆的大小,和相关jcm垃圾回收的性能,能够对应用的暂停负责。这个实现也对distributed cache 备份有效,对于read-mostly 和read-only的换取特别有用,尤其要求高可用性的备份,因为这意味着backup不影响java堆的大小,二爷在失效备援时候能够立即可用。
- Serialization Map:这个backing map的实现转换它的数据位一种能够存储在硬盘上的形式,引用一种序列化的形式。这要求一个独立的的com.tangosol.io.BinaryStore对象,以序列化形式的数据存储;通常,这是内建的LH磁盘的存储实现,但是serialization map支持任何自定义的BinaryStore实现。对于默认的serialization map,shiyongcom.tangosol.net.cache.SerializationMap.
- Serialization Cache: 这个是SerializationMap的扩展,支持LRU啊逐出规则,使用com.tangosol.net.cache.SerializationCache。
- Overflow Map:overflow map没有正式的提供存储,但是在这里应该提到,因为它能够结合两种local storage 实现,当第一个填满时候,它填充第二个。对于overflowmap的默认实现,使用com.tangosl.net.cache.OverflowMap。
- 自然访问和更新操作由应用的使用所引起。例如,NamedCache.get()的调用自然的导致了Map.get()的调用,以回应backing map;NamedCache.invoke()调用可能引起一系列的Map.put()和Map.get()操作;NamedCache.keySet(filter)调用可能引起Map.entrySet().iterator循环等等。
- 移除操作由基于时间的过期或者基于大小的逐出所引起。例如,从客户端层调用NamedCache.get()或者NamedCache.size()可能引起Map.remove()调用,缘由于条目的过期时间到;或者NamedCache.put()调用导致一个Map.Remove()调用,缘由于在backing map中总共的数量达到了water-mark的配置值。
- 插入操作可能导致一个CacheStore.load()操作。(对于配置了read-through或者read-ahead特性的backing map)
- 人工访问或者更新是由于partition distribution引起的(这反过来可能是cluster节点失效或者回滚造成的)。这个例子中,没有应用层的调用,有些条目可能从backing map中被插入或者删除。
- on-heap memory
- off-heap memory
- disk(memory-mapped files 或者 in process DB)
- solid state device(日志文件)
- 以上任意的结合
<backing-map-scheme> <local-scheme/> </backing-map-scheme>
上面的backing map是一个com.tangosol.net.cache.LocalCache的实例,没有任何预定的大小限制,且必须显式的控制。如果不这么做,可能导致JVM内存不足。
<backing-map-scheme> <local-scheme> <eviction-policy>LRU</eviction-policy> <high-units>100m</high-units> <unit-calculator>BINARY</unit-calculator> </local-scheme> </backing-map-scheme>
上面的backing map也是com.tangosol.net.cache.LocalCache,有一个100MB的容量限制。如果backing map所持有的数据超出了这个watermark,一些条目将会从backing map被移除,使得容量降低到低的watermark值(<low-units>配置元素,默认是<high-units>的75%)。移除条目的选择是基于LRU逐出规则。其它的参数有LFU和Hybird。<high-units>限制是2GB。要超过这个限制,使用<unit-factor>元素。例如,用<unit-factor>为1048576和<high-units>值为8192,结果是一个high watermark的值为8GB(8192*1048576)。
<backing-map-scheme> <local-scheme> <expiry-delay>1h</expiry-delay> </local-scheme> </backing-map-scheme>
上面的bakcing map自动逐出任何一条木,只要它有超过1小时没有更新。注意,这种逐出是一种"lazy"方式,一谈有更新发生,会在任何时间触发。唯一保证Coherence提供了超过1小时的条目不会回调。
<backing-map-scheme> <external-scheme> <nio-memory-manager> <initial-size>1MB</initial-size> <maximum-size>100MB</maximum-size> </nio-memory-manager> <high-units>100</high-units> <unit-calculator>BINARY</unit-calculator> <unit-factor>1048576</unit-factor> </external-scheme> </backing-map-scheme>
配置这个缓存的backup storage为off-heap(or file-mapped):
<backup-storage> <type>off-heap</type> <initial-size>1MB</initial-size> <maximum-size>100MB</maximum-size> </backup-storage>
5.使用Partitioned Backing Maps
传统的backing map实现为所有相应节点所拥有的partitions包含了条目。(在partition传输过重中,它也能持有"in flight"条目,从客户端的角度来说是暂时的不属于任何人)。
下图展示了一个传统backing map实现的概念视图。
partitioned的backing map是一个基本的多个真是映射的实现,每个都只包含了属于同一个partition的条目。
下图是partitioned backing map实现的概念视图
配置partitioned backing map,增加一个<partitioned>元素,值为true,例如:
<backing-map-scheme> <partitioned>true</partitioned> <external-scheme> <nio-memory-manager> <initial-size>1MB</initial-size> <maximum-size>50MB</maximum-size> </nio-memory-manager> <high-units>8192</high-units> <unit-calculator>BINARY</unit-calculator> <unit-factor>1048576</unit-factor> </external-scheme> </backing-map-scheme>
JournalBinaryStore
类的实现。<distributed-scheme> <scheme-name>distributed-journal</scheme-name> <service-name>DistributedCacheRAMJournal</service-name> <backing-map-scheme> <ramjournal-scheme/> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme>
6.2.2 配置Flash Journal Backing Map
配置flash journalbacking map,在缓存定义中的<backing-map-scheme>元素中增加一个<flashjournal-scheme>的元素。下面的例子创建了一个distributed scheme,使用了flash journal 作为bakcing map。
<caching-schemes> <distributed-scheme> <scheme-name>distributed-journal</scheme-name> <service-name>DistributedCacheJournal</service-name> <backing-map-scheme> <ramjournal-scheme> <scheme-ref>default-ram</scheme-ref> </ramjournal-scheme> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme> <ramjournal-scheme> <scheme-name>default-ram</scheme-name> </ramjournal-scheme> </caching-schemes>
6.2.4 使用Journal Scheme作为Backup Storage
Journal scheme也可以像backing map一样用来作为backup storage。默认的,distributed scheme配置类使用RAM journal作为backing map,也为backup storage使用RAM journal。同样的,使用flash journal作为backing map的distributed scheme也使用flash journal作为backup storage。这个默认的行为能够通过使用<backup-storage>明确指定存储类型来修改。下面的配置使用一个RAM journal 作为backing map和志明配置一个flash journal作为backup storage:
<caching-schemes> <distributed-scheme> <scheme-name>default-distributed-journal</scheme-name> <service-name>DistributedCacheJournal</service-name> <backup-storage> <type>scheme</type> <scheme-name>example-flash</scheme-name> </backup-storage> <backing-map-scheme> <ramjournal-scheme/> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme> <flashjournal-scheme> <scheme-name>example-flash</scheme-name> </flashjournal-scheme> </caching-schemes>
- 二进制值默认限制为64KB(最大4MB)
- 一个单独的缓冲区(一个journal file)限制在2MB(最大2GB)
- journal 由最多512个文件组成
- journal总共能用的内存是默认为1GB(最大64GB)
<?xml version='1.0'?> <coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/ coherence-operational-config coherence-operational-config.xsd"> <cluster-config> <journaling-config> <ramjournal-manager> <maximum-value-size>64K</maximum-value-size> <maximum-size>2G</maximum-size> </ramjournal-manager> </journaling-config> </cluster-config> </coherence>
6.3.2 配置Flash Journal Resource Manager
<flashjournal-manager>元素用来配置flash journal behavior。下面的列表提供了一个由resouce manager设置的默认值的详细汇总。
- 二进制值默认限制为64MB
- 一个单独的缓冲区(一个journal file)限制在2GB(最大4GB)
- journal 由最多512个文件组成
- journal 默认限制在1TB,理论上是2TB
<?xml version='1.0'?> <coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/ coherence-operational-config coherence-operational-config.xsd"> <cluster-config> <journaling-config> <flashjournal-manager> <maximum-value-size>64K</maximum-value-size> <maximum-file-size>8M</maximum-file-size> <block-size>512K</block-size> <maximum-pool-size>32M</maximum-pool-size> <directory>/coherence_storage</directory> <async-limit>32M</async-limit> </flashjournal-manager> </journaling-config> </cluster-config> </coherence>
NOTE:用来存储journal文件的目录必须存在。如果不存在,会有警告信息,并且使用JVM指定的默认的临时文件目录。
7.使用差异备份
差异备份是一种当主要的entry发生改变时,用来将变化的部分保存进backup binary entry 而不是将整个entry给替换。差异备份对于需要更新的东西非常庞大,但是只有很小的一部分发生变化的场景是非常理想的。在这种情况下,改变的代价只是enrry的一小部分,小于重写整个entry的代价,结果是性能更好。如果改变超过50% ,差异备份通常可以证明几乎没有什么性能上的增加。
<distributed-scheme> ... <compressor>standard</compressor> ... </distributed-scheme>
为所有的distributed cache 服务类型的实例开启差异备份。在operational override file中覆盖部分缓存服务的compressor 初始参数。例如:
<?xml version='1.0'?> <coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/ coherence-operational-config coherence-operational-config.xsd"> <cluster-config> <services> <service id="3"> <init-params> <init-param id="22"> <param-name>compressor</param-name> <param-value system-property="tangosol.coherence.distributed.compressor"> standard</param-value> </init-param> </init-params> </service> </services> </cluster-config> </coherence>
<distributed-scheme> ... <compressor> <instance> <class-name>package.MyDeltaCompressor</class-name> </instance> </compressor> ... </distributed-scheme>
作为可选的方案,<instance>元素支持使用<class-factory-name>元素来使用工厂类来负责创建DeltaCompressor实例,<method-name>元素在工厂类上指定了静态工厂方法来执行对象实例化。下面的例子通过使用MyCompressorFactory类中的getCompressor方法来获取一个自定义的compressor实例。
<distributed-scheme> ... <compressor> <instance> <class-factory-name>package.MyCompressorFactory</class-factory-name> <method-name>getCompressor</method-name> </instance> </compressor> ... </distributed-scheme>
需要任何的初始参数可以通过<init-params>元素类指定。下面的例子设置iMaxTime参数为2000。
<distributed-scheme> ... <compressor> <instance> <class-name>package.MyDeltaCompressor</class-name> <init-params> <init-param> <param-name>iMaxTime</param-name> <param-value>2000</param-value> </init-param> </init-params> </instance> </compressor> ... </distributed-scheme>