“无底洞”现象

案例:

2010年,Facebook 的 Memcache 节点达到 3000 个,开发和运维人员发现一个问题,为满足业务需要添加的大量 Memcache 节点,但性能却并没提升,反而下降了。这一现象即为缓存的“无底洞”现象。

为什么会出现

  • 添加更多的 Memcache(缓存)所能支撑的数据量,并发量会更大,性能会更强
  • 但实际使用的时候,通常通过 Hash 方式将数据映射到不同节点,导致业务使用,写入或读取数据,会经过更多不同的节点网络请求
  • 分布式批量操作,会增加多次网络请求时间,随之性能下降。

如:

  • 分布式集群下:Redis 的 mget 操作需要访问多个 Redis 节点,即:N 次网络请求时间
  • 单机上:Redis 的 mget 只需要访问一个节点,即:一次网络请求时间

总结:

  • 分布式节点的增加,服务所能支撑的数据量,抗并发量等性能会增加;
  • 客户端批量操作会涉及多次网络操作,意味着操作会随着节点的增多,耗时会不断增大
  • 网络连接数变多,对节点的性能也有一定影响

更多的节点不代表客户端具备高性能,“无底洞”现象就是说:投入越多,产出不一定越多。

如何优化

  • 命令本身优化,如:优化 SQL 语句等
  • 减少网络通信次数;如:基于业务划分多集群、分布同节点等
  • 降低接入成本,如:客户端长连接、连接池、NIO 等

Redis 批量操作 N 个字符串的三种实现方式:

  • 客户端 N 次 get:N 次网络 + N 次 get 命令
  • 客户端 1 次 pipeline get:1 次网络 + N 次 get 命令
  • 客户端 1 次 mget:1 次网络 + 1 次 mget 命令

pipeline/mget 不适用 Redis Cluster 中数据在不同节点的数据,必须在同一台节点

Redis Cluster 四种分布式的批量操作方式:

  • 串行命令

Redis Cluster 实际应用中,N 个 Key 大多不在同一节点中,因此无法使用 mget、pipeline。需要用 N 次 get 命令。

操作耗时:N 次网络时间 + N 次命令时间

  • 串行 IO

Redis Cluster 中 N 个 Key 因为哈希分布到不同节点,实际最终可能分布到 M 个节点(M <=N),如能确定最终分布的节点,可进行分组后,在使用如:mget、pipeline 等批量操作命令。

操作耗时:M 次网络时间 + N 次命令时间

  • 并行 IO

**
即将串行 IO 才用多线程(M)方式执行。该方案会增加编程复杂度。

操作耗时:max(M)网络时间 + N 次命令时间

  • Hash_tag 实现

Redis Cluster 的 hash_tag 功能,可将多个 key 强制分配到同节点上。

操作耗时:1 次网络耗时 + N 次命令时间

方案对比:

方案 优点 缺点 网络 IO
串行命令
- 编程简单
- 如果少量 Key,性能可满足

- 大量 key 时,请求延迟严重
O(keys)
串行 IO
- 编程简单
- 少量节点,性能可满足

- 大量节点时,请求延迟严重
O(nodes)
并行 IO
- 利用并行特性,延迟取决最慢的节点

- 编程复杂
- 由于多线程,问题定位更难
- 并行处理造成的并发,对节点负载稍有影响
O(max(nodes))
hash_tag 性能最高
- 业务维护成本较高
- 容易出现数据倾斜,造成部分节点负载
O(1)