プロジェクト中のHibernateの最適化:Cache.


自己:http://blog.csdn.net/qianling3439/article/details/5772187
これを書く目的はどのcacheの効率が高いかを説明するためではなく、どのcacheがhibernateに適していますか?ただ、hibernateがcacheを使っている時の仕組みを解明するために、自分と会った実際の問題を明らかにします.
二、hibernate二級キャッシュはCacheを検索するのを避けるために、先にdb接続を獲得する必要がある.
hibernate自身が一級キャッシュを管理していますが、もし二級キャッシュを使う必要があるなら、自分で該当コードを実現します.これは複雑ではなく、実現する必要があります.
ヒベルナが提供する対応するインターフェースでいいです.私たちはプロジェクトの中で最も通用するmemcachedを選択しました.具体的な構成は以下の通りです.
springにhibernature Propertiesを配置すると、一つの構成が追加されます.

<prop key="hibernate.cache.provider_class">com.*.frame.cache.memcached.MemcachedCacheProvider</prop>
MemcachedCacheProviderは、ヒベルナが提供するCachProviderインターフェースを実現する必要がある:

public class MemcachedCacheProvider implements CacheProvider {   
    public final static String DEFAULT_REGION_NAME="____DEFAULT_CACHE_REGION";   
  
    public void start(Properties props) throws CacheException {   
        CachePoolManager pool = CachePoolManager.getInstance();   
    }   
  
    public Cache buildCache(String name, Properties props) throws CacheException {   
        if(StringUtils.isEmpty(name))   
            name = DEFAULT_REGION_NAME;   
        //             Memacached      
        MemCache mCache = (MemCache)CachePoolManager.getInstance().getCache(name);   
        return mCache;   
    }   
  
    public void stop() {   
//      CachePoolManager.getInstance().finalize();   
    }   
 
    public boolean isMinimalPutsEnabledByDefault() {   
        return false;   
    }   
  
    public long nextTimestamp() {   
        return Timestamper.next();   
    }   
}  
Memcachedエンティティ類も、hibernateが提供するCacheインターフェースを実現しなければならない:

