從零開始手寫redis(18)緩存淘汰算法 FIFO 優(yōu)化

項(xiàng)目簡介

大家好,我是老馬。

Cache 用于實(shí)現(xiàn)一個(gè)可拓展的高性能本地緩存。

有人的地方,就有江湖。有高性能的地方,就有 cache。

v1.0.0 版本

以前的 FIFO 實(shí)現(xiàn)比較簡單,但是 queue 循環(huán)一遍刪除的話,性能實(shí)在是太差。

于是想到引入一個(gè) Set 存儲有哪些 key,改成下面的方式:

package com.github.houbb.cache.core.support.evict.impl;

import com.github.houbb.cache.api.ICacheContext;
import com.github.houbb.cache.core.model.CacheEntry;
import com.github.houbb.cache.core.support.evict.AbstractCacheEvict;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

/**
 * 丟棄策略-先進(jìn)先出
 * @author binbin.hou
 * @since 0.0.2
 */
public class CacheEvictFifo<K,V> extends AbstractCacheEvict<K,V> {

    /**
     * queue 信息
     * @since 0.0.2
     */
    private final Queue<K> queue = new LinkedList<>();

    /**
     * 避免數(shù)據(jù)重復(fù)加入問題
     * @since 1.0.1
     */
    private final Set<K> keySet = new HashSet<>();

    @Override
    public CacheEntry<K,V> doEvict(ICacheContext<K, V> context, final K newKey) {
        CacheEntry<K,V> result = null;

        // 超過限制,執(zhí)行移除
        if(isNeedEvict(context)) {
            K evictKey = queue.remove();
            keySet.remove(evictKey);
            // 移除最開始的元素
            V evictValue = doEvictRemove(context, evictKey);
            result = new CacheEntry<>(evictKey, evictValue);
        }

        return result;
    }

    @Override
    public void updateKey(ICacheContext<K, V> context, K key) {
        if (!keySet.contains(key)) {
            queue.add(key);
            keySet.add(key);
        }
    }

}

這里雖然可以解決 fifo 的刪除問題,但是內(nèi)存有點(diǎn)浪費(fèi)。

而且這樣其實(shí)順序也太對,每次還是需要更新 queue 的位置。

我們把結(jié)構(gòu)繼續(xù)調(diào)整一下,用其他的數(shù)據(jù)結(jié)構(gòu)來替代。

v1.0.1 實(shí)現(xiàn)

其他的方式

方案 數(shù)據(jù)結(jié)構(gòu) 內(nèi)存開銷 實(shí)現(xiàn)難度 是否推薦
Queue + Set 兩個(gè)結(jié)構(gòu) 較大 簡單 ?
LinkedHashSet 單結(jié)構(gòu) 簡單 ? 推薦
LinkedHashMap Map+鏈表 中等 中等 ? 可選

實(shí)現(xiàn)

簡單起見,我們使用 LinkedHashSet 來實(shí)現(xiàn)。

package com.github.houbb.cache.core.support.evict.impl;

import com.github.houbb.cache.api.ICacheContext;
import com.github.houbb.cache.core.model.CacheEntry;
import com.github.houbb.cache.core.support.evict.AbstractCacheEvict;

import java.util.*;

/**
 * 丟棄策略-先進(jìn)先出
 * @author binbin.hou
 * @since 0.0.2
 */
public class CacheEvictFifo<K,V> extends AbstractCacheEvict<K,V> {

    /**
     * queue 信息
     * @since 0.0.2
     */
    private final Set<K> accessOrder = new LinkedHashSet<>();;

    @Override
    public CacheEntry<K,V> doEvict(ICacheContext<K, V> context, final K newKey) {
        CacheEntry<K,V> result = null;

        // 超過限制,執(zhí)行移除
        if(isNeedEvict(context)) {
            Iterator<K> iterator = accessOrder.iterator();
            K evictKey = iterator.next();
            V evictValue = doEvictRemove(context, evictKey);
            iterator.remove();

            // 移除最開始的元素
            result = new CacheEntry<>(evictKey, evictValue);
        }

        return result;
    }

    @Override
    public void updateKey(ICacheContext<K, V> context, K key) {
        accessOrder.remove(key);
        accessOrder.add(key);
    }

}

這樣我們的目標(biāo)算是達(dá)成了,實(shí)現(xiàn)了內(nèi)存和性能的平衡。

拓展信息

開源矩陣

下面是一些緩存系列的開源矩陣規(guī)劃。

名稱 介紹 狀態(tài)
resubmit 防止重復(fù)提交核心庫 已開源
rate-limit 限流核心庫 已開源
cache 手寫漸進(jìn)式 redis 已開源
lock 開箱即用的分布式鎖 已開源
common-cache 通用緩存標(biāo)準(zhǔn)定義 已開源
redis-config 兼容各種常見的 redis 配置模式 已開源
quota-server 限額限次核心服務(wù) 待開始
quota-admin 限額限次控臺 待開始
flow-control-server 流控核心服務(wù) 待開始
flow-control-admin 流控控臺 待開始

手寫 Redis 系列

java從零手寫實(shí)現(xiàn)redis(一)如何實(shí)現(xiàn)固定大小的緩存?

java從零手寫實(shí)現(xiàn)redis(三)redis expire 過期原理

java從零手寫實(shí)現(xiàn)redis(三)內(nèi)存數(shù)據(jù)如何重啟不丟失?

java從零手寫實(shí)現(xiàn)redis(四)添加監(jiān)聽器

java從零手寫實(shí)現(xiàn)redis(五)過期策略的另一種實(shí)現(xiàn)思路

java從零手寫實(shí)現(xiàn)redis(六)AOF 持久化原理詳解及實(shí)現(xiàn)

java從零手寫實(shí)現(xiàn)redis(七)LRU 緩存淘汰策略詳解

java從零開始手寫redis(八)樸素 LRU 淘汰算法性能優(yōu)化

java從零開始手寫redis(九)LRU 緩存淘汰算法如何避免緩存污染

java從零開始手寫redis(十)緩存淘汰算法 LFU 最少使用頻次

java從零開始手寫redis(十一)緩存淘汰算法 COLOK 算法

java從零開始手寫redis(十二)過期策略如何實(shí)現(xiàn)隨機(jī) keys 淘汰

java從零開始手寫redis(十三)redis漸進(jìn)式rehash詳解

java從零開始手寫redis(十四)JDK HashMap 源碼解析

java從零開始手寫redis(十四)JDK ConcurrentHashMap 源碼解析

java從零開始手寫redis(十五)實(shí)現(xiàn)自己的 HashMap

java從零開始手寫redis(十六)實(shí)現(xiàn)漸進(jìn)式 rehash map

java從零開始手寫redis(十七)v1.0.0 代碼重構(gòu)+拓展性增強(qiáng)

java從零開始手寫redis(十八)緩存淘汰算法 FIFO 優(yōu)化

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

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

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