Redis 基准测试
在 Redis 服务器上使用 redis-benchmark 实用工具
Redis 有多快?
Redis 包含 redis-benchmark
实用工具,它会模拟 N 个客户端同时运行命令发送 M 个总查询(它类似于 Apache 的 ab
实用工具)。下面是针对在 Linux 机顶盒执行的基准测试的完整输出。
支持以下选项
Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>]
-h <hostname> Server hostname (default 127.0.0.1)
-p <port> Server port (default 6379)
-s <socket> Server socket (overrides host and port)
-a <password> Password for Redis Auth
--user <username> Used to send ACL style 'AUTH username pass'. Needs -a.
-c <clients> Number of parallel connections (default 50)
-n <requests> Total number of requests (default 100000)
-d <size> Data size of SET/GET value in bytes (default 3)
--dbnum <db> SELECT the specified db number (default 0)
--threads <num> Enable multi-thread mode.
--cluster Enable cluster mode.
--enable-tracking Send CLIENT TRACKING on before starting benchmark.
-k <boolean> 1=keep alive 0=reconnect (default 1)
-r <keyspacelen> Use random keys for SET/GET/INCR, random values for
SADD, random members and scores for ZADD.
Using this option the benchmark will expand the string
__rand_int__ inside an argument with a 12 digits number
in the specified range from 0 to keyspacelen-1. The
substitution changes every time a command is executed.
Default tests use this to hit random keys in the
specified range.
-P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline).
-e If server replies with errors, show them on stdout.
(No more than 1 error per second is displayed.)
-q Quiet. Just show query/sec values
--precision Number of decimal places to display in latency output (default 0)
--csv Output in CSV format
-l Loop. Run the tests forever
-t <tests> Only run the comma separated list of tests. The test
names are the same as the ones produced as output.
-I Idle mode. Just open N idle connections and wait.
--help Output this help and exit.
--version Output version and exit.
启动基准测试前,您需要运行一个 Redis 实例。一个典型示例如下
redis-benchmark -q -n 100000
使用此工具非常容易,并且您还可以编写自己的基准测试,但与任何基准测试活动一样,有一些陷阱需要注意。
仅运行一部分测试
每次执行 redis-benchmark 时,你无需运行所有默认测试。选择一个子集测试的最简单方法就是使用 -t
选项,如下面的例子所展示的
$ redis-benchmark -t set,lpush -n 100000 -q
SET: 180180.17 requests per second, p50=0.143 msec
LPUSH: 188323.91 requests per second, p50=0.135 msec
在上边的例子中,我们要求只运行 SET 和 LPUSH 命令测试,使用安静模式(参见 -q
开关)。
也可以像在下边例子中展示的那样直接指定基准命令
$ redis-benchmark -n 100000 -q script load "redis.call('set','foo','bar')"
script load redis.call('set','foo','bar'): 69881.20 requests per second
选择键空间大小
默认情况下,基准针对单个键运行。在 Redis 中,这种合成基准和其他真实基准之间的差异并不大,因为它是一个内存内系统,但是通过使用大型键空间,可以强调缓存错误,并且可以总体模拟更真实的工作负载。
这是通过使用 -r
开关实现的。例如,如果我想运行一百万个 SET 操作,使用 100k 个可能的键中的随机键进行每一个操作,我将使用下边的命令行
$ redis-cli flushall
OK
$ redis-benchmark -t set -r 100000 -n 1000000
====== SET ======
1000000 requests completed in 13.86 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.76% `<=` 1 milliseconds
99.98% `<=` 2 milliseconds
100.00% `<=` 3 milliseconds
100.00% `<=` 3 milliseconds
72144.87 requests per second
$ redis-cli dbsize
(integer) 99993
使用流水线
默认情况下,每一个客户端(如果未通过 -c
指定,此基准模拟 50 个客户端)仅在收到之前命令的答复时才发送下一个命令,这意味着服务器可能需要一个读取调用才能从每个客户端读取每个命令。还需要支付 RTT。
Redis 支持 流水线技术,因此可以一次发送多个命令,这是真实世界应用经常利用的一项功能。Redis 流水线能够大幅提升服务器能够提供的每秒操作数。
这是在 MacBook Air 11" 上使用 16 命令流水线运行基准的一个例子
$ redis-benchmark -n 1000000 -t set,get -P 16 -q
SET: 1536098.25 requests per second, p50=0.479 msec
GET: 1811594.25 requests per second, p50=0.391 msec
使用流水线会导致性能大幅提高。
陷阱和误解
第一个要点显而易见:有用的基准的黄金法则就是只比较苹果与苹果。例如,可以在相同工作负载上比较不同版本的 Redis。或者比较不同选项的相同版本的 Redis。如果你计划将 Redis 与其他内容进行比较,那么评估功能和技术差异并考虑这些差异至关重要。
- Redis 是一个服务器:所有命令都涉及网络或 IPC 回程。将其与 SQLite、Berkeley DB、Tokyo/Kyoto Cabinet 等嵌入式数据存储进行比较是没有意义的,因为大部分操作的成本主要在于网络/协议管理。
- Redis 命令会返回对所有常用命令的确认。而其他一些数据存储不会这么做。将 Redis 与涉及单程查询的存储进行比较只会稍有帮助。
- 在同步 Redis 命令上天真地进行迭代并不能对 Redis 本身进行基准测试,而只能测量网络(或 IPC)延迟以及客户端库固有延迟。要真正测试 Redis,你需要多个连接(例如 redis-benchmark)和/或使用流水线技术将多个命令和/或多个线程或进程聚合起来。
- Redis 是一款具备一些可选持久性选项的内存数据存储器。如果计划将其与事务性服务器(MySQL、PostgreSQL 等)进行比较,则应考虑激活 AOF 并决定一个合适的 fsync 策略。
- 从命令执行的角度来看,Redis 在很大程度上是单线程服务器(实际上 Redis 的现代版本为不同操作使用了线程)。它并非设计为可从多个 CPU 内核中受益。人们应该启动多个 Redis 实例,以便在需要时将多个内核扩展使用。将单个 Redis 实例与多线程数据存储器进行比较是不公平的。
一个常见的误解是 redis-benchmark 设计用于使 Redis 性能看起来很好,redis-benchmark 获得的吞吐量有些做作,无法通过真实的应用程序实现。这实际上是不正确的。
redis-benchmark 程序是在给定硬件上获取一些数字和评估 Redis 实例性能的快速且有用的方法。但是,默认情况下,它并不代表 Redis 实例能够维持的最大吞吐量。事实上,通过使用管道和快速客户端(hiredis),可以很容易地编写出一个生成比 redis-benchmark 更多吞吐量的程序。redis-benchmark 的默认行为是仅通过利用并发性(例如,它与服务器建立多个连接)来实现吞吐量。如果不通过 -P 参数显式启用,它不使用管道或任何并行化(最多每个连接一个待处理查询,并且没有多线程)。因此,在某种程度上,使用 redis-benchmark 并同时在后台触发例如 BGSAVE 操作将向用户提供更接近“最坏情况”而不是最好情况的数字。
要使用管道模式运行基准测试(并获得更高的吞吐量),需要显式使用 -P 选项。请注意,这仍然是一种现实的行为,因为许多基于 Redis 的应用程序积极使用管道来提高性能。但是,你应使用一个管道大小,这个大小或多或少是你应用程序中能够使用的平均管道长度,以便获得实际的数字。
最后,基准测试应应用相同的操作,并且你要比较的多个数据存储器的工作方式应相同。将 redis-benchmark 的结果与其他基准测试程序的结果进行比较并推论是绝对没有意义的。
例如,在单线程模式下可以比较 GET/SET 操作中的 Redis 和 memcached。两者都是内存数据存储器,在协议层上工作方式基本相同。如果它们各自的基准测试应用程序以相同方式(流水线)聚合查询并使用类似数量的连接,则比较实际上是有意义的。
Redis(antirez)和 memcached(dormando)开发人员之间的对话很好地说明了这个完美的例子。
antirez 1 - 关于 Redis、Memcached、速度、基准测试和厕所
dormando - Redis VS Memcached(略好的基准测试)
antirez 2 - Memcached/Redis 基准测试的更新
从最终结果来看,一旦考虑所有技术方面,这两种解决方案之间的差异并不那么惊人。请注意,在这些基准测试之后,Redis 和 memcached 都得到了进一步的优化。
最后,在对高效服务器(并且类似于 Redis 或 memcached 的存储器明确属于这种类型)进行基准测试时,可能很难使服务器饱和。有时,性能瓶颈在于客户端,而不是服务器端。在这种情况下,必须修复客户端(即基准测试程序本身),或者可能为其进行横向扩展,才能达到最大吞吐量。
影响 Redis 性能的因素
有多个因素直接关系到 Redis 性能。我们在此处提及这些因素,因为这些因素可更改任何基准测试的结果。但请注意,通常在性能较低的未调优计算机盒上运行的典型 Redis 实例通常为大多数应用程序提供了足够良好的性能。
- 网络带宽和延迟通常对性能产生直接影响。在启动基准测试之前,使用 ping 程序快速检查客户端与服务器主机之间的延迟是否正常,是一种良好的做法。关于带宽,通常对 Gbit/s 中的吞吐量进行估算,并将其与网络的理论带宽进行比较,这是很有用的。例如,在 Redis 中将 4 KB 字符串设置为 100000 q/s 的基准测试设置实际将消耗 3.2 Gbit/s 的带宽,并且可能适合 10 Gbit/s 的链路,但又不适合 1 Gbit/s 的链路。在许多实际场景中,Redis 吞吐量受网络限制远远早于受 CPU 限制。若要在单台服务器上合并几个高吞吐量 Redis 实例,值得考虑放置 10 Gbit/s NIC 或带有 TCP/IP 绑定机制的多个 1 Gbit/s NIC。
- CPU 是另一个非常重要的因素。由于是单线程的,Redis 倾向于使用拥有大缓存且内核较少的快速 CPU。在这款游戏中,英特尔 CPU 目前是优胜者。使用 AMD Opteron CPU 与类似于 Nehalem EP/Westmere EP/Sandy Bridge 英特尔 CPU 在 Redis 中进行对比,则获得的性能只有一半的情况并不少见。当客户端和服务器在同一计算机盒上运行时,CPU 是使用 redis-benchmark 的限制性因素。
- RAM 的速度和内存带宽对全局性能似乎不太关键,尤其是对于小型对象而言。不过,对于大型对象(>10 KB),这可能变得明显。通常,为了优化 Redis,购买昂贵的快速内存模块并不是真正的经济高效。
- 与在没有虚拟化的相同硬件上运行相比,Redis 在 VM 上的速度较慢。如果您有机会在物理设备上运行 Redis,则此方式更佳。但这并不意味着 Redis 在虚拟化环境中速度很慢,提供的性能仍然十分出色,而且您在虚拟化环境中可能会遇到的大部分严重性能问题都是由于超量供给、高延迟的非本地磁盘,或具有较慢的
fork
系统调用实现的旧版管理程序软件。 - 当服务器和客户端基准程序在同一台设备上运行时,可以使用 TCP/IP 回环和 Unix 域套接字。根据平台,与 TCP/IP 回环相比,Unix 域套接字可实现高出约 50% 的吞吐量(例如在 Linux 上)。redis-benchmark 的默认行为是使用 TCP/IP 回环。
- 当大量使用管道传输时(即长管道传输),与 TCP/IP 回环相比,Unix 域套接字的性能优势 tends to 趋于降低。
- 当使用以太网网络访问 Redis 时,在数据大小保持在以太网数据包大小(约 1500 字节)以下时,使用管道传输聚合命令特别有效。实际上,处理 10 字节、100 字节或 1000 字节的查询几乎可产生相同的吞吐量。请参见下图。
- 在多 CPU 套接字服务器上,Redis 性能取决于 NUMA 配置和进程位置。最明显的影响是,由于客户端和服务器进程随机分配在内核上,因此 redis-benchmark 结果看似不确定。若要获得确定性结果,必须使用进程放置工具(在 Linux 上:taskset 或 numactl)。最有效的组合始终是在同一 CPU 的两个不同内核上放置客户端和服务器,以使用 L3 缓存。以下是具有不同相对位置的 3 个服务器 CPU(AMD Istanbul、Intel Nehalem EX 和 Intel Westmere)的 4 KB SET benchmark 的一些结果。请注意,此基准测试并非用于比较 CPU 型号(因此未披露 CPU 的确切型号和频率)。
- 对于高端配置,客户端连接数量也是一个重要因素。由于基于 epoll/kqueue,因此 Redis 事件循环具有很大的可扩展性。Redis 已通过 60000 多个连接的基准测试,并且在这些条件下仍能够维持 50000 q/s。通常情况下,具有 30000 个连接的实例只能处理使用 100 个连接所能实现的吞吐量的一半。以下是一个示例,展示了 Redis 实例与连接数量之间的吞吐量
- 对于高端配置,可以通过调整 NIC 配置和关联的中断来实现更高的吞吐量。通过在 Rx/Tx NIC 队列和 CPU 内核之间设置关联,并激活 RPS(接收数据包转向)支持,可实现最佳吞吐量。有关此项内容的更多信息,请访问 此讨论组。当使用大型对象时,巨型帧也能提高性能。
- Redis 可以基于不同的内存分配程序(libc malloc、jemalloc、tcmalloc)编译,这些不同的内存分配程序在原始速度、内部和外部碎片方面可能会有不同的行为。如果你没有自己编译 Redis,则可以使用 INFO 命令来检查
mem_allocator
字段。请注意,大多数基准测试运行时间不够长,无法生成明显的外部碎片(与生产 Redis 实例相反)。
其他需要考虑的事项
任何基准测试的一个重要目标是获取可重复生成的结果,以便可以与其他测试的结果进行比较。
- 一个好的做法是尽力在隔离硬件上运行测试。如果无法做到这一点,则必须监控系统以检查基准测试是否受到某些外部活动的影响。
- 一些配置(台式机和笔记本电脑肯定有,一些服务器也具有)具有可变的 CPU 核心频率机制。可以 地在操作系统级别设置控制此机制的策略。与其他 CPU 机型相比,一些 CPU 机型的频率适应型更强。若要获得可重复生成的结果,最好为基准测试中涉及的所有 CPU 核心设置尽可能最高的固定频率。
- 一个重点是根据基准测试调整系统大小。系统必须有足够的 RAM,并且不得进行交换。在 Linux 上,不要忘记正确设置
overcommit_memory
参数。请注意,32 位和 64 位 Redis 实例的内存占用量不同。 - 如果你计划在基准测试中使用 RDB 或 AOF,请检查系统中是否没有其他 I/O 活动。避免将 RDB 或 AOF 文件放在 NAS 或 NFS 共享,或放在影响网络带宽和/或延迟的任何其他设备上(例如,Amazon EC2 上的 EBS)。
- 将 Redis 日志级别(loglevel 参数)设置为警告或通知。避免将生成的日志文件放在远程文件系统中。
- 避免使用可能改变基准测试结果的监控工具。例如,定期使用 INFO 来收集统计信息可能会很好,但 MONITOR 将显著影响测量到的性能。
不同 Redis 版本在裸机服务器上的基准测试结果
至关重要的是,在每个已发布版本中稳定或平稳提升 Redis 性能。
为了评估这一点,我们使用 redis-benchmark
对 Redis 的已发布版本(从 v2.6.0 开始)执行了一系列命令类型的基准测试,在一个独立的 redis-server 上重复多次相同的基准测试,确保其统计显着性,并测量运行时差异。
所使用的硬件平台是一个稳定的裸机 HPE ProLiant DL380 Gen10 服务器,配备一个英特尔(R) 至强(R) Gold 6230 CPU @ 2.10GHz,禁用英特尔超线程技术,禁用 CPU 频率缩放,并将所有可配置的 BIOS 和 CPU 系统设置设置为性能。
该机箱运行 Ubuntu 18.04 Linux 版本 4.15.0-123,且 Redis 是由 gcc 7.5.0 编译的。所使用的基准客户端(redis-benchmark
)在所有测试中保持稳定,版本为 redis-benchmark 6.2.0 (git:445aa844)
。redis-server 和 redis-benchmark 进程同时固定到特定的物理内核。
以下基准选项用于各测试
- 这些测试有 50 个同时客户端执行 5 百万请求。
- 测试使用环回接口执行。
- 测试在没有管道的情况下执行。
- 所使用的有效负载大小为 256 字节。
- 每个 redis-benchmark 进程使用 2 个线程以确保基准客户端不是瓶颈。
- 对象、哈希、集合和有序集合数据类型进行了基准测试。
下面我们展示已获得的结果,按数据类型细分。
其他 Redis 基准测试工具
有很多第三方工具可用于 Redis 基准测试。请参阅每个工具的文档了解更多关于其目标和功能的信息。
- memtier_benchmark 由 Redis Labs 提供,是一款 NoSQL Redis 和 Memcache 流量生成和基准测试工具。
- rpc-perf 由 Twitter 提供,是一款基准测试 RPC 服务的工具,支持 Redis 和 Memcache。
- YCSB 由 Yahoo @Yahoo 提供,一款基准测试框架,带有适用于许多数据库的客户端,包括 Redis。