<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0
    前記:最近公司在做的項目完全基于Cache(Gemfire)構(gòu)建了一個類數(shù)據(jù)庫的系統(tǒng),自己做的一個小項目里用過Guava的Cache,以前做過的項目中使用過EHCache,既然和Cache那么有緣,那就趁這個機會好好研究一下Java中的Cache庫。在Java社區(qū)中已經(jīng)提供了很多Cache庫實現(xiàn),具體可以參考http://www.open-open.com/13.htm,這里只關(guān)注自己用到的幾個Cache庫而且這幾個庫都比較具有代表性:Guava中提供的Cache是基于單JVM的簡單實現(xiàn);EHCache出自Hibernate,也是基于單JVM的實現(xiàn),是對單JVM Cache比較完善的實現(xiàn);而Gemfire則提供了對分布式Cache的完善實現(xiàn)。這一系列的文章主要關(guān)注在這幾個Cache系統(tǒng)的實現(xiàn)上,因而步探討關(guān)于Cache的好處、何時用Cache等問題,由于他們都是基于內(nèi)存的Cache,因而也僅局限于這種類型的Cache(說實話,我不知道有沒有其他的Cache系統(tǒng),比如基于文件?囧)。

    記得我最早接觸Cache是在大學(xué)學(xué)計算機組成原理的時候,由于CPU的速度要遠大于內(nèi)存的讀取速度,為了提高CPU的效率,CPU會在內(nèi)部提供緩存區(qū),該緩存區(qū)的讀取速度和CPU的處理速度類似,CPU可以直接從緩存區(qū)中讀取數(shù)據(jù),從而解決CPU的處理速度和內(nèi)存讀取速度不匹配的問題。緩存之所以能解決這個問題是基于程序的局部性原理,即”程序在執(zhí)行時呈現(xiàn)出局部性規(guī)律,即在一段時間內(nèi),整個程序的執(zhí)行僅限于程序中的某一部分。相應(yīng)地,執(zhí)行所訪問的存儲空間也局限于某個內(nèi)存區(qū)域。局部性原理又表現(xiàn)為:時間局部性和空間局部性。時間局部性是指如果程序中的某條指令一旦執(zhí)行,則不久之后該指令可能再次被執(zhí)行;如果某數(shù)據(jù)被訪問,則不久之后該數(shù)據(jù)可能再次被訪問。空間局部性是指一旦程序訪問了某個存儲單元,則不久之后。其附近的存儲單元也將被訪問。”在實際工作中,CPU先向緩存區(qū)讀取數(shù)據(jù),如果緩存區(qū)已存在,則讀取緩存中的數(shù)據(jù)(命中),否則(失效),將內(nèi)存中相應(yīng)數(shù)據(jù)塊載入緩存中,以提高接下來的訪問速度。由于成本和CPU大小的限制,CPU只能提供有限的緩存區(qū),因而緩存區(qū)的大小是衡量CPU性能的重要指標之一。

    使用緩存,在CPU向內(nèi)存更新數(shù)據(jù)時需要處理一個問題(寫回策略問題),即CPU在更新數(shù)據(jù)時只更新緩存的數(shù)據(jù)(write back,寫回,當緩存需要被替換時才將緩存中更新的值寫回內(nèi)存),還是CPU在更新數(shù)據(jù)時同時更新緩存中和內(nèi)存中的數(shù)據(jù)(write through,寫通)。在寫回策略中,為了減少內(nèi)存寫操作,緩存塊通常還設(shè)有一個臟位(dirty bit),用以標識該塊在被載入之后是否發(fā)生過更新。如果一個緩存塊在被置換回內(nèi)存之前從未被寫入過,則可以免去回寫操作;寫回的優(yōu)點是節(jié)省了大量的寫操作。這主要是因為,對一個數(shù)據(jù)塊內(nèi)不同單元的更新僅需一次寫操作即可完成。這種內(nèi)存帶寬上的節(jié)省進一步降低了能耗,因此頗適用于嵌入式系統(tǒng)。寫通策略由于要經(jīng)常和內(nèi)存交互(有些CPU設(shè)計會在中間提供寫緩沖器以緩解性能),因而性能較差,但是它實現(xiàn)簡單,而且能簡單的維持數(shù)據(jù)一致性。

    在軟件的緩存系統(tǒng)中,一般是為了解決內(nèi)存的訪問速率和磁盤、網(wǎng)絡(luò)、數(shù)據(jù)庫(屬于磁盤或網(wǎng)絡(luò)訪問,單獨列出來因為它的應(yīng)用比較廣泛)等訪問速率不匹配的問題(對于內(nèi)存緩存系統(tǒng)來說)。但是由于內(nèi)存大小和成本的限制,我們又不能把所有的數(shù)據(jù)先加載進內(nèi)存來。因而如CPU中的緩存,我們只能先將一部分數(shù)據(jù)保存在緩存中。此時,對于緩存,我們一般要解決如下需求:
    1. 使用給定Key從Cache中讀取Value值。CPU是通過內(nèi)存地址來定位內(nèi)存已獲取相應(yīng)內(nèi)存中的值,類似的在軟件Cache中,需要通過某個Key值來標識相關(guān)的值。因而可以簡單的認為軟件中的Cache是一個存儲鍵值對的Map,比如Gemfire中的Region就繼承自Map,只是Cache的實現(xiàn)更加復(fù)雜。
    2. 當給定的Key在當前Cache不存在時,程序員可以通過指定相應(yīng)的邏輯從其他源(如數(shù)據(jù)庫、網(wǎng)絡(luò)等源)中加載該Key對應(yīng)的Value值,同時將該值返回。在CPU中,基于程序局部性原理,一般是默認的加載接下來的一段內(nèi)存塊,然而在軟件中,不同的需求有不同的加載邏輯,因而需要用戶自己指定對應(yīng)的加載邏輯,而且一般來說也很難預(yù)知接下來要讀取的數(shù)據(jù),所以只能一次只加載一條紀錄(對可預(yù)知的場景下當然可以批量加載數(shù)據(jù),只是此時需要權(quán)衡當前操作的響應(yīng)時間問題)。
    3. 可以向Cache中寫入Key-Value鍵值對(新增的紀錄或?qū)υ械逆I值對的更新)。就像CPU的寫回策略中有寫回和寫通策略,有些Cache系統(tǒng)提供了寫通接口。如果沒有提供寫通接口,程序員需要額外的邏輯處理寫通策略。也可以如CPU中的Cache一樣,只當相應(yīng)的鍵值對移出Cache以后,再將值寫回到數(shù)據(jù)源,可以提供一個標記位以決定要不要寫回(不過感覺這種實現(xiàn)比較復(fù)雜,代碼的的耦合度也比較高,如果為提升寫的速度,采用異步寫回即可,為防止數(shù)據(jù)丟失,可以使用Queue來存儲)。
    4. 將給定Key的鍵值對移出Cache(或給定多個Key以批量移除,甚至清除整個Cache)。
    5. 配置Cache的最大使用率,當Cache超過該使用率時,可配置溢出策略
      1. 直接移除溢出的鍵值對。在移除時決定是否要寫回已更新的數(shù)據(jù)到數(shù)據(jù)源。
      2. 將溢出的溢出的鍵值對寫到磁盤中。在寫磁盤時需要解決如何序列化鍵值對,如何存儲序列化后的數(shù)據(jù)到磁盤中,如何布局磁盤存儲,如何解決磁盤碎片問題,如何從磁盤中找回相應(yīng)的鍵值對,如何讀取磁盤中的數(shù)據(jù)并方序列化,如何處理磁盤溢出等問題。
      3. 在溢出策略中,除了如何處理溢出的鍵值對問題,還需要處理如何選擇溢出的鍵值對問題。這有點類似內(nèi)存的頁面置換算法(其實內(nèi)存也可以看作是對磁盤的Cache),一般使用的算法有:先進先出(FIFO)、最近最少使用(LRU)、最少使用(LFU)、Clock置換(類LRU)、工作集等算法。
    6. 對Cache中的鍵值對,可以配置其生存時間,以處理某些鍵值對在長時間不被使用,但是又沒能溢出的問題(因為溢出策略的選擇或者Cache沒有到溢出階段),以提前釋放內(nèi)存。
    7. 對某些特定的鍵值對,我們希望它能一直留在內(nèi)存中不被溢出,有些Cache系統(tǒng)提供PIN配置(動態(tài)或靜態(tài)),以確保該鍵值對不會被溢出。
    8. 提供Cache狀態(tài)、命中率等統(tǒng)計信息,如磁盤大小、Cache大小、平均查詢時間、每秒查詢次數(shù)、內(nèi)存命中次數(shù)、磁盤命中次數(shù)等信息。
    9. 提供注冊Cache相關(guān)的事件處理器,如Cache的創(chuàng)建、Cache的銷毀、一條鍵值對的添加、一條鍵值對的更新、鍵值對溢出等事件。
    10. 由于引入Cache的目的就是為了提升程序的讀寫性能,而且一般Cache都需要在多線程環(huán)境下工作,因而在實現(xiàn)時一般需要保證線程安全,以及提供高效的讀寫性能。
    在Java中,Map是最簡單的Cache,為了高效的在多線程環(huán)境中使用,可以使用ConcurrentHashMap,這也正是我之前參與的一個項目中最開始的實現(xiàn)(后來引入EHCache)。為了語意更加清晰、保持接口的簡單,下面我實現(xiàn)了一個基于Map的最簡單的Cache系統(tǒng),用以演示Cache的基本使用方式。用戶可以向它提供數(shù)據(jù)、查詢數(shù)據(jù)、判斷給定Key的存在性、移除給定的Key(s)、清除整個Cache等操作。以下是Cache的接口定義。
    public interface Cache<K, V> {
        public String getName();
        public V get(K key);
        public Map<? extends K, ? extends V> getAll(Iterator<? extends K> keys);
        public boolean isPresent(K key);
        public void put(K key, V value);
        public void putAll(Map<? extends K, ? extends V> entries);
        public void invalidate(K key);
        public void invalidateAll(Iterator<? extends K> keys);
        public void invalidateAll();
        public boolean isEmpty();
        public int size();
        public void clear();
        public Map<? extends K, ? extends V> asMap();
    }
    這個簡單的Cache實現(xiàn)只是對HashMap的封裝,之所以選擇HashMap而不是ConcurrentHashMap是因為在ConcurrentHashMap無法實現(xiàn)getAll()方法;并且這里所有的操作我都加鎖了,因而也不需要ConcurrentHashMap來保證線程安全問題;為了提升性能,我使用了讀寫鎖,以提升并發(fā)查詢性能。因為代碼比較簡單,所以把所有代碼都貼上了(懶得整理了。。。。)。
    public class CacheImpl<K, V> implements Cache<K, V> {
        private final String name;
        private final HashMap<K, V> cache;
        private final ReadWriteLock lock = new ReentrantReadWriteLock();
        private final Lock readLock = lock.readLock();
        private final Lock writeLock = lock.writeLock();
        
        public CacheImpl(String name) {
            this.name = name;
            cache = new HashMap<K, V>();
        }
        
        public CacheImpl(String name, int initialCapacity) {
            this.name = name;
            cache = new HashMap<K, V>(initialCapacity);
        }
        
        public String getName() {
            return name;
        }

        public V get(K key) {
            readLock.lock();
            try {
                return cache.get(key);
            } finally {
                readLock.unlock();
            }
        }

        public Map<? extends K, ? extends V> getAll(Iterator<? extends K> keys) {
            readLock.lock();
            try {
                Map<K, V> map = new HashMap<K, V>();
                List<K> noEntryKeys = new ArrayList<K>();
                while(keys.hasNext()) {
                    K key = keys.next();
                    if(isPresent(key)) {
                        map.put(key, cache.get(key));
                    } else {
                        noEntryKeys.add(key);
                    }
                }
                
                if(!noEntryKeys.isEmpty()) {
                    throw new CacheEntriesNotExistException(this, noEntryKeys);
                }
                
                return map;
            } finally {
                readLock.unlock();
            }
        }

        public boolean isPresent(K key) {
            readLock.lock();
            try {
                return cache.containsKey(key);
            } finally {
                readLock.unlock();
            }
        }

        public void put(K key, V value) {
            writeLock.lock();
            try {
                cache.put(key, value);
            } finally {
                writeLock.unlock();
            }
        }

        public void putAll(Map<? extends K, ? extends V> entries) {
            writeLock.lock();
            try {
                cache.putAll(entries);
            } finally {
                writeLock.unlock();
            }
        }

        public void invalidate(K key) {
            writeLock.lock();
            try {
                if(!isPresent(key)) {
                    throw new CacheEntryNotExistsException(this, key);
                }
                cache.remove(key);
            } finally {
                writeLock.unlock();
            }
        }

        public void invalidateAll(Iterator<? extends K> keys) {
            writeLock.lock();
            try {
                List<K> noEntryKeys = new ArrayList<K>();
                while(keys.hasNext()) {
                    K key = keys.next();
                    if(!isPresent(key)) {
                        noEntryKeys.add(key);
                    }
                }
                if(!noEntryKeys.isEmpty()) {
                    throw new CacheEntriesNotExistException(this, noEntryKeys);
                }
                
                while(keys.hasNext()) {
                    K key = keys.next();
                    invalidate(key);
                }
            } finally {
                writeLock.unlock();
            }
        }

        public void invalidateAll() {
            writeLock.lock();
            try {
                cache.clear();
            } finally {
                writeLock.unlock();
            }
        }

        public int size() {
            readLock.lock();
            try {
                return cache.size();
            } finally {
                readLock.unlock();
            }
        }

        public void clear() {
            writeLock.lock();
            try {
                cache.clear();
            } finally {
                writeLock.unlock();
            }
        }

        public Map<? extends K, ? extends V> asMap() {
            readLock.lock();
            try {
                return new ConcurrentHashMap<K, V>(cache);
            } finally {
                readLock.unlock();
            }
        }

        public boolean isEmpty() {
            readLock.lock();
            try {
                return cache.isEmpty();
            } finally {
                readLock.unlock();
            }
        }

    }
    其簡單的使用用例如下:
        @Test
        public void testCacheSimpleUsage() {
            Book uml = bookFactory.createUMLDistilled();
            Book derivatives = bookFactory.createDerivatives();
            
            String umlBookISBN = uml.getIsbn();
            String derivativesBookISBN = derivatives.getIsbn();
            
            Cache<String, Book> cache = cacheFactory.create("book-cache");
            cache.put(umlBookISBN, uml);
            cache.put(derivativesBookISBN, derivatives);
            
            Book fetchedBackUml = cache.get(umlBookISBN);
            System.out.println(fetchedBackUml);
            
            Book fetchedBackDerivatives = cache.get(derivativesBookISBN);
            System.out.println(fetchedBackDerivatives);
        }
    posted on 2013-10-15 23:46 DLevin 閱讀(15755) 評論(1)  編輯  收藏 所屬分類: CodeToolsEHCache

    FeedBack:
    # re: Java Cache系列之Cache概述和Simple Cache
    2013-10-17 19:16 | ayesd
    不知道common-jcs 是否和你研究的cache相關(guān)  回復(fù)  更多評論
      
    主站蜘蛛池模板: 一本岛v免费不卡一二三区| 91精品全国免费观看青青| 青青草无码免费一二三区| 免费一看一级毛片| 亚洲最大在线视频| 免费人成激情视频在线观看冫| 性感美女视频在线观看免费精品| 亚洲福利视频导航| 久久精品无码免费不卡| 国产精品四虎在线观看免费| 亚洲精品中文字幕无乱码麻豆| 无码av免费网站| 亚洲毛片αv无线播放一区| 黄色大片免费网站| 全免费a级毛片免费看不卡| 亚洲成A∨人片在线观看无码| 永久免费av无码入口国语片| 亚洲日本一区二区一本一道 | 亚洲精品无AMM毛片| 亚洲香蕉免费有线视频| 国产亚洲福利精品一区| jzzjzz免费观看大片免费| 四虎影视精品永久免费| 亚洲高清乱码午夜电影网| 最近中文字幕无吗免费高清| 亚洲欧洲自拍拍偷午夜色| 久久久久国色av免费看| 亚洲日韩精品A∨片无码| 一个人看的免费视频www在线高清动漫| 国产大片线上免费看| 亚洲精品国产av成拍色拍| 大地资源免费更新在线播放| 亚洲啪啪免费视频| xxxxx免费视频| 亚洲理论片在线观看| 9277手机在线视频观看免费| 亚洲男人天堂av| 最近免费最新高清中文字幕韩国 | 韩国免费一级成人毛片| 亚洲欧洲日韩综合| 亚洲精品在线免费观看视频|