跳转到主要内容

Redis TLS 对性能造成了巨大冲击——看看 KeyDB 是如何解决这个问题的

我们对 Redis 和 KeyDB 的 ‘6.0’ 版本中加入的 TLS (传输层安全) 支持感到非常兴奋。TLS 数据库连接是深度防御持续趋势的一部分,这一趋势由来已久,最早始于 2013 年谷歌加密其数据中心之间的链接

不幸的是,在 Redis 中,TLS 带来了 36-61% 的巨大性能损失。虽然安全很重要,但在性能上做出权衡并非总是可行的妥协。我们仔细考虑了 KeyDB 中的 TLS 实现,试图避免让我们的用户遇到这种情况。通过利用 KeyDB 的多线程架构,我们能够保持性能,实现了近 100 万次操作/秒(ops/sec)的速度,比 Redis 的 TLS 实现快了 7 倍以上。

对于那些可能还不知道的人,KeyDB 是一个开源项目,与 Redis API、协议和客户端兼容。

Redis 对 TLS 性能的分析#

我最近在看一位 RedisLabs 性能工程师写的详细的 github issue 回复。他出色地分析了使用 TLS 时的 CPU 占用和性能下降问题。

他在这里指出,单线程性能下,每秒操作数(ops/sec)大约下降了 36%。在他的分析中,他提到“TLS 在协议栈中增加了一层,带来了额外的开销。增加的 SSL/TLS 协议栈意味着 28% 的 CPU 时间被用于它(从 ssl 连接中读/写字节,加密/解密和完整性检查)”……“根据火焰图,他们从花费 17% 的时间在处理器上,增加到使用 tls 后花费 45% 的 CPU 时间。”

根据 Redis 的文档,它目前不支持使用 io-threads。RedisLabs 的文档也警告说“TLS 加密会显著影响数据库的吞吐量和延迟”。

Redis、TLS 和多线程 KeyDB 的比较#

基准测试 Ops/sec#

通过我们自己的测试,我们看到了与 Redis 在单个节点上所述相同的结果(下降 36%)。我们还将包括与 Redis io-threads 的比较,以及 KeyDB 在启用 TLS 后的性能表现。本测试着眼于单个节点的性能。测试在足够大的 AWS m5 实例上进行,以确保机器不是瓶颈。

以下测试使用 memtier 在 m5.8xlarge 实例上执行,命令如下:

$ memtier_benchmark --hide-histogram --tls --cert=/path/to/redis.crt --key=/path/to/tls/redis.key --cacert=/path/to/tls/ca.crt -s 172.31.56.132 --threads=32

KeyDB 和 Redis 运行在 m5.4xlarge 实例上。有关复现基准测试的更多设置细节,请参见本博客末尾。

image

您将看到,我们的测试显示,在 Redis6(单线程)上启用 TLS 后,性能下降了 36%,与测量结果一致。然而,如果您之前使用的是 io-thread 功能,您可能会看到性能下降 61%,因为使用 TLS 时不支持 io-threads。这一点在 TLS.md 中有说明。

为什么 KeyDB 的性能没有下降#

使用 KeyDB 时,启用 TLS 几乎没有性能下降,因为它支持多线程并且可以垂直扩展。由于项目之间的架构差异,其性能总体上仍然要高得多。使用 TLS 会消耗更多的 CPU 资源,但这正是 KeyDB 擅长解决的问题。

在分配 8 个线程的情况下,我们的 ops/sec 接近 80 万,而在没有 TLS 的情况下是 100 万 ops/sec。然而,将线程数增加到 16 个,使我们能够恢复到接近 100 万 ops/sec 的水平。

这使我们能够补偿负载,并仍然为依赖性能的用户提供安全性,而不会牺牲性能。

延迟基准测试(越低越好)#

在上述相同测试的延迟测量中,可以看到类似的趋势。您可以看到,在这些负载下使用 TLS 时,延迟要高得多。KeyDB 不仅能以非常高的吞吐量提供服务,而且其延迟也比带 TLS 的 Redis 低 7 倍。需要注意的是,用 memtier 测量的延迟是在推动峰值负载,而在非重载情况下,延迟会低得多。这应被视为负载下的相对比较。

image

火焰图#

