redisが突然大量に追放され、読み書き要求blockが発生した

2368 ワード

げんしょう
redisはキャッシュシーンとして使用され、メモリが消費されると、突然大量の追放が発生し、この追放中に正常な読み書き要求がブロックされ、redisが短時間で使用できない.
背景
redisのLRUはどのように実現されますか?
  • 当mem_usedメモリはmaxmemoryの設定を超えており、すべての読み書き要求に対してredisがトリガーされる.c/freeMemoryIfNeeded(void)関数は、メモリをクリーンアップします.
  • このクリーンアッププロセスは、十分なメモリ領域がクリーンアップされるまでブロックされます.
  • ここでのLRUまたはTTLポリシーは、redisのすべてのkeyではなく、プロファイル内のmaxmemory-samples個のkeyをサンプルプールとしてサンプリングクリーンアップします.maxmemory-samplesのredis-3.0.0でのデフォルト構成は5であり、増加するとLRUまたはTTLの精度が向上し、redis著者らのテストの結果、この構成が10の場合、完全なLRUの精度に非常に近い.

  • の原因となる
    逐出qpsの急増が非常に大きい原因:一度に逐出して多くの空間を解放する必要があると閉塞を招く.具体的な原因はmem_tofreeの計算ロジックに問題があります.mem_tofreeは、実際に割り当てられたメモリの総量-AOFバッファに関連するメモリを統計します.このときrehashがあると、一時的にバケツを割り当ててrehashを作ります.この部分のメモリは除外されていませんので、rehashフェーズで計算されたmem_tofreeは大きくなり、1つの時点で大量のkeyを追放する必要があり、追放されたloopはブロックされ、この段階でblock redisの要求があります.
    逐出qpsの計算:
    freeMemoryIfNeeded(...)
        //     Redis          ,               :
        // 1)             
        // 2)AOF       
        // 3)AOF          
        mem_used = zmalloc_used_memory();
        if (slaves) {
            listIter li;
            listNode *ln;
    
            listRewind(server.slaves,&li);
            while((ln = listNext(&li))) {
                redisClient *slave = listNodeValue(ln);
                unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave);
                if (obuf_bytes > mem_used)
                    mem_used = 0;
                else
                    mem_used -= obuf_bytes;
            }
        }
        if (server.aof_state != REDIS_AOF_OFF) {
            mem_used -= sdslen(server.aof_buf);
            mem_used -= aofRewriteBufferSize();
        }
        //              
        mem_tofree = mem_used - server.maxmemory;
        propagateExpire(db,keyobj);
        //              
        delta = (long long) zmalloc_used_memory();
        dbDelete(db,keyobj);
        delta -= (long long) zmalloc_used_memory();
        mem_freed += delta;
        //           
        server.stat_evictedkeys++;

    ソリューション
    github上@Rosantaが提供するソリューション:メモリを解放するサイクルロジックで最大一定回数実行し、しきい値に達したら追い出さず、次のリクエストが来たときにもう少しスペースを解放します.この案の利点はblock全体のプロセスができず、正常な業務の読み書き要求に影響がないことです.潜在的な問題は、単一書き込みのデータが解放された空間よりも大きく、全体のメモリが低下するのではなく、上昇し続ける可能性があることです.
    @antirezが与えたシナリオ:同じ反復削除ですが、反復削除の論理の下でメモリが徐々に低下することを保証するフラグが付いています.上昇している場合は、正常な要求をブロックします(メインのメモリサイズをコントロールします).詳細:https://github.com/antirez/redis/pull/4583
    ref
    redis 4.0の逐出アルゴリズム最適化についてhttp://antirez.com/news/109