Tair 分布式key/value存储系统
Tair 简介:https://www.alibabacloud.com/help/zh/doc-detail/145957.htm?concept-2352921
Tair Github:https://github.com/alibaba/tair
Tair 是一个Key/Value结构数据的NoSQL数据库解决方案,它默认支持基于内存和文件的两种存储方式,分别与缓存和持久化存储对应。Tair的功能是get、put、delete以及批量接口。
Tair作为一个分布式系统,由一个中心控制节点(config server)和一系列的服务节点(data server)组成,
1)config server
负责管理所有的data server,并维护data server的状态信息;
为了保证高可用(High Available),config server可通过hearbeat心跳 以一主一备形式提供服务;
2)data server
对外提供各种数据服务,并以hearbeat心跳的形式将自身状况汇报给config server;
所有的 data server 地位都是等价的。
Tair 是阿里巴巴集团开发,已开源,授权协议为GPLv2,开发语言 C/C++,操作系统为 Linux
Tair 项目背景
阿里云数据库Redis企业版(又称阿里云Tair),是基于阿里集团内部使用的Tair产品研发的云上托管键值对缓存服务,从2009年开始正式承载集团缓存业务,历经天猫双十一、优酷春晚、菜鸟、高德等业务场景的磨练,是一款真正的企业级缓存服务产品。
阿里云Tair是阿里云数据库Redis企业版,是基于阿里集团内部使用的Tair产品研发的云上托管键值对缓存服务。Tair作为一个高可用、高性能的分布式NoSQL数据库,专注于多数据结构的缓存与高速存储场景,完全兼容Redis协议。相比云Redis社区版,Tair支持更强的性能、更多的数据结构和更灵活的存储方式。专属集群内Redis实例和Redis实例一样。云数据库专属集群MyBase当前支持的Tair系列类型是性能增强系列。
1)Tair 诞生
2004年,淘宝开始应用缓存技术。最先投入应用的是基于前端页面的缓存技术,采用ESI来标识可以加速和不能加速的网页内容片段,有效减少了从服务端抓取整个页面的次数。
随着淘宝网的流量快速增长,数据库的压力与日俱增,基于后端系统的缓存技术应运而生。从服务淘宝详情和验证码等业务的持久化系统TBStore,到初始服务于淘宝用户中心的TDBM等等,后端系统缓存技术经历了多个系统和阶段的演变与积累,到2009年,这些系统、技术经验经过进一步的研发,融合成了阿里巴巴大规模高速存储系统Tair。
如今,基于Tair演进的Redis企业版已经是阿里巴巴集团调用量最大的系统之一,在多年的阿里巴巴双十一全球狂欢节上提供了核心的在线访问加速能力,承受住了每秒数亿次的调用。
2)Tair 发展
时间 | 事件 |
---|---|
2019年11月 | Tair 3.0,即云数据库Redis企业版,在集团内外同步上线。 |
2019年04月 | KVStore团队在Redis开源社区贡献排名前三,并在RedisConf 2019上发表了公开演讲。 |
2018年08月 | KVStore在中国率先推出混合存储实例,冷热数据分离,有效降低大客户使用成本。 |
2017年11月 | Tair 热点散列经过双十一考验,解决了业内的缓存热点难题。 |
2017年04月 | Tair 2.0上线,开始支持高德、优酷新BU。云上OCS全面升级为KVStore。 |
2016年08月 | Tair智能运维平台上线,助力2016双十一迈入千亿时代。 |
2015年03月 | Tair推出阿里云KVStore,即云数据库Redis版,真正进入了云时代。 |
2014年05月 | Tair推出阿里云上缓存产品OCS,成为阿里云初始的基础产品之一,服务云上Memcache用户。 |
2013年04月 | Fastdump系统落地,大幅度降低导入时间和访问延时。Tair在阿里妈妈获得规模化应用。 |
2012年10月 | 推出RDB缓存引擎,引入类Redis接口,支持更灵活、复杂的数据结构。 |
2011年06月 | 上线LDB持久化引擎,满足互联网KV存储需求。 |
2009年11月 | Tair的第一个双十一,正式开始支撑超大流量场景。 |
2009年04月 | Tair 1.0正式诞生,并被应用于淘宝核心系统、MDB缓存、用户中心等业务。 |
3)Tair 产品类型及特性
随着互联网的高速发展,业务场景变得越来越丰富和复杂,Redis企业版作为一个高可用、高性能的分布式NoSQL数据库,推出了多种不同形态的产品,为您提供更强的性能、更多的数据结构和更灵活的存储方式,满足不同场景下的业务需求。
Redis企业版产品类型 | 特性 |
---|---|
性能增强型 |
性能存储型采用多线程模型,性能约为同规格社区版实例的3倍。 性能存储型提供多种增强型数据结构模块(modules),包括TairString(含CAS和CAD命令)、TairHash、TairGIS、TairBloom以及TairDoc,使业务无需再关心存储的结构和时效性,能够极大提升业务开发效率。 |
混合存储型 | 混合存储型采用内存加磁盘的存储模式,能够在业务高峰期后对冷热数据进行弹性分离,既保障了热数据的内存访问速度,又提供了远超社区Redis的存储容量,实现了性能与成本的平衡。 |
Tair 功能
Tair是一个Key/Value结构数据的解决方案,它默认支持基于内存和文件的两种存储方式,分别和我们通常所说的缓存和持久化存储对应。
Tair除了普通Key/Value系统提供的功能,比如get、put、delete以及批量接口外,还有一些附加的实用功能,使得其有更广的适用场景,包括:Version支持,原子计数器,Item支持
1)Version支持
Tair中的每个数据都包含版本号,版本号在每次更新后都会递增。这个特性有助于防止由于数据的并发更新导致的问题。其设计原理参考的MySQL 多版本并发控制(MVCC,MultiVersion Concurrency Control)机制,请见米扑博客:MySQL 事务隔离级别和实现原理
比如,系统有一个value为“a,b,c”,A和B同时get到这个value。
A执行操作,在后面添加一个d,value为“a,b,c,d”。
B执行操作添加一个e,value为”a,b,c,e”。
如果不加控制,无论A和B谁先更新成功,它的更新都会被后到的更新覆盖,造成修改数据丢失。
Tair无法解决这个问题,但是引入了version机制避免这样的问题。
还是拿刚才的例子,A和B取到数据,假设版本号为10,A先更新,更新成功后,value为”a,b,c,d”,与此同时,版本号会变为11。当B更新时,由于其基于的版本号是10,服务器会拒绝更新,从而避免A的更新被覆盖。B可以选择get新版本的value,然后在其基础上修改,也可以选择强行更新。
2)原子计数器
Tair从服务器端支持原子的计数器操作,这使得Tair成为一个简单易用的分布式计数器。
3)Item支持
Tair还支持将value视为一个item数组,对value中的部分item进行操作。比如有个key的value为[1,2,3,4,5],我们可以只获取前两个item,返回[1,2],也可以删除第一个item,还支持将数据删除,并返回被删除的数据,通过这个接口可以实现一个原子的分布式FIFO的队列。
Tair 设计原理架构
1)Tair 整体架构图
一个Tair集群主要包括client、config server、data server 3个模块。
Config server通过和data server的心跳(HeartBeat)维护集群中可用的节点,并根据可用的节点,构建数据的在集群中的分布信息(见下文的对照表)。
Client在初始化时,从Config server处获取数据的分布信息,根据分布信息和相应的Data server交互完成用户的请求。
Data server负责数据的存储,并按照Config server的指示完成数据的复制和迁移工作。
2)数据的分布
分布式系统需要解决的一个重要问题便是决定数据在集群中的分布策略,好的分布策略应该能将数据均衡地分布到所有节点上,并且还应该能适应集群节点的变化。Tair采用的对照表方式较好地满足了这两点。
对照表的行数是一个固定值,这个固定值应该远大于一个集群的物理机器数,由于对照表是需要和每个使用Tair的客户端同步的,所以不能太大,不然同步将带来较大的开销。我们在生产环境中的行数一般为1023 。
3)对照表简介
下面我们看对照表是怎么完成数据的分布功能的,为了方便,我们这里假设对照表的行数为6。
最简单的对照表包含两列,第一列为hash值,第二列为负责该hash值对应数据的data server节点信息。
比如我们有两个节点192.168.10.1和192.168.10.2,那么对照表类似:
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 |
首先,当客户端接收到请求后,将key的hash值和6取模,得到 0 ~ 5,
然后,根据取模后的结果查找对照表。比如取模后的值为3,客户端将和192.168.10.2通信。
4)对照表如何适应节点数量的变化
我们假设新增了一个节点——192.168.10.3,当config server发现新增的节点后,会重新构建对照表。
构建依据以下两个原则:
(1)数据在新表中均衡地分布到所有节点上。
(2)尽可能地保持现有的对照关系。
更新之后的对照表如下所示:
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 |
这里将原本由192.168.10.1负责的4和192.168.10.2负责的5交由新加入的节点192.168.10.3负责。
如果是节点不可用,则相当于上述过程反过来,即剔除掉192.168.10.3,重新换上192.168.10.1 和 192.168.10.2,道理是一样的。
5)多备份的支持
Tair支持自定义的备份数,比如你可以设置数据备份为2,以提高数据的可靠性。
对照表可以很方便地支持这个特性。
我们以行数为6,两个节点为例,2个备份的对照表类似:
0 | 192.168.10.1 | 192.168.10.2 |
1 | 192.168.10.2 | 192.168.10.1 |
2 | 192.168.10.1 | 192.168.10.2 |
3 | 192.168.10.2 | 192.168.10.1 |
4 | 192.168.10.1 | 192.168.10.2 |
5 | 192.168.10.2 | 192.168.10.1 |
如上表,第二列为主节点的信息,第三列为辅节点信息。
在Tair中,客户端的读写请求都是和主节点交互,所以如果一个节点不做主节点,那么它就退化成单纯的备份节点。因此,多备份的对照表在构建时需要尽可能保证各个节点作为主节点的个数相近。
当有节点不可用时,如果是辅节点,那么config server会重新为其指定一个辅节点,如果是持久化存储,还将复制数据到新的辅节点上。如果是主节点,那么config server首先将辅节点提升为主节点,对外提供服务,并指定一个新的辅节点,确保数据的备份数。
6)多机架和多数据中心的支持
对照表在构建时,可以配置将数据的备份分散到不同机架或数据中心的节点上。
Tair当前通过设置一个IP掩码来判断机器所属的机架和数据中心信息。
比如你配置备份数为3,集群的节点分布在两个不同的数据中心A和B,则Tair会确保每个机房至少有一份数据。假设A数据中心包含两份数据时,Tair会尽可能将这两份数据分布在不同机架的节点上。这可以减少整个数据中心或某个机架发生故障是数据丢失的风险。
轻量级的 Config Server
从Tair的整体架构图上看,Config server很类似传统分布式集群中的中心节点。整个集群服务都依赖于Config server的正常工作。
但Tair的Config server却是一个轻量级的中心节点,在大部分时候,Config server不可用对集群的服务是不造成影响的。
client 和 config server的交互主要是为了获取数据分布的对照表,当client启动时获取到对照表后,会在本地cache缓存这张表,然后通过查这张表决定数据存储的节点,所以请求不需要和config server交互,这使得Tair对外的服务不依赖configserver,所以Config server又不是传统意义上的中心节点,也并不会成为集群的瓶颈。
Tair用户和Config server的交互主要是为了获取数据分布的对照表,当client获取到对照表后,会cache这张表,然后通过查这张表决定数据存储的节点,所以请求不需要和configserver交互,这使得Tair对外的服务不依赖configserver,所以它不是传统意义上的中心节点。
Config server维护的对照表有一个版本号,每次新生成表,该版本号都会增加。当有数据节点状态发生变化(比如新增节点或者有节点不可用了)时,Config server会根据当前可用的节点重新生成对照表,并通过数据节点的心跳,将新表同步给数据节点。
当客户端请求数据节点时,数据节点每次都会将自己的对照表的版本号放入response中返回给客户端,客户端接收到response后,会将数据节点返回的版本号和自己的版本号比较,如果不相同,则主动和configserver通信,请求新的对照表。
所以,client客户端也不需要和Config server保持心跳,以便及时地更新对照表。这使得在正常的情况下,客户端不需要和Config server通信,即使Config server不可用了,也不会对整个集群的服务造成大的影响。这使得在正常的情况下,client不需要和configserver通信,即使config server不可用了,也不会对整个集群的服务造成大的影响。有了config server,client不需要配置data server列表,也不需要处理节点的的状态变化,这使得Tair对最终用户来说使用和配置都很简单。
仅有当Config server不可用,此时有客户端需要初始化,那么客户端将取不到对照表信息,这将使得客户端无法正常工作。
1)容灾
当有某台data server故障不可用的时候,config server会发现这个情况,config server负责重新计算一张新的桶在data server上的分布表,将原来由故障机器服务的桶的访问重新指派到其它有备份的data server中。这个时候,可能会发生数据的迁移,比如原来由data server A负责的桶,在新表中需要由 B负责,而B上并没有该桶的数据,那么就将数据迁移到B上来。同时,config server会发现哪些桶的备份数目减少了,然后根据负载情况在负载较低的data server上增加这些桶的备份。
2)扩容
当系统增加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取新的路由表。
3)迁移
当发生迁移的时候,假设data server A 要把 桶 3,4,5 迁移给data server B。因为迁移完成前,client的路由表没有变化,因此对 3, 4, 5 的访问请求都会路由到A。现在假设 3还没迁移,4 正在迁移中,5已经迁移完成,那么:
- 如果是对3的访问,则没什么特别,跟以前一样;
- 如果是对5的访问,则A会把该请求转发给B,并且将B的返回结果返回给client;
- 如果是对4的访问,在A处理,同时如果是对4的修改操作,会记录修改log,桶4迁移完成的时候,还要把log发送到B,在B上应用这些log,最终A B上对于桶4来说,数据完全一致才是真正的迁移完成;
Data Server 内部结构
Data Server负责数据的物理存储,并根据Config server构建的对照表完成数据的复制和迁移工作。
Data Server具备抽象的存储引擎层,可以很方便地添加新存储引擎。
Data Server还有一个插件容器,支持热插拔,可以动态地加载/卸载插件。
1)插件容器
Data Server 的内部结构示意图
插件容器也让Tair在功能方便具有更好的灵活性。
插件由Config server配置,Config server会将插件配置同步给各个数据节点,数据节点会负责加载/卸载相应的插件。
插件分为request和response两类,可以分别在request和response时执行相应的操作,比如在put前检查用户的quota信息等。
2)抽象的存储引擎层
Tair的存储引擎有一个抽象层,只要满足存储引擎需要的接口,便可以很方便地替换Tair底层的存储引擎。
比如你可以很方便地将bdb、tc甚至MySQL作为Tair的存储引擎,而同时使用Tair的分布方式、同步等特性。
Tair默认包含两个存储引擎:mdb 和 fdb
(1)mdb是一个高效的缓存存储引擎(内存高速存储),它有着和memcached类似的内存管理方式。mdb支持使用share memory,这使得我们在重启Tair数据节点的进程时不会导致数据的丢失,从而使升级对应用来说更平滑,不会导致命中率的较大波动。
(2)fdb是一个简单高效的持久化存储引擎(磁盘文件存储),使用树的方式根据数据key的hash值索引数据,加快查找速度。索引文件和数据文件分离,尽量保持索引文件在内存中,以便减小IO开销。使用空闲空间池管理被删除的空间。
3)自动的复制和迁移
为了增强数据的安全性,Tair支持配置数据的备份数。比如你可以配置备份数为3,则每个数据都会写在不同的3台机器上。得益于抽象的存储引擎层,无论是作为cache的mdb,还是持久化的fdb,都支持可配的备份数。
当数据写入一个节点(通常我们称其为主节点)后,主节点会根据对照表自动将数据写入到其他备份节点,整个过程对用户是透明的。
当有新节点加入或者有节点不可用时,Config server会根据当前可用的节点,重新build一张对照表。数据节点同步到新的对照表时,会自动将在新表中不由自己负责的数据迁移到新的目标节点。迁移完成后,客户端可以从Config server同步到新的对照表,完成扩容或者容灾过程。整个过程对用户是透明的,服务不中断。
Tair 存储引擎
tair 分为持久化和非持久化两种使用方式:
1)非持久化的 tair 可以看成是一个分布式缓存;
2)持久化的 tair 将数据存放于磁盘中,为了解决磁盘损坏导致数据丢失,tair 可以配置数据的备份数目。tair 自动将一份数据的不同备份放到不同的主机上,当有主机发生异常,无法正常提供服务的时候,其余的备份会继续提供服务。
Tair的存储引擎有一个抽象层,只要满足存储引擎需要的接口,便可以很方便的替换Tair底层的存储引擎。比如你可以很方便的将bdb、tc、redis、leveldb甚至MySQL作为Tair的存储引擎,而同时使用Tair的分布方式、同步等特性。
Tair主要有下面三种存储引擎:
1)mdb,定位于cache缓存,类似于memcache。支持k/v存取和prefix操作;
2)rdb,定位于cache缓存,采用了redis的内存存储结构。支持k/v string,list,hash,set,sortedset等数据结构;
3)ldb,定位于高性能存储,采用了levelDB作为引擎,并可选择内嵌mdb cache加速,这种情况下cache与持久化存储的数据一致性由tair进行维护。支持k/v,prefix等数据结构。今后将支持list,hash,set,sortedset等redis支持的数据结构。
1)MDB流程
slab 是Linux操作系统的一种内存分配机制,slab分配算法采用cache 存储内核对象。slab 缓存、从缓存中分配和释放对象,然后销毁缓存的过程,必须要定义一个 kmem_cache 对象,然后对其进行初始化这个特定的缓存包含 32 字节的对象
Linux 的slab 可有三种状态:
-
满的:slab 中的所有对象被标记为使用。
-
空的:slab 中的所有对象被标记为空闲。
-
部分:slab 中的对象有的被标记为使用,有的被标记为空闲。
slab 分配器首先从部分空闲的slab 进行分配。如有,则从空的slab 进行分配。如没有,则从物理连续页上分配新的slab,并把它赋给一个cache ,然后再从新slab 分配空间。
2)RDB流程
3)LDB流程
4)fastdump
大数据量导入:数据预排序,按桶分memtable。
Tair 分布式策略
tair 的分布式采用的是一致性哈希算法,对于所有的key,分到Q个桶中,桶是负载均衡和数据迁移的基本单位。
config server 根据一定的策略把每个桶指派到不同的data server上,因为数据按照key做hash算法,所以可以认为每个桶中的数据基本是平衡的,保证了桶分布的均衡性, 就保证了数据分布的均衡性。
具体说,首先计算Hash(key),得到key所对应的bucket,然后再去config server查找该bucket对应的data server,再与相应的data server进行通信。也就是说,config server维护了一张由bucket映射到data server的对照表,比如:
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个桶,数据被均衡的分布到所有节点上。
如果有多个备份,那么对照表将包含多列,比如备份是为3,则表有4列,后面的3列都是数据存储的节点。
为了增强数据的安全性,Tair支持配置数据的备份数(COPY_COUNT)。比如你可以配置备份数为3,则每个bucket都会写在不同的3台机器上。当数据写入一个节点(通常我们称其为主节点)后,主节点会根据对照表自动将数据写入到其他备份节点,整个过程对用户是透明的。
当有新节点加入或者有节点不可用时,config server会根据当前可用的节点,重新build一张对照表。数据节点同步到新的对照表时,会自动将在新表中不由自己负责的数据迁移到新的目标节点。迁移完成后,客户端可以从config server同步到新的对照表,完成扩容或者容灾过程。整个过程对用户是透明的,服务不中断。
为了更进一步的提高数据的安全性,Tair的config server在build对照表的时候,可以配置考虑机房和机架信息。比如你配置备份数为3,集群的节点分布在两个不同的机房A和B,则Tair会确保每个机房至少有一份数据。当A机房包含两份数据时,Tair会确保这两份数据会分布在不同机架的节点上。这可以防止整个机房发生事故和某个机架发生故障的情况。这里提到的特性需要节点物理分布的支持,当前是通过可配置的IP掩码来区别不同机房和机架的节点。
Tair 提供了两种生成对照表的策略:
1)负载均衡优先,config server会尽量的把桶均匀的分布到各个data server上,所谓尽量是指在不违背下面的原则的条件下尽量负载均衡:每个桶必须有COPY_COUNT份数据; 一个桶的各份数据不能在同一台主机上;
2)位置安全优先,一般我们通过控制 _pos_mask(Tair的一个配置项) 来使得不同的机房具有不同的位置信息,一个桶的各份数据不能都位于相同的一个位置(不在同一个机房)。
位置优先策略还有一个问题,假如只有两个机房,机房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 一致性和可靠性
分布式系统中的可靠性和一致性是无法同时保证的,因为我们必须允许网络错误的发生。tair 采用复制技术来提高可靠性,并且为了提高效率做了一些优化。事实上在没有错误发生的时候,tair 提供的是一种强一致性,但是在有data server发生故障的时候,客户有可能在一定时间窗口内读不到最新的数据,甚至发生最新数据丢失的情况。
version
Tair中的每个数据都包含版本号,版本号在每次更新后都会递增。
这个特性可以帮助防止数据的并发更新导致的问题。
如何获取到当前key的version?
get接口返回的是DataEntry对象,该对象中包含get到的数据的版本号,可以通过getVersion()接口获得该版本号。
在put时,将该版本号作为put的参数即可。 如果不考虑版本问题,则可设置version参数为0,系统将强行覆盖数据,即使版本不一致。
很多情况下,更新数据是先get,然后修改get回来的数据,再put回系统。如果有多个客户端get到同一份数据,都对其修改并保存,那么先保存的修改就会被后到达的修改覆盖,从而导致数据一致性问题,在大部分情况下应用能够接受,但在少量特殊情况下,这个是我们不希望发生的。
比如系统中有一个值”1”, 现在A和B客户端同时都取到了这个值。之后A和B客户端都想改动这个值,假设A要改成12,B要改成13,如果不加控制的话,无论A和B谁先更新成功,它的更新都会被后到的更新覆盖。Tair引入的version机制避免了这样的问题。刚刚的例子中,假设A和B同时取到数据,当时版本号是10,A先更新,更新成功后,值为12,版本为11。当B更新的时候,由于其基于的版本号是10,此时服务器会拒绝更新,返回version error,从而避免A的更新被覆盖。B可以选择get新版本的value,然后在其基础上修改,也可以选择强行更新。
Version改变的逻辑如下:
1)如果put新数据且没有设置版本号,会自动将版本设置成1;
2)如果put是更新老数据且没有版本号,或者put传来的参数版本与当前版本一致,版本号自增1;
3)如果put是更新老数据且传来的参数版本与当前版本不一致,更新失败,返回VersionError;
4)put时传入的version参数为0,则强制更新成功,版本号自增1。
version具体使用案例,如果应用有10个client会对key进行并发put,那么操作过程如下:
1)get key,如果成功,则进入步骤2;如果数据不存在,则进入步骤3;
2)在调用put的时候将get key返回的verison重新传入put接口,服务端根据version是否匹配来返回client是否put成功;
3)get key数据不存在,则新put数据。此时传入的version必须不是0和1,其他的值都可以(例如1000,要保证所有client是一套逻辑)。因为传入0,tair会认为强制覆盖;而传入1,第一个client写入会成功,但是新写入时服务端的version以0开始计数啊,所以此时version也是1,所以下一个到来的client写入也会成功,这样造成了冲突(不是很理解了,TODO)
version 分布式锁
Tair中存在该key,则认为该key所代表的锁已被lock;不存在该key,在未加锁。
操作过程和上面相似。业务方可以在put的时候增加expire,已避免该锁被长期锁住。
当然,业务方在选择这种策略的情况下,需要考虑并处理Tair宕机带来的锁丢失的情况。
Tair 集群的基本概念
一个Tair集群主要包括3个必选模块:configserver、dataserver和client,一个可选模块:invalidserver
通常情况下,一个Tair集群中包含2台configserver及多台dataServer。
1)两台configserver互为主备并通过维护和dataserver之间的心跳获知集群中存活可用的dataserver,构建数据在集群中的分布信息(对照表)。
2)dataserver负责数据的存储,并按照configserver的指示完成数据的复制和迁移工作。client在启动的时候,从configserver获取数据分布信息,根据数据分布信息和相应的dataserver交互完成用户的请求。
3)invalidserver主要负责对等集群的删除和隐藏操作,保证对等集群的数据一致。
从架构上看,config server的角色类似于传统应用系统的中心节点,整个集群服务依赖于configserver的正常工作。但实际上相对来说,tair的configserver是非常轻量级的,当正在工作的服务器宕机的时候另外一台会在秒级别时间内自动接管。而且,如果出现两台服务器同时宕机的最恶劣情况,只要应用服务器没有新的变化,tair依然服务正常,因为client都在自己本地保存有一份对照表,不必每次依赖config server获取对照表。而有了configserver这个中心节点,带来的好处就是应用在使用的时候只需要配置configserver的地址(现在可以直接配置Diamond key),而不需要知道内部节点的情况。
双机房单集群单备份数是指,该Tair集群部署在两个机房中(Tair集群的机器分别在两个机房), 数据存储份数为1, 该类型集群部署示意图如下所示。数据服务器(Dataserver)分布在两个机房中,他们都属于同一集群。
Tair 配置参数
1)configID
唯一标识一个tair集群,每个集群都有一个对应的configID,在当前的大部分应用情况下configID是存放在diamond中的,对应了该集群的configserver地址和groupname。业务在初始化tair client的时候需要配置此ConfigID。
2)namespace,又称area
namespace是tair中分配给应用的一个内存或者持久化(磁盘)存储区域, 可以认为应用的数据存在自己的namespace中。 同一集群(同一个configID)中namespace是唯一的。通过引入namespace,我们可以支持不同的应用在同集群中使用相同的key来存放数据,也就是key相同但前缀不同(类似于命令空间 com.namespace_xx.keyname),但内容不会冲突。一个namespace下是如果存放相同的key,那么内容会受到影响,在简单K/V形式下会被覆盖,rdb等带有数据结构的存储引擎内容会根据不同的接口发生不同的变化。
3)quota 配额
quota配额,对应了每个namespace储存区的大小限制,超过配额后数据将面临最近最少使用(LRU)的淘汰。持久化引擎(ldb)本身没有配额,ldb由于自带了mdb cache,所以也可以设置cache的配额。超过配额后,在内置的mdb内部进行淘汰。
4)expireTime
expireTime 数据的过期时间。当超过过期时间之后,数据将对应用不可见,不同的存储引擎有不同的策略清理掉过期的数据。
Tair 客户端
淘宝开源的Tair客户端有C++和Java两个版本
Tair Server端是C++写的,因为Server和客户端之间使用socket通信,理论上只要可以实现socket操作的语言都可以直接实现成tair客户端。目前实际提供的客户端有Java 和 C++, 客户端只需要知道config server的位置信息就可以享受tair集群提供的服务了。
Tair如果作为存储层,前端肯定还需部署Nginx这样的web服务器,以Nginx为例,淘宝似乎还没有开源其tair模块,也没有公布tair的lua插件,如果想在Nginx里面访问tair,目前似乎还没有什么办法了,除非自己去开发一个模块。
Tair 未来
我们将Tair开源,希望有更多的用户能从我们开发的产品中受益,更希望依托社区的力量,使Tair有更广阔的发展空间。
Tair开源后,有很多用户关心我们是否会持续维护这个项目。我们阿里巴巴将Tair开源后,淘宝内部已经不再有私有的Tair分支,所有的开发和应用都基于开源分支。Tair在淘宝有非常广的应用,我们内部有一个团队,专门负责Tair的开发和维护,相信我们会和社区一起,将Tair越做越好。
有很多用户在淘宝开源平台上申请加入Tair项目,加入项目在我们的开源平台上意味着成为项目的提交者,可以向代码库直接提交代码。所以我们暂时还没有批准外部用户加入,我们将在大家对Tair有更深入的了解后和社区一起决定是否批准加入项目的申请,在此之前,如果你有对代码的改进,欢迎使用patch的方式提交给我们,我们将在review后决定是否合并到代码库。希望我们能和社区一起,将Tair做成一个真正对大家都有帮助的项目。
本文参考:
参考推荐:
BloomFilter + Redis 大数据去重策略的实现
MySQL 中 insert ignore into, replace into 用法总结
MySQL 中 distinct 和 group by 性能比较
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2021-03-26 05:50:27
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!