普通事情的思考方式
1、行动的主体是谁?
2、行动的对象时谁?
3、行动的行为是什么?
4、行为的描述是什么?
如:护士给病人注射标准剂量的流感疫苗
概念抽象为:
护士(有注射疫苗的行为),病人,流感疫苗(设置剂量)
Tair缓存的思考方式:
1、缓存服务包括那几部分:
一个Tair集群主要包括3个必选模块:configserver、dataserver和client,一个可选模块:invalidserver。
2、缓存服务器中各部分的作用:
2.1 ConfigServer的功能
1) 通过维护和dataserver心跳来获知集群中存活节点的信息
2) 根据存活节点的信息来构建数据在集群中的分布表。
3) 提供数据分布表的查询服务。
4) 调度dataserver之间的数据迁移、复制。
2.2 DataServer的功能
1) 提供存储引擎
2) 接受client的put/get/remove等操作
3) 执行数据迁移,复制等
4) 插件:在接受请求的时候处理一些自定义功能
5) 访问统计
2.3 InvalidServer的功能
1) 接收来自client的invalid/hide等请求后,对属于同一组的集群(双机房独立集群部署方式)做delete/hide操作,保证同一组集群的一致。
2) 集群断网之后的,脏数据清理。
3) 访问统计。
2.4 client的功能
1) 在应用端提供访问Tair集群的接口。
2) 更新并缓存数据分布表和invalidserver地址等。
3) LocalCache,避免过热数据访问影响tair集群服务。
4) 流控
3、缓存服务有哪些应用方式:
Tair经过这两年的发展演进,除了应用于cache缓存外,在存储(持久化)上支持的应用需求也越来越广泛。现在主要有mdb,rdb,ldb三种存储引擎。
3.1 mdb
定位于cache缓存,类似于memcache。
支持k/v存取和prefix操作
3.1.1 mdb的应用场景
在实际业务中,大部分当缓存用(后端有DB之类的数据源)。
也可用做大访问少量临时数据的存储(例如session登录,防攻击统计等)。
集团内绝对多数cache服务都是采用的tair mdb。
3.2 rdb
定位于cache缓存,采用了redis的内存存储结构。
支持k/v,list,hash,set,sortedset等数据结构。
3.2.1 rdb的应用场景
适用于需要高速访问某些数据结构的应用,例如SNS中常见的的粉丝存储就可以采用set等结构;或者存储一个商品的多个属性(hashmap);高效的消息队列(list)等。现在有30个左右的应用在使用rdb服务。
3.3 ldb
定位于高性能存储,并可选择内嵌mdb cache加速,这种情况下cache与持久化存储的数据一致性由tair进行维护。
支持k/v,prefix等数据结构。今后将支持list,hash,set,sortedset等redis支持的数据结构。
3.3.1 ldb的应用场景
存储,里面可以细分如下场景:
1) 持续大数据量的存入读取,类似淘宝交易快照。
2) 高频度的更新读取,例如计数器,库存等。
3) 离线大批量数据导入后做查询。参见fastdump
也可以用作cache:
数据量大,响应时间敏感度不高的cache需求可以采用。例如天猫实时推荐。
4、 缓存系统中的基本概念
4.1 configID
唯一标识一个tair集群,每个集群都有一个对应的configID,在当前的大部分应用情况下configID是存放在diamond中的,对应了该集 群的configserver地址和groupname。业务在初始化tairclient的时候需要配置此ConfigID。
4.2 namespace
又称area, 是tair中分配给应用的一个内存或者持久化存储区域, 可以认为应用的数据存在自己的namespace中。
同一集群(同一个configID)中namespace是唯一的。
通过引入namespace,我们可以支持不同的应用在同集群中使用相同的key来存放数据,也就是key相同,但内容不会冲突。一个namespace
下是如果存放相同的key,那么内容会受到影响,在简单K/V形式下会被覆盖,rdb等带有数据结构的存储引擎内容会根据不同的接口发生不同的变化。
4.3 quota配额
对应了每个namespace储存区的大小限制,超过配额后数据将面临最近最少使用(LRU)的淘汰。
持久化引擎(ldb)本身没有配额,ldb由于自带了mdb cache,所以也可以设置cache的配额。超过配额后,在内置的mdb内部进行淘汰。
4.3.1 配额是怎样计算的
配额大小直接影响数据的命中率和资源利用效率,业务方需要给出一个合适的值,通常的计算方法是评估在保证一定命中率情况下所需要的记录条数,这样配额大小即为: 记录条数 * 平均单条记录大小。
4.3.2 管理员如何配置配额
单份数据情况下,业务提供的配额就是需要配置在Tair系统中的配额。但对于多备份,在系统中实际计算的配额为: 业务配额 * 备份数
4.4 expireTime:过期时间
expiredTime
是指数据的过期时间,当超过过期时间之后,数据将对应用不可见,这个设置同样影响到应用的命中率和资源利用率。不同的存储引擎有不同的策略清理掉过期的数
据。调用接口时,expiredTime单位是秒,可以是相对时间(比如:30s),也可以是绝对时间(比如:当天23时,转换成距1970-1-1
00:00:00的秒数)。
小于0,不更改之前的过期时间
如果不传或者传入0,则表示数据永不过期;
大于0小于当前时间戳是相对时间过期;
大于当前时间戳是绝对时间过期;
4.5 version
Tair中存储的每个数据都有版本号,版本号在每次更新后都会递增,相应的,在Tair put接口中也有此version参数,这个参数是为了解决并发更新同一个数据而设置的,类似于乐观锁。
很多情况下,更新数据是先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,然后在其基础上修改,也可以选择强行更新。
4.5.1 如何获取到当前key的version
get接口返回的是DataEntry对象,该对象中包含get到的数据的版本号,可以通过getVersion()接口获得该版本号。在put时,将该
版本号作为put的参数即可。 如果不考虑版本问题,则可设置version参数为0,系统将强行覆盖数据,即使版本不一致。
4.5.2 version是如何改变的
Version改变的逻辑如下:
1) 如果put新数据且没有设置版本号,会自动将版本设置成1。
2) 如果put是更新老数据且没有版本号,或者put传来的参数版本与当前版本一致,版本号自增1。
3) 如果put是更新老数据且传来的参数版本与当前版本不一致,更新失败,返回VersionError。
4) put时传入的version参数为0,则强制更新成功,版本号自增1。
4.5.3 version返回不一致的时候,该如何处理
如果更新所基于的version和系统中当前的版本不一致,则服务器会返回ResultCode.VERERROR。
这时你可以重新get数据,然后在新版本的数据上修改;或者设置version为0重新请求,以达到强制更新的效果,应用可以根据自身对数据一致性的要求
在这两种策略间进行选择。
4.5.4 version具体使用案例
如果应用有10个client会对key进行并发put,那么操作过程如下:
1) get key。如果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写入也会成功,这样造成了冲突
4.5.5 version分布式锁
Tair中存在该key,则认为该key所代表的锁已被lock;不存在该key,在未加锁。操作过程和上面相似。业务方可以在put的时候增加expire,已避免该锁被长期锁住。
当然业务方在选择这种策略的情况下需要考虑并处理Tair宕机带来的锁丢失的情况。
4.5.6 什么情况下需要使用version
业务对数据一致性有较高的要求,并且访问并发高,那么通过version可以避免数据的意外结果。
如果不关心并发,那么建议不传入version或者直接传0。