redisクラスタ内のhot Keyの処理方法

4053 ワード

一般に、hot keyの問題は、単一のインスタンスのredisまたは1つのプライマリ・スペアのredisにとって考慮する必要はない.しかし,トラフィックの増加に伴いredisクラスタも自然に選択肢となる.

hot keyの概念


redisクラスタをキャッシュとして使用する場合、ビジネス上で大きな促進に遭遇したり、非常に熱い投稿があったりすると、対応するキャッシュが頻繁にアクセスされます.このキャッシュはredisクラスタの同じクラスタに落ち、データが頻繁に同じマシンにアクセスし、クラスタ性能の不均衡をもたらす.これがいわゆるhot keyの問題です.

hot keyの解決策


もちろん、同じキーに頻繁にアクセスする必要がありますが、このキーを同じマシンに直接落下させたくない場合は、他のマシンでこのキーのバックアップをコピーし、リクエストを入れるときにランダムに各マシンにキャッシュにアクセスする必要があります.残りの問題は,要求を各機器に平均的に分散させる方法である.
簡単に、N台のマシンがあると仮定し、リクエストが来たとき、私たちはランダムに1からNの間からランダムに1台のマシンを選択してアクセスします.どのように選択しますか?redisクラスタではkeyに基づいて対応しているため,keyからkeyに変化するランダム値を接尾辞として生成することができる.suffix.
また、同じ接尾辞がマッピングされた後も一部の機器に集中することを防止するためには、クラスタ数Nの2倍など、ランダム値の上限を拡大する必要があるのが一般的である.
そのため、最も簡単なバージョンがあります.
const M = N * 2
random = GenRandom(0, M)
bakHotKey = hotKey + “_” + random
data = redis.GET(bakHotKey)
if data == NULL {
    data = GetFromDB()
    redis.SET(bakHotKey, expireTime)
}

期限切れによる雪だるま効果の解決


最初のバージョンがありましたが、生産環境でこのように使うのは問題があるに違いありません.hot keyである以上,短時間で必然的に大量のアクセス要求があり,上のコードはredisクラスタ内の各マシンに同じ期限切れが設定されている.redisクラスタ内のキャッシュセットが期限切れになると、必ずDBに圧力が移行します.このような雪崩効果を防止するためには,各機器の有効期限をできるだけ異なるように設定する必要がある.したがって、期限切れにランダム値を追加することができ、2番目のバージョンがあります.
const M = N * 2
random = GenRandom(0, M)
bakHotKey = hotKey + “_” + random
data = redis.GET(bakHotKey)
if data == NULL {
    data = GetFromDB()
    redis.SET(bakHotKey, expireTime + GenRandom(0,5))
}

さらに性能を向上させるために、クラスタ内のマシンにこのkeyのバックアップがある以上、なぜ各マシンにbakHotKeyが存在しない場合にDBにアクセスするのか、redis自身の内部で完全に解決できる.したがって、元のhotKeyをキャッシュに保存することができ、すべてのbakHotKeyが失効した場合、hotKeyからデータを同期することができます.hotKeyも無効になった場合にのみ、DBにデータにアクセスします.これにより、前後して何台ものマシンのキャッシュが失効しても、1台のマシンだけが本当にDBに行ってデータにアクセスし、他のredisはその後のhotKeyのキャッシュからデータをバックアップします.
const M = N * 2
random = GenRandom(0, M)
bakHotKey = hotKey + “_” + random
//      bakHotKey
data = redis.GET(bakHotKey)
if data == NULL {
    // bakHotKey  ,    hotKey
    data = redis.GET(hotKey)
    if data == NULL {
        // hotKey   , DB  
        data = GetFromDB()
        //      
        redis.SET(hotKey, data, expireTime)
        //       
        redis.SET(bakHotKey, data, expireTime + GenRandom(0,5))
    } else {
        //          bakHotKey,    DB
         redis.SET(bakHotKey, data, expireTime + GenRandom(0,5))
    }
}

hotKeyの検出


Redis 4.0には、LFU(Least Frequently Used)というメモリ逐出ポリシーが追加されました.LFUは最もよく使われていないことを示しています.LRUに比べて、LRUはあまり使わないkeyに対して、偶然使ったら、このkeyをアクティブにしたように、短時間で削除しません.しかしLFUはkeyの全体的な使用頻度を統計し,使用頻度が最も小さいkeyを削除する.
redis 4.0.3はredis-cliのホットスポットkey発見機能を提供し、redis-cliを実行する際に–hotkeysオプションを追加すればよい.例は以下の通りである.
$./redis-cli --hotkeys

# Scanning the entire keyspace to find hot keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Hot key 'counter:000000000002' found so far with counter 87
[00.00%] Hot key 'key:000000000001' found so far with counter 254
[00.00%] Hot key 'mylist' found so far with counter 107
[00.00%] Hot key 'key:000000000000' found so far with counter 254
[45.45%] Hot key 'counter:000000000001' found so far with counter 87
[45.45%] Hot key 'key:000000000002' found so far with counter 254
[45.45%] Hot key 'myset' found so far with counter 64
[45.45%] Hot key 'counter:000000000000' found so far with counter 93

-------- summary -------

Sampled 22 keys in the keyspace!
hot key found with counter: 254    keyname: key:000000000001
hot key found with counter: 254    keyname: key:000000000000
hot key found with counter: 254    keyname: key:000000000002
hot key found with counter: 107    keyname: mylist
hot key found with counter: 93    keyname: counter:000000000000
hot key found with counter: 87    keyname: counter:000000000002
hot key found with counter: 87    keyname: counter:000000000001
hot key found with counter: 64    keyname: myset

LFUの実現については『Redis 4.0のLFUに基づくホットスポットkey発見メカニズム』を参照