zoukankan      html  css  js  c++  java
  • Coherence Step by Step 第三篇 缓存(四) 缓存数据源(翻译)

    本章介绍了用Coherence作为临时的system-of-record来缓存数据源。本篇包含了例子和实现的注意事项。

    1 缓存数据源概述

    Coherence 支持透明的读/写任何数据源的缓存,包含数据库,web服务,套装软件和文件系统;然而,数据库是最常用的用例。简要的说,数据库是用来描述任何back-end数据源。有效果的缓存必须都支持密集的只读和读写操作,并且对于读写操作,缓存和数据库必须保持完全同步。为了完成数据源的缓存,Coherence支持 Read-Through, Write-Through, Refresh-Ahead and Write-Behind 缓存。

    NOTE:Read-through/write-through缓存(和变体) 是只用在Partitioned(Distributed) cache拓扑(和货站的,Near Cache),Local caches支持这功能的自己。Replicated和Optimistic 缓存不能被使用。

    1.1 Pluggable Cache Store

    CacheStore 是一个指定应用的适配器,用来连接一个缓存到基本数据源。CacheStore的实现通过使用一个数据访问机制来获取数据源(例如,Hibernate,Toplink Essentials, JPA, application-specific JDBC calls, another application, mainframe, another cache, 等)。CacheStore知道如何创建一个java对象来实现从数据源检索数据,映射和写一个对象到数据源,从数据源擦出一个对象。

    数据源连接策略和数据源-应用-对象的映射信息是针对数据源架构,应用程序类布局,和操作环境。因此,映射信息必须由应用开发者来提供,以CacheStore实现的形式。

    1.2 Read-Through Caching

    当应用程序想缓存请求一条entry,例如key x,并且x不在缓存中,Coherence 会自动委托给CacheStore,请求它来从基础数据源中加载x。如果x在数据源中存在,CacheStore加载它,返回给Coherence,然后Coherence将它放在缓存中为将来使用,最后返回x给请求它的应用程序的代码。这个叫做Read-Through缓存。Refresh-Ahead Cache功能可以更加提升读取性能(通过减少感知延迟)。

    Image

    1.3 Write-Through Caching

    Coherence用两种不同的方式来处理更新到数据源,第一个是Write-Through。这个例子,当应用程序更新一条在缓存中的数据(是调用put(...)来改变缓存entry),直到Coherence运行了CacheStore并成功存储数据到基础数据源才算操作完成。这个完全不会提升写的性能,因为你仍然使用有延时的方式写数据源。提升写的性能建议使用Write-Behind Cache 功能。

    Image(1)

    1.4 Write-Behind Caching

    在Write-Behind的场景中,修改缓存条目是在配置的延迟值后异步的写入数据源,可能是10秒,20分钟,一天,一个星期或者是更久。注意,这个只用于对缓存的插入和更新。缓存条目从数据源移除是同步的。Write-Behind缓存,Coherence维护者一个需要在数据源执行更新的write-behind数据队列。当应用程序更新缓存中的X,X被添加在write-behind队列(如果不存在;否则就替换它),然后在指定的write-behind延迟以后,COherence调用了CacheStore,用最新的X的状态来更新基础数据源,注意write-behind延迟是相对于第一个一系列的修改--换句话说,在数据源的数据从未落后于缓存超过wirte-behind延迟的这个时间。

    结果是"一次读取和在配置好的时间间隔写"的场景,有四个主要的益处,对于这个架构体系的类型:

    • 应用程序提升了性能,因为用户不用等待数据写入基础数据源。(数据延迟写入,并且通过其他线程)
    • 应用程序经历了彻底降低数据库的负载:由于减少了大量的读和写操作,数据库的负载也是同样。和其他缓存方法一样,通过缓存,读取变少了。写,这个通常是最昂贵的操作,也减少了,因为针对同一个对象的多个变化使用write-behind是合并并且只写一次到数据源  ("write-coalescing")。此外,对多个缓存条目的写可能被合并成一个数据库事务 ("write-combining")。如果用CacheStore.storeAll()方法。
    • 应用程序多少有点隔绝数据库失效的情况:Write-Behind特性能够被这样配置,失败的写入导致对象重新被请求写。如果应用程序正在使用的数据时在Coherence的缓存里,那么应用程序能够继续操作,而不用数据库是up状态的。使用Coherence Partitioned Cache,这很容易做到,只要将所有的缓存分区到所有的participating cluster 节点(开启了local-storage),允许庞大的缓存。
    • 线性可扩展:要应用程序处理更多的并发用户,你只需要增加cluster的节点即可;影响数据库的负载可以用增加write-behind间隔来调节。

    Image(2)

    1.4.1 Write-Behind Requirements

    要启用write-behind缓存只是一个简单的设置配置的调整,确保write-behind如期望的那样工作要根复杂。特别的,应用程序的设计必须解决几个预先设计问题。

    Write-Behind缓存的最直接的含义是发生在缓存事务之外的数据库更新;就是缓存的事务通常在数据库事务开始之前完成。这意味着数据库事务永远不是失败;如果不能保存,那么必须保证可以回滚。

    write-behind可能重新请求数据库更新,参照完整性约束必须允许无序的更新。概念上的,这类似于使用ISAM-style storage的数据库(基于主键的访问,保存没有更新冲突)。如果其他的应用程序共享数据库,这将引入新的挑战--没有其他方式保证write-behind事务和外部的更新不发生冲突。这意味着write-behind冲突必须通过人家的操作来启发式的处理或者手动调整来增强。

    根据鹰眼,映射每个缓存条目更新到逻辑数据库的事务的比较理想的,这能保证简单的数据库事务。

    因为write-behind 有效的使得缓存system-of-record(直到write-behind 队列被写进磁盘),业务规则必须允许cluster-durable(而不是disk-durable),数据存储和事务。

    1.5 Refresh-Ahead Caching

    在Refresh-Ahead 的场景,Coherence允许开发者配置缓存,能够在它失效之前自动和异步的从cache loader重载最近访问过的缓存条目。结果是一个平凡被访问的条目进入缓存后,应用程序没有感觉到对于一个千载的减慢cache store的读取的影响,当这个条目由于过期而重新加载时。异步的属性只会在足够接近它的国企时间时候被访问时触发--如果对象在它的过期时间之后,Coherence会从cache store执行同步的读取来刷新他的值。

    refresh-ahead 时间表示的是条目的过期时间的百分比例如,假设cache中设置的条目的过期时间是60秒,refresh-ahead因素设置为0.5.如果cache对象在60秒后被访问,Coherence从cache store执行同步读取来刷新值。然后,如果对于一个条目的请求执行是超过30小于60秒,cache中现在的值会被返回,并且coherence 调度一个异步的从cache store中重新加载。

    Refresh-ahead是特别有用的,如果对象被大量的用户访问。缓存中的值始终保持最新,避免了由于从cache store过度重新加载导致的延迟。

    refresh-ahead 因素的值通过在coherence-cache-config.xml文件的<read-write-backing-map-scheme>元素的子元素<refresh-ahead-factor>指定。Refresh-ahead假定你也在缓存中为条目设置过期时间。

    下面的例子对local cache的条目配置了一个refresh-ahead因素为0.5,过期时间是20秒,如果条目的访问在它过期时间的10秒内,那么会异步的从cache store重新加载。

    <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> 

    2.选择一个缓存策略

    这一节介对照和比较了几个缓存策略的益处。

    2.1 Read-Through/Write-Through 对 Cache-Aside

    在cluster环境中,对于cache-aside模式有两个常用的方法。一个包含检查缓存溢漏,然后查询数据库,填充缓存,然后程序继续处理。这个可能导致多次的数据库访问,如果此时有不同的应用现成执行处理。二选一,程序也可以执行双重检查锁(这个操作是对缓存条目原子的检查)。然而,结果是大量的开销花费在缓存溢漏和数据更新上(一个cluster锁,额外的读,cluster的解锁,知道10个额外的网络条数,或者6~8毫秒在通常的gigabit以太网络链接,加上额外的处理花费和一条缓存条目锁定时间的增减)。

    使用inline 缓存,entry只被锁定2个网络跳数的时间(数据时被复制到为容错的备份服务器)。另外,锁被分区拥有者所维护。此外,应用程序代码完全万里cache server,意味着只是一个被控制的节点子集直接访问数据库(导致更多的可预测的加载和安全性)。此外,这将缓存客户端从数据库逻辑中分离。

    2.2 Refresh-Ahead 对Read-Through

    Refresh-Ahead 和read-Through相比较减少了延迟,但是只有当缓存能够准确的预测到哪个缓存想可能在将来会被用到。如果预测完全的正确,refresh-ahead提供了减少延迟和没有更多的负载。高错误率的预测,提升了吞吐率的影响(如更多的不希望的请求被发送给数据库)-可能甚至在延迟上有消极的影响,数据库开始拖累请求处理。

    2.3 Write-behind 对Write-Through


    如果write-behind缓存的需求能够被满足,和write-through相比,write-behind 缓存可以实现相当高的吞吐率和减少延迟。另外write-behind缓存降低了数据库的负载(更少的写),和在缓存服务器上的(减少了cache值的反序列化)。

    3.创建CacheStore实现

    为了插入一个CacheStore 模块,指定CacheStore实现类的名字,用distributed-scheme,backing-map-scheme,cachestore-shceme,或者read-write-backing-map-scheme,缓存配置元素。

    read-write-backing-map-scheme配置了一个com.tangosol.net.cache.ReadWriteBackingMap。这个backing map由两个关键的元素组成:一个内部的映射,它正是的缓存数据,和一个CacheStore模块,它和数据库互动。

    下面的例子说明了一个缓存配置,指定了CacheStore模块。<init-params>元素报了一个请求列表的参数,传递给CacheStore构造器。 {cache-name} 配置宏命令是用来传递缓存名给CacheStore的实现,允许它映射到数据库的表。完整可用的宏命令的表,请看"Using Parameter Macros".

    关于配置write-behind和refresh0ahead的更多详细信息,看read-write-backing-map-scheme,注意 write-batch-factor, refresh-ahead-factor, write-requeue-threshold, and rollback-cachestore-failures 元素。

    <?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>

    NOTE:

    线程数量:使用CacheStore模块实质上增加了cache service 线程的消耗(即使最快的数据库选择也是比在in-memory结构中的更新要慢很多歌数量级)。因此,缓存服务的线程数量必须增加(通常是10-100的范围)。值得注意的是线程池不足的最明显的征兆就是缓存请求的延迟增加(没有响应backing database的行为)。

    5.CacheStore 例子

    这个章节提供了con.tangosol.net.cache.CacheStore接口的基本实现。例子中的实现使用了jdbc作为数据库的链接,没有使用批量操作。完整的实现将使用connection pool,并且,如果使用write-behind,要为bulk JDBC 插入和更新实现CacheStore.storeAll()。

    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;
    
    
    /**
    * An example implementation of CacheStore
    * interface.
    *
    * @author erm 2003.05.01
    */
    public class DBCacheStore
            extends Base
            implements CacheStore
        {
        // ----- constructors ---------------------------------------------------
        /**
        * Constructs DBCacheStore for a given database table.
        *
        * @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(&quot;org.gjt.mm.mysql.Driver&quot;);
                            m_con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
                            m_con.setAutoCommit(true);
                            }
                    catch (Exception e)
                            {
                            throw ensureRuntimeException(e, &quot;Connection failed&quot;);
                            }
                    }
    
    
            // ---- accessors -------------------------------------------------------
    
        /** 
        * Obtain the name of the table this CacheStore is persisting to.
        * 
        * @return the name of the table this CacheStore is persisting to
        */
        public String getTableName()
            {
            return m_sTableName;
            }
    
        /** 
        * Obtain the connection being used to connect to the database.
        * 
        * @return the connection used to connect to the database
        */
        public Connection getConnection()
            {
            return m_con;
            }
    
    
        // ----- CacheStore Interface --------------------------------------------
    
        /**
        * Return the value associated with the specified key, or null if the
        * key does not have an associated value in the underlying store.
        *
        * @param oKey  key whose associated value is to be returned
        *
        * @return the value associated with the specified key, or
        *         &lt;tt&gt;null&lt;/tt&gt; if no value is available for that key
        */
        public Object load(Object oKey)
            {
            Object     oValue = null;
            Connection con    = getConnection();
            String     sSQL   = &quot;SELECT id, value FROM &quot; + getTableName()
                              + &quot; WHERE id = ?&quot;;
            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(&quot;Not a unique key: &quot; + oKey);
                        }
                    }
                stmt.close();
                }
            catch (SQLException e)
                {
                throw ensureRuntimeException(e, &quot;Load failed: key=&quot; + oKey);
                }
            return oValue;
            }
    
        /**
        * Store the specified value under the specific key in the underlying
        * store. This method is intended to support both key/value creation
        * and value update for a specific key.
        *
        * @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;
            
            // the following is very inefficient; it is recommended to use DB
            // specific functionality that is, REPLACE for MySQL or MERGE for Oracle
     if (load(oKey) != null)
                    {
                    // key exists - update
             sSQL = &quot;UPDATE &quot; + sTable + &quot; SET value = ? where id = ?&quot;;
                    }
            else
                    {
                    // new key - insert
             sSQL = &quot;INSERT INTO &quot; + sTable + &quot; (value, id) VALUES (?,?)&quot;;
                    }
            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, &quot;Store failed: key=&quot; + oKey);
                    }
            }
    
        /**
        * Remove the specified key from the underlying store if present.
        *
        * @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 = &quot;DELETE FROM &quot; + getTableName() + &quot; WHERE id=?&quot;;
            try
                {
                PreparedStatement stmt = con.prepareStatement(sSQL);
    
                stmt.setString(1, String.valueOf(oKey));
                stmt.executeUpdate();
                stmt.close();
                }
            catch (SQLException e)
                {
                throw ensureRuntimeException(e, &quot;Erase failed: key=&quot; + oKey);
                }
            }
    
            /**
            * Remove the specified keys from the underlying store if present.
            *
            * @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();
                    }
    
            /**
            * Return the values associated with each the specified keys in the
            * passed collection. If a key does not have an associated value in
            * the underlying store, then the return map does not have an entry
            * for that key.
            *
            * @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();
                    }
    
            /**
            * Store the specified values under the specified keys in the underlying
            * store. This method is intended to support both key/value creation
            * and value update for the specified keys.
            *
            * @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 = &quot;SELECT id FROM &quot; + 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, &quot;Iterator failed&quot;);
                }
    
            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   = &quot;org.gjt.mm.mysql.Driver&quot;;
    
            /**
            * Connection URL.
            */
            private static final String DB_URL      = &quot;jdbc:mysql://localhost:3306/CacheStore&quot;;
    
            /**
            * User name.
            */
            private static final String DB_USERNAME = &quot;root&quot;;
    
            /**
            * Password.
            */
            private static final String DB_PASSWORD = null;
        }

    6. Controllable CacheStore 例子

    这个章节要说明的是controllable cache store的实现。这个场景下,应用程序能够控制合适写更新到cache store。这个场景的最通常的用例就是在启动的时候,从data store填充数据到缓存。在启动时,没有需求将缓存中的值写入data store。任何的尝试都是自愿的浪费。

    下面例子中的Main.java 文件,用两种不同的方法来和controllable cache store交互。

    • 使用controllable cache启用和关闭cache store。这个通过ControllableCacheStore1类来说明。
    • 使用CacheStoreAware接口来表示将对象添加进缓存而不需要存储。通过ControllableCacheStore2类来展示。

    ControllableCacheStore1和ControllableCacheStore1都继承了com.tangosol.net.cache.AbstractCacheStore类。这个帮助类提供了对storeAll和eraseAll操作的没有优化的实现。

    CacheStoreAware.java文件是一个借口,它能够展示一个对象被添加进缓存而不存到数据库。

    下面的例子提供了Main.java接口的列表。

    import com.tangosol.net.CacheFactory;
    import com.tangosol.net.NamedCache;
    import com.tangosol.net.cache.AbstractCacheStore;
    import com.tangosol.util.Base;
    
    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 = &quot;cachestorecontrol&quot;;
    
            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 &amp;&amp; isEnabled.booleanValue())
                    {
                    log(&quot;controllablecachestore1: enabled &quot; + oKey + &quot; = &quot; + oValue);
                    }
                else
                    {
                    log(&quot;controllablecachestore1: disabled &quot; + oKey + &quot; = &quot; + oValue);
                    }
                }
    
            public Object load(Object oKey)
                {
                log(&quot;controllablecachestore1: load:&quot; + 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(&quot;controllablecachestore2: enabled &quot; + oKey + &quot; = &quot; + oValue);
                    }
                else
                    {
                    log(&quot;controllablecachestore2: disabled &quot; + oKey + &quot; = &quot; + oValue);
                    }
                }
    
            public Object load(Object oKey)
                {
                log(&quot;controllablecachestore2: load:&quot; + oKey);
                return new MyValue2(oKey);
                }
    
            }
    
        public static class MyValue1 implements Serializable
            {
            String m_sValue;
    
            public String getValue()
                {
                return m_sValue;
                }
    
            public String toString()
                {
                return &quot;MyValue1[&quot; + getValue() + &quot;]&quot;;
                }
    
            public MyValue1(Object obj)
                {
                m_sValue = &quot;value:&quot; + 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 &quot;MyValue2[&quot; + getValue() + &quot;]&quot;;
                }
    
            public MyValue2(Object obj)
                {
                super(obj);
                }
    
            }
    
        public static void main(String[] args)
            {
            try
                {
    
                // example 1
    
                NamedCache cache1 = CacheFactory.getCache(&quot;cache1&quot;);
    
                // disable cachestore
                ControllableCacheStore1.disable(&quot;cache1&quot;);
                for(int i = 0; i &lt; 5; i++)
                    {
                    cache1.put(new Integer(i), new MyValue1(new Date()));
                    }
    
                // enable cachestore
                ControllableCacheStore1.enable(&quot;cache1&quot;);
                for(int i = 0; i &lt; 5; i++)
                    {
                    cache1.put(new Integer(i), new MyValue1(new Date()));
                    }
    
                // example 2
    
                NamedCache cache2 = CacheFactory.getCache(&quot;cache2&quot;);
    
                // add some values with cachestore disabled
                for(int i = 0; i &lt; 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 &lt; 5; i++)
                    {
                    cache2.put(new Integer(i), new MyValue2(new Date()));
                    }
    
    
                }
            catch(Throwable oops)
                {
                err(oops);
                }
            finally
                {
                CacheFactory.shutdown();
                }
            }
    
        }

    下面的例子提供了CacheStoreAware.java接口的列表

    public interface CacheStoreAware
    {
        public boolean isSkipStore();
    }

    7.实现的注意事项

    当实现CacheStore时,请始终记住以下几点。

    7.1 Idempotency

    所有的CacheStore操作应该被设计成idempotent(就是,重复,但是没有副作用)。如Write-through和write-behind缓存,允许Coherence提供一个低消费的,容错的部分更新,通过重新尝试更新缓存的数据库部分,当故障转移时处理。如Write-behind缓存,idemprtency 也允许Coherence 结合多个缓存更新到一个单独的CacheStore请求,不影响数据完整性。

    应用程序有一个需求是write-behind缓存但是要避免write-combining(例如,审计的原因),应该创建一个版本化的缓存键(例如,通过用一个序列化id合并自然主键)。

    7.2 Write-Through 限制

    Coherence 不支持两段式的横穿多个CacheStore实例的CacheStore操作。换句话说,如果两个缓存条目更新,触发调用在分开的缓存服务器上的CacheStore,这个可能会导致一个数据库更新成功,另一个失败,这个也许最好使用一个应用服务器事务管理器的cache-aside结构(更新缓存的数据库,用一个两个组件的单独事务)。在许多案例中,有可能涉及数据库方案来防止逻辑提交失败(但是明显不是服务器失败)。Write-behind缓存避免了这个问题,puts不会影响数据库的行为(潜在的问题在涉及的时候已经处理了)。

    7.3 缓存队列

    Cache队列只在缓存的data store上操作,并不触发CacheStore来加载任何缺少的数据。因此,应用程序查询CacheStore backed caches应该确保所有需要查询的数据已经被预加载了。为了提高效率,多数的大量加载操作应该在程序启动的时候完成,通过从数据库直接流动数据集到缓存(通过使用NamedCache.putAll()毗邻加载数据块进混村。加载进程必须使用"Controllable CacheStore"模式来禁止通知更新返回给数据库。CacheStore可以使用Invocation service来控制(通过cluster发送代理来修改每个jvm中的local flag),或者通过设置Replicated Cache(不同的缓存服务)的值,在每个CacheStore方法请求中读取它(与常用数据库操作,最小化开销)。自定义的MBean 也能被使用,用Coherence的cluster JMX 设备是一个简单的任务。

    7.4  Re-entrant Calls

    CacheStore不能回调给托管缓存服务。这个包含了ORM解决方案,它内部的应用Coherence 缓存服务。注意的是允许调用另一个缓存服务,尽管应该小心,鼻渊深层的嵌套调用(每个称为"消费"一个缓存服务进程,能够会导致死锁,如果缓存服务的线程池耗尽)。

    7.5 Cache Server Classpath

    缓存条目的类必须在cache server的classpath中,cache服务器必须序列化-反序列化缓存条目来和CacheStore 模块交互。

    7.6 CacheStore Collection Operation

    CacheStore.storeAll 方法最有可能被使用,如果缓存缓存配置了write-behind和<write-batch-factor>。CacheLoader.loadAl方法也被Coherence使用。基于相似的理由,首次使用时候,需要开启refresh-ahead。

    7.7 Connection Pools

    数据库连接应该能够从container连接池中找到,或者使用本地线程 lazy-initialization模式。作为专业的缓存服务器,经常背书在没有管理容器,后者可能是最有有吸引力的选择(尽管缓存服务京城之的大小应该被约束,避免过多的同步数据库连接)。

  • 相关阅读:
    C#取枚举描述
    JSON数组操作
    .NET DES 加密
    MVC返回图片
    with check option
    HashSet<T>类
    Repository模式
    C#泛型集合之Dictionary<k, v>使用技巧
    权限管理——在线列表
    数据库锁
  • 原文地址:https://www.cnblogs.com/danye/p/CoherenceCaches4.html
Copyright © 2011-2022 走看看