欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

正确运用Redis指南:实战性能测试揭秘

最编程 2024-07-22 22:11:01
...

1. 概述

简单来说,Redis就是一个数据结构存储器,可以用作数据库、缓存和消息中间件,它和传统数据库主要有两点不同:

  1. 它是Key-Value型数据库,不是关系型数据库,所有数据以Key-Value的形式存在服务器的内存中,其中Value可以是多种数据结构:字符串(String), 哈希(hashes), 列表(list), 集合(sets) 和有序集合(sorted sets)等类型;
  2. 它所有运行时数据都存在内存中,总所周知,内存的存取效率比磁盘要高不止一个数量级,Redis的性能必定非常优秀,根据官方数据,Redis可以轻松支持超过10万次QPS的读写频率;

有了以上两个主要特性支撑,虽然它起步较晚,但发展迅速,目前已经成为主流架构中缓存服务的首选。除了以上两个特性,Redis还提供了非常丰富的能力,包括:

  1. 原子性,Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行,我们能非常方便地实现事务;
  2. 支持发布、订阅,可以用来实现消息系统;
  3. 支持过期逻辑,做缓存时非常实用;
  4. 支持内存中的数据持久化,不用担心服务器宕机带来的灾难性后果;
  5. 提供了简单的事务功能,能在一定程度上保证事务特性
  6. 支持Lua脚本,可以利用Lua创造出新的Redis命令;
  7. 提供了流水线(Pipeline)功能,这样客户端能将一批命令一次性传到Redis,减少了网络的开销,在请求数据较小的情况下,可以大幅提升吞吐量;
  8. Redis使用单线程模型,预防了多线程可能产生的竞争问题,简单且稳定。
  9. 原生支持主从复制,为高可用实现提供有力支持;
  10. 受到社区和各大公司的广泛认可,支持Redis的客户端语言非常多,几乎涵盖了主流的编程语言,例如Java、PHP、Python、C、C++、Nodejs等。

适合Redis的应用场景非常多,本节列几个简单的场景供大家参考:

  1. 缓存频繁读取但修改频率小的数据 ,比如首页推荐的产品列表等,不怕丢数据,丢了可以从数据库中重新加载;
  2. 用户Session ,不怕丢数据,丢了用户重新登录即可;
  3. 缓存批量任务的中间结果。不怕丢数据,丢了重新计算中间数据就可以了;
  4. 分布式锁。

2. 性能

由于各种原因,Redis的性能非常好,也由于Redis的性能非常好,才会有这么多人关注Redis,但是到底有多好,没有实验数据做支撑,别人问咱们的时候,咱们也不好张嘴就来,在本节中我们来做个简单的性能测试,测试目的主要有两点,第一就是看看Redis的性能到底有多好,在数据上有个感性认识,第二,我们来分析下影响Redis性能的因素到底有哪些。

首先我们总结下Redis性能好的原因:

  1. 业务数据的存取基于内存实现,内存的IO速度很快;
  2. Redis使用单线程模型,预防了多线程可能产生的CPU资源争夺造成的性能损耗;
  3. 支持Pipeline,将一批命令一次性传到Redis,减少了网络的开销。

2.1 性能测试环境

在做性能测试之前,我们把本次测试的环境说清楚。

  • 机器性能: 使用一台1核CPU,内存为1G的阿里云服务器去压测一台2核CPU,内存4G的Redis服务器,Redis服务器的CPU型号为2.5 GHz主频的Intel ® Xeon ® E5-2682 v4(Broadwell) 确保压测客户端的机器性能不会遇到瓶颈

  • 网络环境: 两台服务器的网卡都使用1000M,在同一个局域网内,内网带宽1000M 两台机器的内网IP分别为:172.17.167.56(Redis服务器)、172.17.167.55(压测机)

  • 系统环境 两台服务器都使用CentOS 7,将一下两个参数设置成: vm.overcommit_memory = 1 net.core.somaxconn = 2048
  • Redis相关配置 有三个配置需要注意一下: #后代运行 daemonize yes #不开启applend形式的数据持久化能力 appendfsync no #不开启快照能力

