Redis 性能测试记录

虽然 Redis 本身也提供redis-benchmark工具来对 Redis 的性能进行测试,但为了对测试维度自由定制,我们还是通过自己编写脚本的方式来测试。

Redis 的官方文档也提到了,简单的起一个循环,然后在循环中向 Redis 发送操作命令,其实不是对 Redis 进行性能测试,而是对网络延迟进行测试。为了真正测试 Redis 的并发性能,需要使用多个 Redis 连接,或者使用 pipelining 来聚合多个命令。当然也可以使用多线程或者多进程。
我们使用 Python 脚本来作为 Redis 的测试工具,并采用每个进程一个 Redis 连接的方案。

测试脚本如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import time
import datetime
from multiprocessing import Process
import redis


def write_worker(n):
print 'in write worker: %d, time: %s' % (n, datetime.datetime.now())
r = redis.StrictRedis(host='10.88.115.114', port=6380)
s = 'a' * 1024 * 1024 * 10
t1 = time.time()
bigkey = 'bigkey%d' % (n + 100)
r.set(bigkey, s)
t2 = time.time()
print 'write time: %f' % (t2 - t1)


def read_worker(n):
print 'in read worker: %d, time: %s' % (n, datetime.datetime.now())
r = redis.StrictRedis(host='10.88.115.114', port=6380)
t1 = time.time()
bigkey = 'bigkey%d' % n
r.get(bigkey)
t2 = time.time()
print 'read time: %f' % (t2 - t1)


def test():
read_processes = []
for i in range(0, 100):
read = Process(target=read_worker, args=(i,))
read.start()
read_processes.append(read)

write_processes = []
for i in range(0, 100):
write = Process(target=write_worker, args=(i,))
write.start()
write_processes.append(write)

for write in write_processes:
write.join()
for read in read_processes:
read.join()
print 'end press test'


if __name__ == '__main__':
test()

redis-py 的StrictRedis已提供了连接池的功能的,可以在多线程中直接使用,这里我们使用了多进程的方式,并采用每个进程都使用独立的一个StrictRedis实例。
在上面的代码中,我们起了大量的读写进程。对 Redis 的写入操作中每次发送一个SET的请求,写入的数据大小为 100 MB,读操作则每次读取GET 100 MB的数据(需要提前准备GET请求的 key 的数据)。测试程序的目的是为了测试 Redis 在处理较高并发时,大数据读写的性能。

测试结果在这里就不贴不出来,有兴趣的读者也可以运行一下这个 Python 脚本,就可以得到测试结果了。从测试结果来看,Redis 的性能还是很优秀的。

这里重点提一下测试过程中 Redis 服务会报错的问题。
在写入大量的数据(100个10MB,即1GB)后,Redis 进程开始出现以下日志:

[2418] 21 Apr 09:38:24.041 * 10 changes in 300 seconds. Saving…
[2418] 21 Apr 09:38:24.042 # Can’t save in background: fork: Cannot allocate memory

客户端执行写入命令会报错:

127.0.0.1:6380> set key value
(error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

从网上查找 Redis 的资料可以知道,这是由于 Redis 开启了 RDB 功能所致。RDB 用于保存 Redis 的数据快照,将数据快照数据持久化到硬盘中。以下是 Redis 的 RDB 默认配置:

1
2
3
save 900 1    
save 300 10
save 60 10000

其含义是900秒内有1次修改,300秒内有10次修改,60秒内有10000秒修改,都会将照数据持久化到磁盘中。

要解决 Redis 报错的问题,可以把 RDB 功能关闭,或者通过优化 Linux 内存配置优化解决。具体可以参考《LINUX下REDIS内存优化》

1
sysctl vm.overcommit_memory=1

以 root 权限执行此命令后,再对 Redis 服务进行重启,然后执行测试程序,不再出现 Redis 报错的问题。

需要指出的是,按照网上很多文章说的,可以通过修改 redis.conf 配置中的stop-writes-on-bgsave-error选项的方式来解决报错的问题,实际上只是把问题掩盖起来而。因为采用下面的配置:

stop-writes-on-bgsave-error no

RDB 功能虽然不能保存 Redis 数据快照了,但 Redis 仍可继续接受新的 Redis 请求。
为了从根本解决 RDB 报错的问题,还是应该通过修改vm.overcommit_memoryLinux 内核参数的方式来解决。

参考资料

  1. https://redis.io/topics/benchmarks
  2. http://www.redis.cn/topics/benchmarks.html
  3. http://blog.chedushi.com/archives/7719