public class MemCache implements Cache {   
    private static final Log log = LogFactory.getLog(MemCache.class);   
    private static final int SIXTY_THOUSAND_MS = 60000;   
    private MemCachedClient mc;   
    private int secondToLive;   
    private String cache_name;   
    private String poolName;   
    public MemCache(String poolName, String regionName, int secondToLive){   
*****         
/**  
     * Get an item from the cache  
     * @param key  
     * @return the cached object or <tt>null</tt>  
     * @throws CacheException  
     */  
    public Object read(Object key) throws CacheException;   
    /**  
     * Get an item from the cache, nontransactionally  
     * @param key  
     * @return the cached object or <tt>null</tt>  
     * @throws CacheException  
     */  
    public Object get(Object key) throws CacheException;   
    /**  
     * Add an item to the cache, nontransactionally, with  
     * failfast semantics  
     * @param key  
     * @param value  
     * @throws CacheException  
     */  
    public void put(Object key, Object value) throws CacheException;   
    /**  
     * Add an item to the cache  
     * @param key  
     * @param value  
     * @throws CacheException  
     */  
    public void update(Object key, Object value) throws CacheException;   
    /**  
     * Remove an item from the cache  
     */  
    public void remove(Object key) throws CacheException;   
    /**  
     * Clear the cache  
     */  
    public void clear() throws CacheException;   
    /**  
     * Clean up  
     */  
    public void destroy() throws CacheException;   
    /**  
     * If this is a clustered cache, lock the item  
     */  
    public void lock(Object key) throws CacheException;   
    /**  
     * If this is a clustered cache, unlock the item  
     */  
    public void unlock(Object key) throws CacheException;   
    /**  
     * Generate a timestamp  
     */  
    public long nextTimestamp();   
    /**  
     * Get a reasonable "lock timeout"  
     */  
    public int getTimeout();   
       
    /**  
     * Get the name of the cache region  
     */  
    public String getRegionName();   
  
    /**  
     * The number of bytes is this cache region currently consuming in memory.  
     *  
     * @return The number of bytes consumed by this region; -1 if unknown or  
     * unsupported.  
     */  
    public long getSizeInMemory();   
  
    /**  
     * The count of entries currently contained in the regions in-memory store.  
     *  
     * @return The count of entries in memory; -1 if unknown or unsupported.  
     */  
    public long getElementCountInMemory();   
  
    /**  
     * The count of entries currently contained in the regions disk store.  
     *  
     * @return The count of entries on disk; -1 if unknown or unsupported.  
     */  
    public long getElementCountOnDisk();   
       
    /**  
     * optional operation  
     */  
    public Map toMap();   
}
MemcachedエンティティクラスのMemCachedClientの実現については、より多くのオンラインで流布されている異なるバージョンを修正し、最適化することができます.
第四のステップは、キャッシュを使用する必要があるクラスに対応するマッピングファイルにキャッシュポリシーの配置を追加し、例えば、「cache usage=「nonstrict-read-write」/」、他にもread-only、read-write、tranctionalなどがあります.
    大成功を収めて、ヒップホップ・ショーを開く.sql再起動アプリケーション.リストを更新して、初めて山のようなsql文を見ました.再度の更新は二条しか現れません.三回目かそれとも二条です.私達が設定した二級キャッシュは有効になりました.
一回の圧力テストをして、具体的な圧力テストの報告がなくなって、具体的な数字を出すことができません.当時の大体の現象は、アプリケーションの応答が遅く、データベース接続がほとんど無くなり、待ち時間が大変でした.
私たちは、hibernateがcacheから検索した時に、まずデータベースから該当のIDを取って、IDによってcacheから該当のデータを取って帰ってきます.圧力テストはただシミュレーションの同一の要求で、毎回調べたいものは必ずcacheに存在するという意味です.なぜdbConnectionは足りないですか?
    システム問題を分析するリAーが登場します.ちなみに、java開発は避けられない枠組みを採用しています.もちろん元の生態を堅持する人もいます.ほとんどのフレームはロゴ4 jでログを出力しています.ログを詳しく分析すると、多くの問題を発見することができます.まず、ログレベルのDebugを設定し、次にいくつかの不要な情報を削除します.例えば、初期化、設定ファイルのロードなどです.準備が完了したら、ユーザーの行動をシミュレーションして操作します.このとき出力したログは、実行を要求するフィルタ、ブロッキング、方法などのスタック情報を記録します.  

[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - begin   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.jdbc.ConnectionManager - opening JDBC connection   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - current autocommit status: true  
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - disabling autocommit   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.cache.NonstrictReadWriteCache - Cache lookup: com.**#3827849  
[DEBUG] 2010-07-28 11:28:31 com.**.frame.cache.memcached.MemCache - key: com.**#3827849  
[DEBUG] 2010-07-28 11:28:31 com.**.frame.cache.memcached.SockIOPool$SockIO - ++++ marking socket (Socket[addr=/127.0.0.1,port=11211,localport=56135]) as closed and available to return to avail pool   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.cache.NonstrictReadWriteCache - Cache hit//       
[DEBUG] 2010-07-28 11:28:31 org.hibernate.engine.StatefulPersistenceContext - initializing non-lazy collections   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - commit   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - re-enabling autocommit   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.transaction.JDBCTransaction - committed JDBC Connection   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.jdbc.ConnectionManager - aggressively releasing JDBC connection   
[DEBUG] 2010-07-28 11:28:31 org.hibernate.jdbc.ConnectionManager - releasing JDBC connection  

    logl 4 jログを分析すると、hibernateは毎回jdbcConnectionをゲットして、データベースからいっぱいのIDを調べて、cacheに行って対象を探しています.cacheにデータが存在する場合は取り出し、事務解放接続を提出する.cacheにない場合は、データベースを調べ、結果を保存したcacheに事務解放接続を提出する.問題が来ました.cacheの中に検索したいデータがありますが、なぜ先にデータベース接続を取って、事務をオープンしてからキャッシュに検索しますか?すべてのユーザが照会した内容がcacheに存在する場合、各要求はjdbc接続を取得してcacheを検索することができ、システムのボトルネックは必然的に出現する接続池の接続数になります.これは、なぜ圧力テストをする時にデータベース接続がなくなったのかを説明するのに難しくないです.これにより、hibernateはデータベース接続を維持してcacheにおける検索データを維持する必要があり、データベースの検索回数を減らしただけで、アプリケーションのdb接続数を減少させていないと結論した.
    この問題はどう解決しますか?理想的な状況は、cacheの中には直接cacheの中で調べて、cacheの中にないのはdbに行って調べて、cacheの中に入れます.コードを修正して、データベースからIDだけを調べて、IDによってcacheから該当データを検索します.cacheにないデータはhibernateの検索方法を呼び出します.以前はキャッシュポリシーが設定されていましたので、hibernateは該当データを調べたら自動的にcacheに入れます.このように、最初の要求はdbに行ってIDを取得し、その後全てのデータをcacheに入れ、二番目の要求は先にIDを調べてcacheに行ってデータを検索する.以降は毎回、IDを調べてデータベース接続を取得し、結果セットを返して接続を解除します.これにより、ヒベルナタはdb接続を維持してcacheを調べる必要があります.
    cacheからデータを取得するところにトラブルがありました.hibernateはcacheの対象に書き込みます.コードを書いて取っても解析できません.関連資料によると、hibernateは2級キャッシュに預け入れられたものをデフォルトでカプセル化し、対外的なインターフェースを提供しないでデータの解析を行い、原因とパッケージがどのようなタイプになるかについてはここでは言及しない.私たちは配置を変更することによって、ヒベルナに保存されたオブジェクトのcacheにおけるデータ構造を変更することができる.具体的な方法:hibernature Propertiesを配置するには、配置を追加します.

<prop key="hibernate.cache.use_structured_entries">true</prop>
このようにして二段キャッシュに格納されたオブジェクトは、属性名と属性値を格納するmapであり、mapを解析すれば、該当データが得られます.
    このようにして、二級キャッシュの使用には多少の侵入性があるが、ヒベルナの二級キャッシュの読み取りはデフォルトのようにデータベース接続を維持する必要がないことが保証されている.第二の問題は解決する.