memtier_benchmarkを使うとredisやmemcachedのベンチマークを取得できるらしく使ってみました。
dockerを使ってさくっとやってみます。-t
でスレッド数-c
でコネクション数-n
でそれぞれのコネクションでどの程度のリクエストを投げるかを調整できます。
docker run --rm redislabs/memtier_benchmark:latest -s ${SERVER_IP} -p 6379 -a test12345 -t 4 -c 30 -n 10000 [RUN #1] Preparing benchmark client... [RUN #1] Launching threads now... [RUN #1 100%, 285 secs] 0 threads: 1200000 ops, 5476 (avg: 4208) ops/sec, 233.43KB/sec (avg: 178.51KB/sec), 22.04 (avg: 1746.48) msec latencyncy 4 Threads 30 Connections per thread 10000 Requests per client ALL STATS ========================================================================= Type Ops/sec Hits/sec Misses/sec Latency KB/sec ------------------------------------------------------------------------- Sets 383.16 --- --- 1955.85700 29.51 Gets 3827.39 0.00 3827.39 1725.52400 149.09 Waits 0.00 --- --- 0.00000 --- Totals 4210.55 0.00 3827.39 1746.48400 178.60
見るべきなのはOps/SecでしょうかGetとSetがそれぞれどの程度でているかを確認できます。
pmapをみてみる
特に面白いものは見れなかった。arm特有の何かしらを期待したけど特にはなさそう。anonになってるあたりが恐らくredisのデータ領域でしょうか。read onlyなanonの用途はさっぱりわからず。
Address Kbytes RSS Dirty Mode Mapping 0000000000400000 1276 672 0 r-x-- redis-server 000000000073f000 24 24 24 rw--- redis-server 0000000000745000 92 92 92 rw--- [ anon ] 00000000011a3000 132 56 56 rw--- [ anon ] 00000039fa800000 128 40 0 r-x-- ld-2.12.so 00000039faa20000 4 4 4 r---- ld-2.12.so 00000039faa21000 4 4 4 rw--- ld-2.12.so 00000039faa22000 4 4 4 rw--- [ anon ] 00000039fac00000 8 4 0 r-x-- libdl-2.12.so 00000039fac02000 2048 0 0 ----- libdl-2.12.so 00000039fae02000 4 4 4 r---- libdl-2.12.so 00000039fae03000 4 4 4 rw--- libdl-2.12.so 00000039fb000000 1576 508 0 r-x-- libc-2.12.so 00000039fb18a000 2048 0 0 ----- libc-2.12.so 00000039fb38a000 16 16 8 r---- libc-2.12.so 00000039fb38e000 8 8 8 rw--- libc-2.12.so 00000039fb390000 16 12 12 rw--- [ anon ] 00000039fb400000 92 72 0 r-x-- libpthread-2.12.so 00000039fb417000 2048 0 0 ----- libpthread-2.12.so 00000039fb617000 4 4 4 r---- libpthread-2.12.so 00000039fb618000 4 4 4 rw--- libpthread-2.12.so 00000039fb619000 16 4 4 rw--- [ anon ] 00000039fc000000 524 4 0 r-x-- libm-2.12.so 00000039fc083000 2044 0 0 ----- libm-2.12.so 00000039fc282000 4 4 4 r---- libm-2.12.so 00000039fc283000 4 4 4 rw--- libm-2.12.so 00007fa14b600000 370688 331656 331656 rw--- [ anon ] 00007fa1621fd000 4 0 0 ----- [ anon ] 00007fa1621fe000 10240 8 8 rw--- [ anon ] 00007fa162bfe000 4 0 0 ----- [ anon ] 00007fa162bff000 10240 8 8 rw--- [ anon ] 00007fa1635ff000 4 0 0 ----- [ anon ] 00007fa163600000 10240 2048 2048 rw--- [ anon ] 00007fa164000000 2048 2048 2048 rw--- [ anon ] 00007fa16436b000 96852 0 0 r---- locale-archive 00007fa16a200000 2048 2048 2048 rw--- [ anon ] 00007fa16a5f9000 16 16 16 rw--- [ anon ] 00007fa16a60f000 4 4 4 rw--- [ anon ] 00007fff55af5000 84 20 20 rw--- [ stack ] 00007fff55b8f000 4 4 0 r-x-- [ anon ] ffffffffff600000 4 0 0 r-x-- [ anon ]
armだけではないけどlibpthread.soを読んでるのは若干不思議だったけどredisはシングルスレッド動作というのはメモリの管理のみでバックグラウンドで行うAOFとかメモリのevictは別スレッドでやるらしい。6.0以外でもスレッドが複数いるのはこれが原因。AOF書きながらクライアント応答もすると思いったけどそんなわけはなかった。(計算量の多い処理があると処理がブロックされるのは変わらないので注意が必要)
エラーはどういう時に出るのか
公式FAQにあったが基本的には性能低下はネットワークが原因になることが多いらしい。次にメモリでメモリの使用率が高くなると書き込みたいメモリのサイズの探索が走ったりswapが走ったりするので遅くなる。
redis-documentasion-japanese.readthedocs.io
evictするのも良さそうだけどevictに追いつくくらいのメモリ使用率とかだとそれはそれで性能は低下しそう(未検証)
メモリの使用率とかを見てみる
infoとかで見れるやつ。この辺は何が違うのかを理解して見ていく必要があると感じた。
used_memory
Redisがアロケータ (標準libc, jemalloc あるいは tcmallocなどの代替のアロケータ)を使用して割り当てた総バイト数
実装はこの辺
// この辺数をredisはatomicにメモリサイズとして使っている static redisAtomic size_t used_memory = 0; // updateはこの関数 #define update_zmalloc_stat_alloc(__n) atomicIncr(used_memory,(__n)) #define update_zmalloc_stat_free(__n) atomicDecr(used_memory,(__n)) // この関数を読んで取得 size_t zmalloc_used_memory(void) { size_t um; atomicGet(used_memory,um); return um; }
used_memory_rss
オペレーティングシステムから見たRedisが割り当てたバイト数 (別名 常駐セットサイズ)。これは top(1) と ps(1)のようなツールによって報告された数です
psなどで出力されるrssの値なのでバイナリ自身や共有ライブラリもこの値に含まれる。
実装はこの辺。procfsをそのまま除くという感じだった。_SC_PAGESIZEとかやってるけど特殊環境で走らせるとこの辺の数値が4kを返さなかったりするからバグりそう。
size_t zmalloc_get_rss(void) { int page = sysconf(_SC_PAGESIZE); size_t rss; int fd, count; char *p, *x; snprintf(filename,256,"/proc/%ld/stat",(long) getpid()); if ((fd = open(filename,O_RDONLY)) == -1) return 0; if (read(fd,buf,4096) <= 0) { close(fd); return 0; } return rss; }
以下はそれぞれの関係式をまとめてみた。
used_memory_rss > used_memory
関係が一番良い動作。バイナリとかライブラリ以外は全てredisのメモリアロケーション配下になっている
used_memory > used_memory_rss
場合はswapしている領域をredisが使っている可能性がある。rssにはswap領域は加わらない
used_memory_rss >>> used_memorn
redisが管理しているメモリよりもOS的にはもっと使っているように見えている(メモリの断片化)。
used_memory_peak
Redisによって消費されたピークメモリ (バイト単位)
used_memoryの過去の最大値。rssの最大値ではないので注意
実装はredisServerというバカでかい構造体で保持している。仕事以外では読みたくないぐらいでかいw
mem_fragmentation_ratio
used_memory_rssとused_memory間の比率