2015年5月22日 星期五

[NodeJs] Solution of Redis runnig out of memory when using Kue (使用 Kue 時 Redis 記憶體空間不足解法)



將碰到的情況整理分享,下次碰到可以少花些時間到處爬。



與 Redis 相關的環境

1. Sails Session 存放在 Redis。
2. Kue (處理 job queue 的 node module) 使用 Redis。



事由

這次正式機發生 Redis 的記憶體空間不足,
導致使用者無法登入,因為 Session 無法存入 Redis。

錯誤訊息
ERR command not allowed when used memory > 'maxmemory'



原因

1. maxmemory-policy 為預設值 volatile-lru (詳見附錄),若記憶體不足,
    只會回收有設有效期限的資料。

2. Kue 在丟 q:job 資料進 Redis 時並沒有設 Expire Set,我們也沒有程式或手動清除,
    導致狀態為 complete 或 failed 的 q:job 和 q:search 的資料無限增加佔住記憶體。



解決方式

Redis

增加記憶體空間不足的 Error Handling,參考 連結 以及 官方 FAQ

What happens if Redis runs out of memory?

Redis will either be killed by the Linux kernel OOM killer, crash with an error, or will start to slow down. With modern operating systems malloc() returning NULL is not common, usually the server will start swapping and Redis performances will degrade so you'll probably notice there is something wrong. The INFO command will report the amount of memory Redis is using so you can write scripts that monitor your Redis servers checking for critical conditions. Redis has built-in protections allowing the user to set a max limit to memory usage, using the maxmemory option in the config file to put a limit to the memory Redis can use. If this limit is reached Redis will start to reply with an error to write commands (but will continue to accept read-only commands), or you can configure it to evict keys when the max memory limit is reached in the case you are using Redis for caching.



Kue

將狀態為 complete 和 failed 的 q:job 清除,可用方法如下:

1. kue-sweeper



2. removeOnComplete(true)
queue.create( ... ).removeOnComplete( true ).save()
在 0.8.4 版本之後才有實作,而我們使用的為 0.7.9,參考 連結一連結二 以及 連結三



3. 在每一個 job 建立之後收到該 job complete 事件就將該 job 資料移除,參考 連結
jobs.on('job complete', function(id) {
  kue.Job.get(id, function(err, job) {
    setTimeout(function() {
      job.remove();
    }, 60000);
  });
});



4. 定時移除已完成的 jobs,參考 連結
var CLEANUP_TIME = 0.5 * 60 * 1000;
var CLEANUP_INTERVAL = 0.1 * 60 * 1000;

function cleanUpCompleteJobs() {
  var now = new Date().getTime();
  Job.rangeByStatus('complete', 0, -1, 'asc', function (err, selectedJobs) {
    selectedJobs.forEach(function (job) {
      var created = job.created_at;
      if (now - created > CLEANUP_TIME) {
        job.remove();
      }
    });
  });
}

setInterval(cleanUpCompleteJobs, CLEANUP_INTERVAL);



5. 在建立 job 時,帶入 expires 參數,並 setExpiration(),參考 連結



暫時使用第四個方式並調整 Redis 記憶體不足空間回收機制 (maxmemory-policy),
測試新版本若銜接沒問題則升版使用第二個方式解決。

另外加上 Error Handling,
var queue = require('kue').createQueue();

queue.on('error', function( err ) {
  console.log( 'Oops... ', err );
});
參考 連結



附錄

1. 看目前 Redis 的記憶體使用狀況
redis-cli info memory
也可以只下
redis-cli info
得到更多資訊。


較重要的為

used_memory:Redis 實際上使用掉的記憶體大小。
used_memory_rss:作業系統覺得 Redis 使用掉的記憶體大小。
mem_fragmentation_ratio:used_memory_rss 除以 used_memory。

中間的差距是存放在 Redis 的資料清掉之後留下的碎片記憶體空間。
至於記憶體空間的分配、使用方式視使用的 mem_allocator 而定,
最佳化方式可以參考 Memory-Optimization



2. Redis 的 maxmemory 設定
看目前設定
redis-cli config get maxmemory
更改設定
redis-cli config set maxmemory 1234567



3. Redis 的 maxmemory-policy 設定
看目前設定
redis-cli config get maxmemory-policy
更改設定
redis-cli config set maxmemory-policy volatile-lru

maxmemory-policy 為 Redis 記憶體空間不足時,需要空間時的回收機制,分別有
noeviction:純粹回傳錯誤,不回收。
allkeys-lru:從最後一次使用時間離現在最久 (Less Recently Used) 的資料空間開始回收。
volatile-lru:同上,但只回收有設定有效期限 (Expire Set) 的資料,此為預設值。
allkeys-random:隨機回收。
volatile-random:同上,但只回收有設定有效期限的資料。
volatile-ttl:在有設定有效期限的資料中,先回收距有效期限較近 (Time To Live) 的資料。

參考 Using Redis as an LRU cache



參考

https://github.com/Automattic/kue/issues/58
http://stackoverflow.com/a/10008222/1979454



冷知識

Redis port number 竟然是 MERZ 的手機按法來的!
http://oldblog.antirez.com/post/redis-as-LRU-cache.html



沒有留言:

張貼留言