Table of Contents
因为小规模benchmark时文件都被cache, IO访问其实只有内存操作而已, 所以测试数据只能说明系统在基本无IO操作时的处理能力.
为了避免文件被cache, 可以减少机器空闲内存, 或者使操作的数据集远大于内存, 我们的测试机内存64G, 所以测试时, 我们使用大约100G的数据集来进行测试.
benchmark场景:
先写, 后读, 采集的数据包括:
为此, 写了这样一个程序用于benchmark和记录结果:
... class LoadThread(threading.Thread): def run(self): global g_qps num = 1000000000 #num = 100000 cmd = 'redis-benchmark -p 8888 -t set -n %s -r 100000000000 -d 100' % num p = Popen(cmd, shell=True, stdout=PIPE, bufsize=1024) for line in iter(lambda: p.stdout.readline(), ''): line = str(line).strip() #print(">>> " + line) if line.startswith('SET'): g_qps = line.split()[1] cmd = 'redis-benchmark -p 8888 -t get -n %s -r 100000000000 -d 100' % num p = Popen(cmd, shell=True, stdout=PIPE, bufsize=1024) for line in iter(lambda: p.stdout.readline(), ''): line = str(line).strip() #print(">>> " + line) if line.startswith('GET'): g_qps = line.split()[1] ...
代码在此: https://github.com/idning/iostat-py/blob/master/ssdb-bench/ssdb-bench.py
这个测试是手工完成和记录的, 没有图.
写:
$ redis-benchmark -p 8888 -t set -n 1000000000 -r 100000000000 -d 100 38000
持续写1000000000条, (93G)
磁盘写带宽持续70M/s左右, 内存使用会上升到10G左右, 低峰会回落, 12核cpu上, cpu占用约30%(4个核占满)
qps稳定在3.8w/s, 不会随着写数据增多而变差.
写完之后, 读:
$ redis-benchmark -p 8888 -t get -n 1000000000 -r 100000000000 -d 100 60~400
如果能命中热点:
$ redis-benchmark -p 8888 -t get -n 1000000000 -r 100000 -d 100 23803.46
初始qps只能达到60/s, 逐渐上升到400/s趋于稳定.
此时磁盘每秒读请求达到150-300r/s (达到磁盘IOPS极限):
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util sda 0.00 0.00 137.00 0.00 12940.00 0.00 188.91 1.08 7.86 6.86 94.00
小结:
ssdb在hdd上的表现:
hdd上, ssdb适合写多读少的场景.
环境:
/dev/sdb1 on /ssd type ext4 (rw,noatime) mem: 48G cpu: 12 ssd: Intel SSD 530 480GB, 2.5in SATA 参数: http://ark.intel.com/products/75336/Intel-SSD-530-Series-480GB-2_5in-SATA-6Gbs-20nm-MLC
这块SSD 的性能参数:
我们实际上是3块ssd做RAID0, fio测试结果:
$ sudo fio -filename=/dev/sdb -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=1k -size=200G -numjobs=30 -runtime=1000 -group_repor 5.7w/s $rw=randread 7.3w/s
ssdb测试结果:
小结
LevelDB只有block级别的cache, 所有Key集合是记录在磁盘上, 内存中并没有一个记录key是否存在的hash表或树结构, 所以每次查询, 不管key是否存在, LevelDB都需要到磁盘上去找, 如果block不在缓存中, 就要一层层去找, 是非常耗时的,
为此, LevelDB增加了bloomfilter支持, 可以过滤掉一些key不存在的情况, 减少对磁盘的访问:
ssdb->options.filter_policy = leveldb::NewBloomFilterPolicy(10); ssdb->options.block_cache = leveldb::NewLRUCache(cache_size * 1048576);
benchmark显示 100G数据 时, 读性能稳定在大约5000 qps
原因:
修改:
static Command commands[] = { - PROC(get, "r"), + PROC(get, "rt"),
把读放到多线程里面去做, 性能从5000提到15000 , 磁盘r/s 达到23000左右, 日志级别改为error后可以达到16000/s
读没有用Transaction加锁, 所以这时候已经能同时向IO系统发多个IO请求了:
int SSDB::get(const Bytes &key, std::string *val) const{ std::string buf = encode_kv_key(key); leveldb::Status s = db->Get(leveldb::ReadOptions(), buf, val); ... return 1; }
调整 READER_THREADS = 10 为5, 20, 50, 发现在我的机器上10貌似是个最佳值,
coding for ssd 系列 关于多线程read的观点:
http://codecapsule.com/2014/02/12/coding-for-ssds-part-5-access-patterns-and-system-optimizations/