多级缓存架构在消息系统中的应用
下面是谷歌的技术奠基人杰夫·狄恩(Jeff Dean)给出的一些计算机相关的硬件指标,虽然有些数据可能由于时间太久不够准确,但大致的量级基本还是一致的。
- L1 cache reference 0.5 ns
- Branch mispredict 5 ns
- L2 cache reference 7 ns
- Mutex lock/unlock 100 ns
- Main memory reference 100 ns
- Compress 1K bytes with Zippy 10,000 ns
- Send 2K bytes over 1 Gbps network 20,000 ns
- Read 1 MB sequentially from memory 250,000 ns
- Round trip within same datacenter 500,000 ns
- Disk seek 10,000,000 ns
- Read 1 MB sequentially from network 10,000,000 ns
- Read 1 MB sequentially from disk 30,000,000 ns
- Send packet CA->Netherlands->CA 150,000,000 ns
使用缓存虽然能够给我们带来诸多性能上的收益,但存在一个问题是缓存的资源成本非常高。因此,在 IM 系统中对于缓存的使用,就需要我们左右互搏地在“缓存命中率”和“缓存使用量”两大指标间不断均衡。
缓存的分布式算法
取模求余
如下图所示:如果消息 ID 哈希后对缓存节点取模求余,余数是多少,就缓存到哪个节点上。
取模求余的分布式算法在实现上非常简单。但存在的问题是:如果某一个节点宕机或者加入新的节点,节点数量发生变化后,Hash 后取模求余的结果就可能和以前不一样了。由此导致的后果是:加减节点后,缓存命中率下降严重。
一致性哈希
数据倾斜
为了解决物理节点少导致节点间数据倾斜的问题,我们还可以引入虚拟节点,来人为地创造更多缓存节点,以此让数据分布更加均匀。
我们为每一个物理节点分配多个虚拟节点,比如在上图这里,给节点 1 虚拟出 4 个节点。当消息进行缓存哈希定位时,如果落到了这个物理节点上的任意一个虚拟节点,那么就表示,真正的缓存存储位置在这个物理节点上,然后服务端就可以从这个物理节点上进行数据的读写了。
缓存热点问题
多级缓存架构 - 主从模式
为了防止文章下载阅读出现热点时,造成后端存储服务的压力太大,我们一般会通过缓存来进行下载时的加速。比如说,我们可以通过文章的唯一 ID 来进行哈希,并且通过缓存的一主多从模式来进行部署,主从模式的部署大概如下图:
多级缓存架构 -L1+ 主从模式
我们来看一下,L1+ 主从模式的部署和访问形式:
L1 缓存作为最前端的缓存层,在用户请求的时候,会先从 L1 缓存进行查询。如果 L1 缓存中没有,再从主从缓存里查询,查询到的结果也会回种一份到 L1 缓存中。
与主从缓存模式不一样的地方是:L1 缓存有分组的概念,一组 L1 可以有多个节点,每一组 L1 缓存都是一份全量的热数据,一个系统可以提供多组 L1 缓存,同一个数据的请求会轮流落到每一组 L1 里面。
多级缓存架构 - 本地缓存 +L1+ 主从的多层模式
对于大部分请求量较大的应用来说,应用层机器的部署一般不会太少。如果我们的应用服务器本身也能够承担一部分数据缓存的工作,就能充分利用应用层机器的带宽和极少的内存,来低成本地解决带宽问题了