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模式。作为专业的缓存服务器,经常背书在没有管理容器,后者可能是最有有吸引力的选择(尽管缓存服务京城之的大小应该被约束,避免过多的同步数据库连接)。

posted on 2012-07-28 09:12  萌二&威比  阅读(2667)  评论(0编辑  收藏  举报

导航