緩沖更新策略

近段時(shí)間在學(xué)習(xí)緩存相關(guān)知識(shí)的時(shí)候,看到了緩存更新策略,于是就根據(jù)自己的理解,寫下這篇文章

分類
  • Cache Aside
  • Read / Write Though
  • Write Behind
Cache Aside
  1. 步驟

    1. 讀請(qǐng)求未命中緩存,取數(shù)據(jù)庫數(shù)據(jù),并回寫緩存
    2. 寫請(qǐng)求先更新數(shù)據(jù)庫,再讓緩存失效
  2. 優(yōu)點(diǎn)

    1. 實(shí)現(xiàn)簡(jiǎn)單,調(diào)用者可控制數(shù)據(jù)持久化的細(xì)節(jié)
  3. 缺點(diǎn)

    1. 上層需要同時(shí)管理緩存與持久化,調(diào)用較復(fù)雜
    2. 寫請(qǐng)求與讀請(qǐng)求并發(fā),讀請(qǐng)求持續(xù)時(shí)間比寫請(qǐng)求長,可能會(huì)覆蓋舊數(shù)據(jù)到緩存中
  4. 使用場(chǎng)景

    1. 允許緩存數(shù)據(jù)不準(zhǔn)確的場(chǎng)景
    2. 因?yàn)椴l(fā)情況下,可能造成臟數(shù)據(jù)的情況,所以 QPS 較低場(chǎng)景也可以適用
  5. 代碼示例

public class CacheAside<T, K> implements CacheUpdate<T, K>{
    private Map<K, T> map;

    @Override
    public T getData(K key) {
        //if cache has data, return
        return map.get(key);
    }

    @Override
    public boolean updateData(K key, T data) {
        map.remove(key, data);
        return true;
    }

    @Override
    public boolean addData(K key, T data) {
        return Objects.nonNull(map.put(key, data));
    }

    @Override
    public boolean removeData(K key) {
        map.remove(key);
        return true;
    }

    public CacheAside() {
        map = new HashMap<>();
    }
}
  1. 調(diào)用示例
public class CacheAsideClient<T, K> implements CacheUpdateClient<T, K>{

    public CacheUpdateFactory<T, K> factory = CacheUpdateFactory.getInstance();

    private CacheUpdate<T, K> cacheUpdate;

    private DatabaseOperation<T, K> databaseOperation;

    @Override
    public T getData(K key){
        //get data from cache
        T dataFromCache = cacheUpdate.getData(key);
        //if cache haven't, get from database and put to cache
        if(Objects.nonNull(dataFromCache)){
            return dataFromCache;
        }
        T dataFromDatabase = databaseOperation.getData(key);
        cacheUpdate.addData(key, dataFromDatabase);
        return dataFromDatabase;
    }

    @Override
    public boolean updateData(K key, T data){
        //update data to database
        boolean updateToDatabaseRes = databaseOperation.updateData(key, data);
        if(updateToDatabaseRes){
            //invalid cache data
            return cacheUpdate.removeData(key);
        }
        return false;
    }

    @Override
    public boolean addData(K key, T data){
        //add data to database
        return databaseOperation.addData(key, data);
    }

    @Override
    public boolean removeData(K key){
        //remove from database
        boolean removeFromDatabaseRes = databaseOperation.removeData(key);
        if(removeFromDatabaseRes){
            //invalid cache data
            return cacheUpdate.removeData(key);
        }
        return false;
    }

    public CacheAsideClient() {
        cacheUpdate = factory.getObject(CacheUpdateEnum.CACHE_ASIDE);
        databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<T>();
    }
}
Read / Write Though
  1. 步驟

    1. 讀/寫請(qǐng)求都只依賴緩存
    2. 緩存數(shù)據(jù)同步持久化
  2. 優(yōu)點(diǎn)

    1. 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感
  3. 缺點(diǎn)

    1. 同步持久化性能較低,但能有效保證數(shù)據(jù)一致性
  4. 使用場(chǎng)景

    1. 性能要求不高的場(chǎng)景
  5. 代碼示例

public class ReadOrWriteThough<T, K> implements CacheUpdate<T, K>{

    private DatabaseOperation<T, K> databaseOperation;

    private Map<K, T> map;

    @Override
    public T getData(K key) {
        //if cache has data, return
        if(map.containsKey(key)){
            return map.get(key);
        }
        //get data from database and write to cache
        T data = databaseOperation.getData(key);
        map.put(key, data);
        return data;
    }

    @Override
    public boolean updateData(K key, T data) {
        map.put(key, data);
        return databaseOperation.updateData(key, data);
    }

    @Override
    public boolean addData(K key, T data) {
        map.put(key, data);
        return databaseOperation.addData(key, data);
    }

    @Override
    public boolean removeData(K key) {
        map.remove(key);
        return databaseOperation.removeData(key);
    }

    public ReadOrWriteThough() {
        databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<>();
        map = new HashMap<>();
    }
}
  1. 調(diào)用示例
public class ReadOrWriteThoughClient<T, K> implements CacheUpdateClient<T, K>{

    private CacheUpdateFactory<T, K> factory = CacheUpdateFactory.getInstance();

    private CacheUpdate<T, K> cacheUpdate;

    @Override
    public T getData(K key) {
        return cacheUpdate.getData(key);
    }

    @Override
    public boolean updateData(K key, T data) {
        return cacheUpdate.updateData(key, data);
    }

    @Override
    public boolean addData(K key, T data) {
        return cacheUpdate.addData(key, data);
    }

    @Override
    public boolean removeData(K key) {
        return cacheUpdate.removeData(key);
    }