#save <seconds> <changes>

  • 测试工具 使用Redis自带的测试工具redis-benchmark进行测试,下面是一条在压测机上运行的测试命令:

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 100 -P 8 -q

我们简单介绍下这条命令的各个参数的语义:

-h 目标Redis服务网络地址

-p 目标Reids服务的端口

-c 客户端并发长连接数

-n 本次测试需要发起的请求数

-t 测试请求的方法

-d 测试请求的数据大小

-P 开启Pipeline模式,并制定Pipeline通道数量

-q 只显示requests per second这一个结果

上面这条命令的语义就是,向172.17.167.56:6379这个Redis发送100万个请求,使用20个长连接发送,所有请求都是set命令,每个set命令的包体为100字节,使用8条Pipeline通道发送,并且只显示requests per second这一个结果。

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 100 -P 8 -q
SET: 534759.38 requests per second

2.2 基本性能测试

本节中的性能测试主要观察一个指标,就是Redis每秒处理多少个请求,RPS(Request per second)。

2.2.1 客户端长连接数量对性能的影响

我们做四个测试,分别使用1个长连接,5个长连接,10个长连接,50个长连接发送100万个请求大小为100字节的请求,对比四个测试结果看看客户端长连接数量对Redis服务性能有什么影响:

  • 1个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 1 -n 1000000 -t set -d 100 -q SET: 8768.55 requests per second
  • 5个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 5 -n 1000000 -t set -d 100 -q SET: 35334.44 requests per second
  • 10个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 100 -q SET: 52430.14 requests per second
  • 50个长连接 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 50 -n 1000000 -t set -d 100 -q SET: 52413.65 requests per second

连接数

整体RPS

单连接RPS

1个

8768.55

8768.55

5个

35334.44

7066.88

10个

52430.14

5243.01

50个

52413.65

1048.27

从上面三个测试用例的测试结果来看,我们可以发现:

  1. 只有一个长连接通信时,RPS是8700左右;
  2. 在长连接数量增加时,RPS的值会接近线性地增加;
  3. 长连接数量增加到一定数值时,整个Redis的RPS就稳定了,稳定在52400左右;

客户端长连接的数量会影响Redis整体吞吐量,但长连接数量增长到一个平衡值之后,长连接的数量不再影响系统的整体吞吐量,这个平衡值要看实际情况,网速、请求包大小等因素都会有影响。

2.2.2 请求包大小的影响

请求包的大小肯定会影响Redis每秒处理请求数量,这个是毋庸置疑的,但是具体是怎么影响的,我们做几个实验来观察下:

  • 请求包大小为2字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 2 -q SET: 52474.16 requests per second CPU平均损耗:42%
  • 请求包大小为1000字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 1000 -q SET: 52430.14 requests per second CPU损耗:48%
  • 请求包大小为1400字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 1400 -q SET: 45396.77 requests per second CPU平均损耗:41%
  • 请求包大小为1500字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 1500 -q SET: 25518.67 requests per second CPU平均损耗:29%
  • 请求包大小为5000字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 10 -n 1000000 -t set -d 5000 -q SET: 12736.74 requests per second CPU平均损耗:24%
  • 请求包大小为10000字节 ./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 10000 -q SET: 6476.81 requests per second CPU平均损耗:18%

请求包体大小(字节)

整体RPS

redis server cpu

2

52474.16

42%

1000

52430.14

48%

1400

45396.77

41%

1500

25518.67

29%

5000

12736.74

24%

10000

6476.81

18%

