点 快速的未来即将到来,在您所在城市举行一项活动。

加入我们,参加 Redis 发布会

基准:共享 vs. 专用 Redis 实例

 
 
 
如果说 Redis 以什么而闻名,那就是速度;它被认为是现代 K/V 存储中最具执行力的。Redis 的设计和执行几乎每个方面都极其高效,并且面向顶级性能。
 
然而,可能无意地会导致 Redis 服务器性能远低于最佳状态的方面。一个这样的方面是看似有用的在单个 Redis 实例上管理多个数据库的能力(与专用、每个实例一个数据库的方法相反)。此主题过去已讨论过,比如,Elikenn 在 Stack Overflow 上、Chris Laskey 在他的博客上以及matteo 在 Redis 小组上。

理论

极类似传统 RDBMS 的模式,Redis 数据库由 Redis 服务器的单个实例管理,但逻辑上是分开的。拥有单个 Redis 实例管理多个数据库的主要好处是减少了管理开销。常见的做法是,单个应用程序同时使用多个数据库(例如会话、排名、计数器…),并且每个环境都需要这些数据库(开发、测试、登台、生产…)。通过拥有单个 Redis 实例管理多个(最多 16 个)数据库,降低了实例(可能还有服务器)的总数量,以及管理它们的所需工作量。
 
为了减少管理,使用单个实例用于多个数据库(又名共享实例)的理由很有道理,所以应用程序开发人员常常临时采用此方法。然而,事物的性质是暂时的会变成永久的。因此,共享实例部署选择得以延续,有时甚至进入生产。通常在这个时候,性能问题会浮出水面。
 
共享实例最终会因为 Redis 的架构而表现得很不够好。为了极其快速,每个 Redis 实例使用(几乎)单一线程来实现,以便消除上下文切换的工作量,取消阻塞并提供简单的序列化。由于共享实例使用单个线程,因此在对特定数据库执行操作后该线程可能会变慢甚至被阻塞,从而影响其他数据库。虽然在开发和测试中几乎难以察觉,但如果在生产中遇到,这可能会造成灾难性的后果。

证明

我们设置了一个基准测试以论证这一点。我们在 us-east-1 中设置了一个 m2.4xlarge AWS 实例,作为我们的应用客户端,并使用它来运行我们自己的自制基准测试工具(可从我们的 github 帐户获得此处)。基准测试工具的每次运行包含执行 10000 个 SET 和 GET 操作(1:1 比率),通过启动 4 个线程,且每个线程打开 25 个连接。我们让工具执行 10 次的每次运行以收集有意义的聚合平均值。对于那些希望重现我们的成果的人,以下是运行基准测试工具的语法

memtier_benchmark -s <主机> -p <端口> -P redis -t 4 -n 10000 –ratio 1:1 -c 25 -x 10 -d 100 –key-pattern S:S

我们在两个 Redis 实例两次运行基准测试。第一个实例是一个标准的 Redis 服务器,运行的是一个 m1.xlarge AWS 实例,第二个实例是从我们自己的 Redis 云服务一个新配置的资源。这两个服务器都使用的是 Ubuntu v12.04 和 Redis v2.6.14(我们将在几周内将 v2.6.14 升级到生产服务)。
 
在两个实例上运行的第一个目标是基准测试实例在专用模式时的性能,这样只有基准测试工具会使用它。在第二次运行中,我们模拟了一个共享实例场景,因此我们创建了另一个数据库1,并在基准测试运行时,针对它执行资源密集的 ZINTERSTORE 操作。
 
下图清楚地显示了我们的这个小循环对基准测试结果的影响,共享实例的性能指标下降了一个数量级。每个实例的吞吐量(由该实例每秒处理的平均请求数衡量)从接近 35K 下降到每秒仅 3.5K 个请求
每秒请求图表
 
每个实例的延迟(由毫秒中的平均响应时间衡量)也受到了一些影响 - 从不到 3 毫秒增加到将近 28 毫秒
平均响应时间图表
 
最后但并非最不重要的是,代表延迟分布曲线尾部的排名前 95% 的请求的响应时间。虽然这两个专用实例都在同一范围内,但共享实例早已超出了它们范围
第 Ninety-Fifth 分位响应时间图表

不可避免的结论

Redis 是一种了不起的技术,具有丰富的特性和强大的能力。它和任何工具一样,都可能被滥用并带来不良后果。在最初,使用共享 Redis 实例可能节省许多开销,但在生产中使用它们通常被认为是一个坏主意。尽管增加了工作负载,我们建议只在实际场景中使用专用 Redis 实例。它们不仅可以避免数据库间阻塞,还能允许配置每个数据库不同的数据持久性和驱逐策略。此外,Redis 可能正在逐渐淘汰共享实例,因此无论如何最好停止使用它们。

1 事实上,我们的 Redis 云服务中的每一个资源都是一个专用实例。因此,第二次 Redis 云基准测试是在两个独立的资源(数据库)上执行的,并且展示了与对该服务执行的第一个基准测试相同的结果