1. 程式人生 > >談談使用Redis緩存時批量刪除的幾種實現

談談使用Redis緩存時批量刪除的幾種實現

cursor 時也 ons 純粹 rod 依據 大致 比較 根據

前言

在使用緩存的時候,我們時不時會遇到這樣一個需求,根據緩存鍵的規則去批量刪除這些數據,比較常見的就是按前綴去刪除。

舉個簡單的例子,Redis中現在有幾百個商品的數據,這些數據的key值是有一定規律的,都是以product:id的形式存在的。

現在由於不得以為的原因要刪除這幾百個商品的數據,這個時候我們肯定就要把緩存鍵以product:開頭的給全部刪除掉。

其實這個需求在Redis中是可以很容易去實現的。

來看看幾種常見的做法。

常見的幾種做法

  1. 用Keys命令找到key之後執行刪除操作
  2. 用Scan命令找到key之後執行刪除操作(2.8.0版本之後)
  3. 添加緩存數據的時候,可以同時將key存放到一個SET中,然後依據這個SET來執行刪除操作

對於Keys命令,網上有不少血的教訓,對於生產環境還是要謹慎謹慎再謹慎!能不用就別用。

Scan命令的話是大部分人推薦的做法,是增量式叠代的一個命令。

存到SET中就相對繁瑣一點,而且額外占用了一部分內存。而且在進行刪除的時候還要從這裏讀取出相應的key,同時也要移除這部分key的數據。

下面來看看如何在.NET Core中來處理,主要還是針對SCAN的做法。

示例操作Redis用的是StackExchange.Redis

使用IServer.Keys

可能有人會有疑惑,不是說Keys命令盡量不要用嗎?怎麽你還用?

這個還真的要解釋一下!

可能從方法上,我們找遍所有IServer和IDataBase接口都找不到純粹的SCAN命令(SetScan,HashScan等除外)。

但是如果看過裏面的實現,你就會知道是為什麽了!

傳送門:Keys

可以看看下圖高亮的兩行代碼:

技術分享圖片

大致意思就是,如果你用的Redis的版本支持SCAN命令,走的就是SCAN,反之只能是KEYS了。

下面定義一個查找RedisKey的方法。

private static RedisKey[] SearchRedisKeys(IServer server, string pattern)
{
    var keys = server.Keys(pattern: pattern).ToArray();
    Console.WriteLine("Search Count-{0}",keys.Length);
    return keys;
}

知道那些Key要刪除,剩下的就比較簡單了!

private static void KeysOrScanSolution(IServer server,IDatabase db, string pattern)
{            
    db.KeyDelete(SearchRedisKeys(server, pattern));                        
}

使用IDatabase.Execute

IServer.Keys可以說是隱式的調用了SCAN命令,那麽我們自然也可以顯式的去調用這個命令來完成這些。

private static RedisKey[] SearchRedisKeys(IDatabase db,string pattern)
{
    var keys = new HashSet<RedisKey>();

    int nextCursor = 0;
    do
    {
        RedisResult redisResult = db.Execute("SCAN", nextCursor.ToString(), "MATCH", pattern, "COUNT", "1000");
        var innerResult = (RedisResult[])redisResult;

        nextCursor = int.Parse((string)innerResult[0]);

        List<RedisKey> resultLines = ((RedisKey[])innerResult[1]).ToList();

        keys.UnionWith(resultLines);
    }
    while (nextCursor != 0);

    return keys.ToArray();
}

刪除的代碼。

private static void ExecuteSolution(IDatabase db, string pattern)
{
    db.KeyDelete(SearchRedisKeys(db, pattern));
}

當然還有一種做法是調用lua腳本去完成,這裏就不細說了。

總結

雖然上面幾種做法能比較簡單的處理這個問題,但是在拿出這些Keys的時候,客戶端的內存占用可能會比較大,尤其是有大量符合條件的緩存項的時候。

涉及緩存的諸多操作(包含根據前綴去刪除緩存項),我也在EasyCaching中實現了相應的操作,後面也會不斷的抽時間來完善這一項目,有興趣的朋友可以關註一下。

文中的示例代碼 RedisBatchRemoveSolution

談談使用Redis緩存時批量刪除的幾種實現