从上面六个实验的结果来分析,我们可以得出以下结论:

  1. 在Redis服务器的CPU资源充足的情况下,2个字节的请求和1000个字节的请求对于Redis的整体吞吐量无任何影响;
  2. 请求大小在1400字节以内时,Redis的性能表现稳定,当请求大小等于1500字节时,Redis整体性能下降得很厉害。由于一般TCPIP网络的MTU设置为1500字节,一旦测试数据尺寸超过1500字节时会被拆分为多个数据包在网络上传输,加剧了性能下降的幅度。
  3. 请求大小大于1500之后,Redis的性能随着包体的增加成接近线性关系地下降。

2.3 Pipleline模式

Redis是基于同步的请求应答模型提供服务的,正常情况下,客户端发送一个请求,在等到Redis的应答后才会继续发送第二个请求。在这种情况下,如果同时需要执行大量的命令,每一个长连接的利用率不高,大多数时间都在等待,这种模式长连接的利用率不高,如下图。

Redis 提供了一种聚合请求和应答的pipeline模式,简单说就是讲多个命令聚合在一个请求中发送给Redis,Redis执行这一批命令,在执行过程中,讲执行结果缓存到内存中,等这所有一批命令都被执行完成后,讲所有的命令执行结果放在一个应答中返回给客户端。

Pipeline 在某些场景下非常有用,比如有多个 command对相应结果没有互相依赖,对结果响应也无需立即获得,那么 pipeline 就可以充当这种“批处理”的工具;而且在一定程度上,可以较大的提升性能,性能提升的原因主要是 TCP 连接中减少了“交互往返”的时间。本文图示的例子,三个正常的command一般数据量较小,放在一个pipeline请求,一般一个tcp报文就发送给服务器端了,而非pipeline模式需要发送三次,每次都需要等待应答回来后才能继续发送,在传输与处理效率上,pipeline机制明显要高效很多。下面我们做一个实验来验证下具体效率会高多少。

  • 命令大小为100字节,不使用pipeline传输

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 100 -q

SET: 52394.43 requests per second

  • 命令大小为100字节,使用pipeline传输,每个请求携带8个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 8

SET: 495662.97 requests per second

  • 命令包大小为100字节,使用pipeline传输,每个请求携带10个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 10

SET: 558659.25 requests per second

  • 命令包大小为100字节,使用pipeline传输,每个请求携带11个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 11

SET: 312940.09 requests per second

  • 命令包大小为100字节,使用pipeline传输,每个请求携带15个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 100 -q -P 15

SET: 452386.34 requests per second

  • 请求包大小为100字节,使用pipeline传输,每个请求携带40个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 5000000 -t set -d 100 -q -P 40

SET: 487519.53 requests per second

  • 请求包大小为200字节,不使用pipeline传输

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q

SET: 51167.91 requests per second

  • 请求包大小为200字节,使用pipeline传输,每个请求携带4个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q -P 4

SET: 220288.56 requests per second

  • 请求包大小为200字节,使用pipeline传输,每个请求携带6个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q -P 6

SET: 161147.36 requests per second

  • 请求包大小为200字节,使用pipeline传输,每个请求携带8个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 2000000 -t set -d 200 -q -P 8

SET: 221361.38 requests per second

  • 请求包大小为3000字节,不使用pipeline传输

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 3000 -q

SET: 21104.17 requests per second

  • 请求包大小为3000字节,使用pipeline传输,每个请求携带8个命令

./redis-benchmark -h 172.17.167.56 -p 6379 -c 20 -n 1000000 -t set -d 3000 -q -P 8

SET: 21713.17 requests per second

请求包体大小(字节)

请求携带命令数

整体RPS

100

1

52474.16

100

8

495662.97

100

10

558659.25

100

11

312940.09

100

15

452386.34

100

40

487519.53

200

1

51167.91

200

4

220288.56

200

6

161147.36

200

8

221361.38

3000

上一篇: 实操指南:时序预测比赛中的异常检测算法解析

下一篇: 静电、浪涌与TVS

推荐阅读