Redisのメモリ使用量(setについて、valueがstringとint、そしてttlを付加した場合)について

Redisに対してストアするとどのぐらいメモリを使うのか調べました。表題のとおりです。

環境
– Mac OSX 10.8
– Redis 2.6.16

その1 – setでvalueがstring

方法
ある長さのkeyとある長さのvalueを、ある回数だけsetして、そのときのメモリ使用量を調査しました。

結果

1000 keys (key-byte: 24, value-byte: 24)
Expectation : 46.9 [KB]
Redis Memory: 148.6 [KB]
(Expectationとは、素直に (keyとvalueの長さ) * 回数、を計算したものです)

結論
単純なsetですと、おおよそrawのbyte数の5倍程度使うようです。

String版
<?php

define("MAX", 1000);
define("REDIS_KEY_BYTE", 24);
define("REDIS_VALUE_BYTE", 24);

$redis = new Redis();
$redis->connect("localhost", 6379);

$redis->flushAll();

$before = getRedisMemory($redis);
for ($i = 0; $i < MAX; $i++) {
	$token = openssl_random_pseudo_bytes(REDIS_KEY_BYTE);
	$redis->set($token, openssl_random_pseudo_bytes(REDIS_VALUE_BYTE));
}
$after = getRedisMemory($redis);

printf("%d keys (key-byte: %d, value-byte: %d)\n", MAX, REDIS_KEY_BYTE, REDIS_VALUE_BYTE);
printf("Expectation : %.1f [KB]\n", MAX * (REDIS_KEY_BYTE + REDIS_VALUE_BYTE) / 1024.0);
printf("Redis Memory: %.1f [KB]\n", ($after - $before) / 1024.0);

function getRedisMemory(Redis $redis) {
	$res = $redis->info('MEMORY');
	return $res['used_memory'];
}


その2 – setでvalueがint

先程はvalueに24-byteのランダムな文字列を使いましたが、今度は21億ちょっとまでの整数値をvalueとしました。

結果
1000 keys (key-byte: 24, value-byte(int): 4)
Expectation : 27.3 [KB]
Redis Memory: 101.8 [KB]

結論
32bit Integer (signed)のつもりです。おおよそ2/3ぐらいに収まるかなと予想しましたがその通りの結果となりました。

考察
cookieのsessionを保存することを考えてみます。sessionの24byteとして、sessionにはサーバー側でuserid(32bit)が紐付けられているとします(ちょうどこの実測例です)。

さて、1GBのDRAMで何個のtokenを保存できるのでしょうか?

↑の結果より、1000個で約100KBですので、1万個で1MBです。その1000倍ですので、1GBで1000万個、と言えそうです。

今時であれば、サーバーに32GBや64GBのDRAMを詰むのは当たり前のことだと思います。ですのでRedisで16GB使うと考えると、1.6億個のtokenを管理できるのでかなり余裕がありますね。32GBマシン1個でtokenの処理は十分にこなせそうです。もちろん冗長化(redis-sentinelやslaveなど)は必要ですが。もちろん、メモリだけでなくネットワーク帯域などの問題もありますが。

scale-outではなくscale-upで対応できそうですね。

ちなみに、Redisのバックアッププロセスでのメモリ使用量を考えると、搭載しているDRAMの半分程度の運用が安全でしょう。その他のプロセスもメモリを使うので、もっと言えば1/3程度ですかね。

Integer版
<?php

define("MAX", 1000);
define("REDIS_KEY_BYTE", 24);
define("REDIS_VALUE_BYTE", 4); // 32bit Integer = 4-byte

$redis = new Redis();
$redis->connect("localhost", 6379);

$redis->flushAll();

$before = getRedisMemory($redis);
for ($i = 0; $i < MAX; $i++) {
	$token = openssl_random_pseudo_bytes(REDIS_KEY_BYTE);
	$redis->set($token, mt_rand(1, 2147483647)); // max signed_int = 2147483647
}
$after = getRedisMemory($redis);

printf("%d keys (key-byte: %d, value-byte(int): %d)\n", MAX, REDIS_KEY_BYTE, REDIS_VALUE_BYTE);
printf("Expectation : %.1f [KB]\n", MAX * (REDIS_KEY_BYTE + REDIS_VALUE_BYTE) / 1024.0);
printf("Redis Memory: %.1f [KB]\n", ($after - $before) / 1024.0);

function getRedisMemory(Redis $redis) {
	$res = $redis->info('MEMORY');
	return $res['used_memory'];
}


その3 – setでvalueがstringでttl付

さらにttlを付けて計測してみました。tokenの期限を半年と設定すると仮定してみます。半年、という時間は秒に直すと、60 [sec/min] * 60 [min/hour] * 24 [hour/day] * 365 / 2 = 15768000 [sec] ですね。

結果
1000 keys (key-byte: 24, value-byte(int): 4)
Expectation : 27.3 [KB]
Redis Memory: 141.0 [KB]

結論
先ほどの1.5倍程度となりました。とは言え1台のマシンで1つのシステムのtokenは何とかなりそうな感じですが。

TTL版
<?php

define("MAX", 1000);
define("REDIS_KEY_BYTE", 24);
define("REDIS_VALUE_BYTE", 4); // 32bit Integer = 4-byte
define("TTL", 15768000);

$redis = new Redis();
$redis->connect("localhost", 6379);

$redis->flushAll();

$before = getRedisMemory($redis);
for ($i = 0; $i < MAX; $i++) {
	$token = openssl_random_pseudo_bytes(REDIS_KEY_BYTE);
	$redis->setex($token, TTL, mt_rand(1, 2147483647)); // max signed_int = 2147483647
}
$after = getRedisMemory($redis);

printf("%d keys (key-byte: %d, value-byte(int): %d)\n", MAX, REDIS_KEY_BYTE, REDIS_VALUE_BYTE);
printf("Expectation : %.1f [KB]\n", MAX * (REDIS_KEY_BYTE + REDIS_VALUE_BYTE) / 1024.0);
printf("Redis Memory: %.1f [KB]\n", ($after - $before) / 1024.0);

function getRedisMemory(Redis $redis) {
	$res = $redis->info('MEMORY');
	return $res['used_memory'];
}

Bookmark the permalink.

Comments are closed.