    public ReadOrWriteThoughClient() {
        cacheUpdate = factory.getObject(CacheUpdateEnum.READ_WRITE_THOUGH);
    }
}
Write Behind
  1. 步驟

    1. 讀/寫請(qǐng)求都只依賴緩存
    2. 緩存數(shù)據(jù)異步批量持久化
  2. 優(yōu)點(diǎn)

    1. 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感
    2. 異步持久化,性能較 Read /Write Though 提高
  3. 缺點(diǎn)

    1. 異步持久化可能會(huì)導(dǎo)致數(shù)據(jù)丟失
  4. 使用場(chǎng)景

    1. 性能要求較高的場(chǎng)景
    2. 允許持久化數(shù)據(jù)丟失場(chǎng)景
  5. 代碼示例

public class WriteBehind<T, K> implements CacheUpdate<T, K> {

    private Map<K, T> map;

    private DatabaseOperation<T, K> databaseOperation;

    private ThreadPoolExecutor threadPoolExecutor;

    @Override
    public T getData(K key) {
        if(map.containsKey(key)){
            return map.get(key);
        }
        T data = databaseOperation.getData(key);
        map.put(key, data);
        return data;
    }

    @Override
    public boolean updateData(K key, T data) {
        map.put(key, data);
        threadPoolExecutor.execute(() -> databaseOperation.updateData(key, data));
        return true;
    }

    @Override
    public boolean addData(K key, T data) {
        map.put(key, data);
        threadPoolExecutor.execute(() -> databaseOperation.addData(key, data));
        return true;
    }

    @Override
    public boolean removeData(K key) {
        map.remove(key);
        threadPoolExecutor.execute(() -> databaseOperation.removeData(key));
        return true;
    }

    public WriteBehind() {
        map = new HashMap<>();
        databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<>();
        threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
    }

}
  1. 調(diào)用示例
public class WriteBehindClient<T, K> implements CacheUpdateClient<T, K>{

    private CacheUpdateFactory<T, K> cacheUpdateFactory = CacheUpdateFactory.getInstance();

    private CacheUpdate<T, K> cacheUpdate;

    @Override
    public T getData(K key) {
        return cacheUpdate.getData(key);
    }

    @Override
    public boolean updateData(K key, T data) {
        return cacheUpdate.updateData(key, data);
    }

    @Override
    public boolean addData(K key, T data) {
        return cacheUpdate.addData(key, data);
    }

    @Override
    public boolean removeData(K key) {
        return cacheUpdate.removeData(key);
    }

    public WriteBehindClient() {
        cacheUpdate = cacheUpdateFactory.getObject(CacheUpdateEnum.WRITE_BEHIND);
    }
}
總結(jié)
分類 優(yōu)點(diǎn) 缺點(diǎn) 使用場(chǎng)景
Cache Aside 1. 實(shí)現(xiàn)簡(jiǎn)單,調(diào)用者可控制數(shù)據(jù)持久化的細(xì)節(jié) 1. 寫請(qǐng)求與讀請(qǐng)求并發(fā),讀請(qǐng)求持續(xù)時(shí)間比寫請(qǐng)求長,可能會(huì)覆蓋舊數(shù)據(jù)到緩存中<br />2. 上層需要同時(shí)管理緩存與持久化,調(diào)用較復(fù)雜 1. 允許緩存數(shù)據(jù)不準(zhǔn)確的場(chǎng)景<br />2. 因?yàn)椴l(fā)情況下,可能造成臟數(shù)據(jù)的情況,所以 QPS 較低場(chǎng)景也可以適用
Read / Write Though 1. 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感 1. 同步持久化性能較低,但能有效保證數(shù)據(jù)一致性 1. 性能要求不高的場(chǎng)景
Write Behind 1. 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感<br />2. 異步持久化,性能較 Read /Write Though 提高 1. 異步持久化可能會(huì)導(dǎo)致數(shù)據(jù)丟失 1. 性能要求較高的場(chǎng)景<br />2. 允許持久化數(shù)據(jù)丟失場(chǎng)景

本文首發(fā)于cartoon的博客

轉(zhuǎn)載請(qǐng)注明出處:https://cartoonyu.github.io

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 隨著移動(dòng)互聯(lián)網(wǎng)的野蠻瘋長,各種互聯(lián)網(wǎng)技術(shù)層出不窮,但是不管各種技術(shù)框架如何滋長,永遠(yuǎn)無法繞過的一個(gè)坎,那就是數(shù)據(jù)庫...
    丑人林宗己閱讀 7,496評(píng)論 0 6
  • 1、為什么需要緩存 一般在項(xiàng)目中,最消耗性能的地方就是后端服務(wù)的數(shù)據(jù)庫了。而數(shù)據(jù)庫的讀寫頻率常常都是不均勻分布的,...
    冰河winner閱讀 1,468評(píng)論 0 2
  • [TOC] 參考 Cache Aside Pattern 究竟先操作緩存,還是數(shù)據(jù)庫? 緩存更新的套路 使用緩存的...
    GOGOYAO閱讀 3,427評(píng)論 0 0
  • 緩存一般是為了應(yīng)對(duì)高并發(fā)場(chǎng)景、緩解數(shù)據(jù)庫讀寫壓力,而將數(shù)據(jù)存儲(chǔ)在讀寫更快的某種存儲(chǔ)介質(zhì)中(如內(nèi)存),以加快讀取數(shù)據(jù)...
    C1R2閱讀 962評(píng)論 0 0
  • Flink 極簡(jiǎn)教程: 架構(gòu)及原理 Apache Flink? — Stateful Computations o...
    光劍書架上的書閱讀 932評(píng)論 0 3

友情鏈接更多精彩內(nèi)容