在 RedisLabs 的分析中,他们提供了火焰图作为参考。对于感兴趣的人,我们对 Redis 6、带 io-threads 的 Redis 6、Redis 6 + TLS、KeyDB 和 KeyDB + TLS 运行了火焰图。通过点击图表下方的链接,可以看到完整的可展开明细。

image

完整可展开火焰图链接

结论#

在安全性方面,TLS 加密是一个很好的选择,但如果您目前正在使用 Redis,这可能会带来性能损失。如果您没有高负载地使用 Redis,您也许可以处理额外的开销,但这是需要考虑的因素。

总结性能:

  • 与单线程 Redis 相比,启用 TLS 的 Redis 性能下降了 36%,与使用 io-threads 的 Redis 相比,性能下降了 61%。
  • 在增加额外线程后,KeyDB 实现了接近 100 万次/秒的操作,性能几乎没有下降。
  • 启用 TLS 的 KeyDB 达到了近 100 万次/秒的操作,而 Redis 约为 13 万次/秒。

Redis 用户提高性能的选项包括分片或增加集群大小。KeyDB 也可以作为 Redis 二进制文件的直接替代品。

在使用 TLS 时,KeyDB 的速度比 Redis 快 7 倍以上。如果性能对您是个问题,那么在使用此功能时,KeyDB 可能是一个可行的替代方案。

了解更多:#

## 复现基准测试

对于基准测试,需要一台 aws m5.8xlarge 作为基准测试机,使用 memtier 作为基准测试工具。对于 Redis/KeyDB 实例,使用了一台 m5.4xlarge。机器大小的选择基于终端性能,确保机器不是瓶颈。更大的机器大小不会对结果产生影响,但较小的机器可能会导致吞吐量降低。

如果您是第一次使用 TLS,可以简单地克隆 github 项目并运行 `./utils/gen-test-certs.sh` 来为 Redis 或 KeyDB 生成证书。

您现在可以使用以下命令运行 keydb-server:

$ keydb-server --tls-port 6379 --port 0 --tls-cert-file ./tests/tls/redis.crt --tls-key-file ./tests/tls/redis.key --tls-ca-cert-file ./tests/tls/ca.crt --server-threads 16 --server-thread-affinity true --protected-mode no

对于 Redis,我们运行了以下命令:

$ redis-server --tls-port 6379 --port 0 --tls-cert-file ./tests/tls/redis.crt --tls-key-file ./tests/tls/redis.key --tls-ca-cert-file ./tests/tls/ca.crt --protected-mode no

为了使用 memtier 通过 TLS 连接,请确保将 ./test/tls/* 文件传输到基准测试机。然后您可以运行以下命令:

$ memtier_benchmark --hide-histogram --tls --cert=/path/to/redis.crt --key=/path/to/tls/redis.key --cacert=/path/to/tls/ca.crt -s 172.31.56.132 --threads=32

对于不使用 TLS 的测试,使用了以下命令: KeyDB

$ keydb-server –-server-threads 10 –-server-thread-affinity true –-protected-mode no

Redis

$ redis-server -–io-threads 8 –-protected-mode no

Memtier

$ memtier_benchmark -s 172.31.56.132 --hide-histogram –-threads=32

避免瓶颈#

  • 由于 KeyDB 的多线程和性能提升,我们通常需要一台比运行 KeyDB 的机器大得多的基准测试机。我们发现需要一台 32 核的 m5.8xlarge 才能用 memtier 产生足够的吞吐量。这支持高达 16 核 KeyDB 实例(中型到 4xlarge)的吞吐量。
  • 使用 Memtier 时,运行 32 个线程。
  • 在同一网络上运行测试。如果比较实例,请确保您的实例在同一可用区(AZ)。例如,两个实例都在 us-east-2a。
  • 使用私有 IP 地址运行。如果您使用 AWS 公有 IP,网络可能会有更大的可变性。
  • 注意不要通过代理或 VPC 运行。当使用这些方法、防火墙和其他附加层时,很难确定瓶颈可能在哪里。最好在简单的环境(在同一个 vpc 内)中进行基准测试,然后再添加其他层,以确保您已经优化。
  • 在比较不同的机器实例时,请确保它们在同一个可用区,并且测试时间尽可能接近。一天中的网络吞吐量会发生变化,因此尽可能相近地进行测试可以提供最具代表性的相对比较。
  • KeyDB 是多线程的。运行时请确保指定多个线程。