http://blog.csdn.net/chinainvent/article/details/8073746
TS把若干个物理硬盘、目录(通过storeage.config描述),视为一个大的逻辑盘。并把这个逻辑盘,切分为若干个逻辑卷(通过volume.config描述)。
当一个请求过来,这个请求的URL等信息,将被计算出一个md5值,而key则是这个md5值的一个函数:key = fun(md5)。TS需要为每一个请求,关联到一个逻辑卷,以便从这个逻辑卷访问Cache内容。这个过程,就是通过key查找Volume的过程。为了确保高效,key=>Volume的映射保存在一张hash_table中:通过对key取模,映射到hash_table的某个slot,并从这个slot中取出Volume的序号。因此,查找的速度为O(1)。
问题的关键在于,如何生成、维护这张hash_table。这个哈希表,应该满足三个条件:
*条件1、因为逻辑卷(Volume)的Size是各异的,应该根据Volume的Size成比例的映射的hash_table的slot上。即是说,较大的Volume,应该占有哈希表较多的slot。
*条件2、应该让同一个Volume所占据的slots,随机地分布在hash_table上。这样有一个好处,可以确保相似的key,被分流到不同的Volume中,如果这些Volume恰好在不同的物理硬盘(这取决于Volume与物理硬盘的映射关系,我还没有读到相关代码),就能够并行的读写,提高IO访问速度。
*条件3、摘除或添加物理硬盘,会引起Cache数据的“重新均衡”,但一个好的hash算法,应该尽量减少这个均衡过程的代价。
下面将介绍TS是如何满足这三个条件的。
1)满足条件1:
设所有逻辑卷的大小T、hash_table的slot数为N,通过一个比例转换,可以计算数出每个Volume在hash_table中应占用的slot数:
nr_slots = Volume.size * N/T
2)满足条件2:
为了把某个Volume所占有的slot打散到hash_table上,TS使用每个Volume自身的md5作为一个随机数的种子,然后通过简单的求模,计算出第一个slot的位置,并生成下一个随机数,再次求模,计算出第二个slot的位置。循环这个过程,直到找出所有slot。如果发现某个slot已经被前面的Volume占用,则跳过该slot,继续查找下一未被占用的slot。
通过以上两步,在物理硬盘不发生变化的情况下,已经实现了一个简单且分布性较随机的hash_table,可以高效的把key映射到某个Volume上。
3)满足条件3:
当物理硬盘发生变化时,Volume与物理硬盘的映射关系,也可能发生变化(https://issues.apache.org/jira/browse/TS-949 大概是想解其中的一个bug?尚未完全理解)。而物理硬盘的变化,并不会改变volume.config的配置,所以物理硬盘的变化,并不直接影响逻辑卷在hash_table的分布(但如果物理硬盘的变化,使某个Volume被标识为不可用,则会影响)。从以上分析来看,满足条件3的关键,在于Volume与物理硬盘的映射关系。以上的分析,使我们可以在一个更小的范围内,来满足条件3,即只要关注Volume与物理硬盘的映射关系即可:D。