redis 突然大量逐出導致讀寫請求block


現象

redis作為緩存場景使用,內存耗盡時,突然出現大量的逐出,在這個逐出的過程中阻塞正常的讀寫請求,導致 redis 短時間不可用;

背景

redis 中的LRU是如何實現的?

  1. 當mem_used內存已經超過maxmemory的設定,對於所有的讀寫請求,都會觸發redis.c/freeMemoryIfNeeded(void)函數以清理超出的內存。
  2. 這個清理過程是阻塞的,直到清理出足夠的內存空間。
  3. 這里的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 就會很大,造成一個時刻需要逐出大量的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 給的方案:同樣是迭代刪除,但會加個標志,保證在迭代刪除的邏輯下內存是逐漸下降的,而如果是上升的,還是會block住正常的請求(要控制主總的內存大小);
詳見:
https://github.com/antirez/redis/pull/4583

ref

關於 redis 4.0的逐出算法優化
http://antirez.com/news/109


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM