最近在读《大型网站技术架构——核心原理与案例分析》,第一感觉是虽然目前没看到有啥深奥难懂的地方,但是一下子开阔了我的视野。在总体上给我描绘了一个大型网站的技术架构,就像一个知识框架,虽然很多技术还只是处于听过的阶段,但是却指明了学习的方向。
分布式缓存集群的伸缩性设计
书中以Memcached为例介绍分布式缓存集群,而正好我在工作中也用到了这个,所以稍微记录一下。
Memcached访问模型
Memcached会通过一个特定的路由算法获取将要读写的服务器地址,然后将数据缓存在其中或者取出,其访问模型如下:
伸缩性挑战
在Memcached中,通过特定的路由算法在指定的缓存服务器中将数据写入或者读出,那么路由算法的质量直接决定了Memcached分布式缓存集群的伸缩性。比较简单的路由算法如使用余数Hash,就是拿缓存服务器数量做模,对key值Hash除以模取余,得到缓存服务器编号。这种算法简单,但是伸缩性很差,设想一旦增加或者缩减缓存服务器数量,改变了模值,那么相应的余数也很可能被改变,获得错误的缓存服务器编号,造成缓存未命中。假设模值由3变为4,在缓存均匀分布在原来3台服务器上的情况下,会造成75%的缓存失败,而模值如果由99变为100,相同的情况下,则会有99%的缓存失败。这会对数据库造成非常大的影响。
一致性Hash算法
一致性Hash算法是先构建一个0到2^32整数环,然后按节点名称的Hash值将缓存服务器放在对应的环节点上。根据key值算出其Hash值,然后顺时针找到离该Hash值最近的缓存服务器,进行数据的读写。如下图所示:
在需要增加缓存服务器时,在该环上加入新增服务器,这样就只有环上该新增服务器所在点到逆时针最近的一个服务器节点的缓存数据会失效。假设原来环上3台,现在新增一台,在原来的服务器节点均匀分布的情况下,缓存继续命中的概率超过75%,而原来环上99台,现在新增一台,同样情况下,缓存继续命中的概率超过99%。这种算法就不会在缓存服务器伸缩的时候给数据库带来过大的压力。
这种路由算法已经足够好了,但是书中也提到了这个算法的一种小问题,就是会出现缓存负载不均的情况。因为新增的一台缓存服务器只会减轻顺时针离它最近的一个缓存服务器的压力,对其他的缓存服务器没有任何影响。假设缓存服务器的性能都是一样的,那么自然会造成部分缓存服务器存储数据过多,负载过大的问题。
那么解决这个问题的方法就是在这个数据环上增加一个虚拟层,通过该虚拟层将新增的一个缓存服务器虚拟为一组虚拟缓存服务器,然后将这些虚拟缓存服务器的Hash值放到环上。进行数据操作时,先通过key的Hash值找到虚拟缓存服务器,再找到对应的真实服务器。在原来的虚拟服务器均匀分布并且虚拟的服务器数量大于真实的服务器数量的情况下,新增的一组虚拟服务器能帮助原来的所有真实服务器分担一小部分负载。如果每个真实服务器虚拟的虚拟服务器越多,那么各个真实服务器之间的负载就会越均衡,根据实际经验,虚拟服务器一组分150左右是比较合适的。
总结
当一头牛拉不到车了,不是找一头更强壮的牛来拉,而是让两头牛来拉车,这是为什么大型网站需要分布式服务器集群。如何让两头牛出力相差不大,不至于一头被累死,这是为什么要使用一致性Hash算法。