“无底洞”现象
案例:
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) |