Tair是淘宝的一个开源项目,它是一个分布式的key/value结构数据的解决方案。算法
做为一个分布式系统,Tair由一个中心控制节点(config server)和一系列的服务节点(data server)组成,缓存
config server 负责管理全部的data server,并维护data server的状态信息;为了保证高可用(High Available),config server可经过hearbeat 以一主一备形式提供服务安全
client 和 config server的交互主要是为了获取数据分布的对照表,当client启动时获取到对照表后,会cache这张表,而后经过查这张表决定数据存储的节点,因此请求不须要和config server交互,这使得Tair对外的服务不依赖configserver,因此它不是传统意义上的中心节点,也并不会成为集群的瓶颈。并发
config server维护的对照表有一个版本号,每次新生成表,该版本号都会增长。当有data server状态发生变化(好比新增节点或者有节点不可用了)时,configserver会根据当前可用的节点从新生成对照表,并经过数据节点的心跳,将新表同步给data server。当client请求data server时,后者每次都会将本身的对照表的版本号放入response中返回给客client,client接收到response后,会将data server返回的版本号和本身的版本号比较,若是不相同,则主动和config server通讯,请求新的对照表。负载均衡
这使得在正常的状况下,client不须要和configserver通讯,即便config server不可用了,也不会对整个集群的服务形成大的影响。有了config server,client不须要配置data server列表,也不须要处理节点的的状态变化,这使得Tair对最终用户来讲使用和配置都很简单。分布式
data server 对外提供各类数据服务,并以心跳的形式将自身情况汇报给config server;全部的 data server 地位都是等价的。ui
tair 分为持久化和非持久化两种使用方式:spa
对于全部的key,分到Q个桶中,桶是负载均衡和数据迁移的基本单位。config server 根据必定的策略把每一个桶指派到不一样的data server上,由于数据按照key作hash算法,因此能够认为每一个桶中的数据基本是平衡的,保证了桶分布的均衡性, 就保证了数据分布的均衡性。插件
具体说,首先计算Hash(key),获得key所对应的bucket,而后再去config server查找该bucket对应的data server,再与相应的data server进行通讯。也就是说,config server维护了一张由bucket映射到data server的对照表,好比:code
bucket data server
0 192.168.10.1 1 192.168.10.2 2 192.168.10.1 3 192.168.10.2 4 192.168.10.1 5 192.168.10.2
这里共6个bucket,由两台机器负责,每台机器负责3个bucket。客户端将key hash后,对6取模,找到负责的数据节点,而后和其直接通讯。表的大小(行数)一般会远大于集群的节点数,这和consistent hash中的虚拟节点很类似。
假设咱们加入了一台新的机器——192.168.10.3,Tair会自动调整对照表,将部分bucket交由新的节点负责,好比新的表极可能相似下表:
0 192.168.10.1 1 192.168.10.2 2 192.168.10.1 3 192.168.10.2 4 192.168.10.3 5 192.168.10.3
在老的表中,每一个节点负责3个桶,当扩容后,每一个节点将负责2个桶,数据被均衡的分布到全部节点上。
为了加强数据的安全性,Tair支持配置数据的备份数(COPY_COUNT)。好比你能够配置备份数为3,则每一个bucket都会写在不一样的3台机器上。当数据写入一个节点(一般咱们称其为主节点)后,主节点会根据对照表自动将数据写入到其余备份节点,整个过程对用户是透明的。
若是有多个备份,那么对照表将包含多列,好比备份是为3,则表有4列,后面的3列都是数据存储的节点。
当有新节点加入或者有节点不可用时,config server会根据当前可用的节点,从新build一张对照表。数据节点同步到新的对照表时,会自动将在新表中不禁本身负责的数据迁移到新的目标节点。迁移完成后,客户端能够从config server同步到新的对照表,完成扩容或者容灾过程。整个过程对用户是透明的,服务不中断。
当系统增长data server的时候,config server根据负载,协调data server将他们控制的部分桶迁移到新的data server上,迁移完成后调整路由。
注意:
不论是发生故障仍是扩容,每次路由的变动,config server都会将新的配置信息推给data server。在client访问data server的时候,会发送client缓存的路由表的版本号,若是data server发现client的版本号过旧,则会通知client去config server取一次新的路由表。若是client访问某台data server 发生了不可达的状况(该 data server可能宕机了),客户端会主动去config server取新的路由表。
当发生迁移的时候,假设data server A 要把 桶 3,4,5 迁移给data server B。由于迁移完成前,client的路由表没有变化,所以对 3, 4, 5 的访问请求都会路由到A。如今假设 3还没迁移,4 正在迁移中,5已经迁移完成,那么:
位置优先策略还有一个问题,假如只有两个机房,机房1中有100台data server,机房2中只有1台data server。这个时候,机房2中data server的压力必然会很是大,因而这里产生了一个控制参数 _build_diff_ratio(参见安装部署文档),当机房差别比率大于这个配置值时,config server也再也不build新表,机房差别比率是如何计出来的呢?首先找到机器最多的机房,不妨设使RA,data server数量是SA,那么其他的data server的数量记作SB,则机房差别比率=|SA – SB|/SA,由于通常咱们线上系统配置的COPY_COUNT=3,在这个状况下,不妨设只有两个机房RA和RB,那么两个机房什么样的data server数量是均衡的范围呢? 当差别比率小于 0.5的时候是能够作到各台data server负载都彻底均衡的。这里有一点要注意,假设RA机房有机器6台,RB有机器3台,那么差别比率 = 6 – 3 / 6 = 0.5,这个时候若是进行扩容,在机房A增长一台data server,扩容后的差别比率 = 7 – 3 / 7 = 0.57,也就是说,只在机器数多的机房增长data server会扩大差别比率。若是咱们的_build_diff_ratio配置值是0.5,那么进行这种扩容后,config server会拒绝再继续build新表。
Tair中的每一个数据都包含版本号,版本号在每次更新后都会递增。这个特性能够帮助防止数据的并发更新致使的问题。
Version改变的逻辑以下:
version分布式锁
Tair中存在该key,则认为该key所表明的锁已被lock;不存在该key,在未加锁。操做过程和上面类似。业务方能够在put的时候增长expire,已避免该锁被长期锁住。
固然业务方在选择这种策略的状况下须要考虑并处理Tair宕机带来的锁丢失的状况。
Tair还内置了一个插件容器,能够支持热插拔插件。
插件由config server配置,config server会将插件配置同步给各个数据节点,数据节点会负责加载/卸载相应的插件。
插件分为request和response两类,能够分别在request和response时执行相应的操做,好比在put前检查用户的quota信息等。
插件容器也让Tair在功能方便具备更好的灵活性。