携程持久化KV存储挑战Redis,狂省90%成本( 二 )


调研的过程受制于篇幅限制 , 不再一一展开 , 最终我们继续选择了Kvrocks来作为治理演进的对象 , 其他的NoSQL/NewSQL有各种不足 , 而Kvrocks受益于Redis运维治理的成熟 , 可以复用现有的大部分Redis中间件和运维治理的能力 , 在携程与Redis几乎无差异的部署/使用方式 , 当下无疑是最适合的一种持久化KV数据库 。
四、从Kvrocks到TRocks
经过不断的开发迭代和使用 , 最终我们将新系统命名为TRocks(Trip+Kvrocks) , 作为携程自己的持久化KV数据库 。相比于原来的Kvrocks , 除了与Redis可以互通协议互为主从外 , 主要是基于以下几个方面的改进 。
1、功能增强独占锁
一些业务方存在着流程协调 , 执行顺序的限制 , 往往会需要使用分布式锁 , 比如扣减库存的逻辑 。常见的方式是引入一个第三方的分布式系统 , 将锁标识存储在那里用于共享访问 , 以达到锁的目的 。
这样做虽然常见 , 但也有一些问题 , 首先需要引入额外的系统 , 并单独考虑各种异常情况的处理 , 增加了整个应用的复杂度 。其次标识位往往有一定含义或者能与当前业务数据做关联 , 这就相当于额外存储了一份业务数据 , 存在一定的安全隐患 。同时多个应用可能共用一套外部分布式系统来处理锁 , 这就无形中增加了系统的访问压力 , 一旦出现问题将影响多个依赖方 , 缺乏隔离性 。
为了解决此类问题 , TRocks在内部实现了基于Key力度的锁功能 , 将其分布式部署并作为应用的业务数据库时 , 其本身就拥有了分布式锁的能力(图2) 。对锁的处理和业务数据在一起 , 无需引入多余的系统 , 降低复杂度 , 帮助业务方专注于业务代码的开发 。
携程持久化KV存储挑战Redis,狂省90%成本
文章图片

文章图片
图2
为了保证请求的唯一性和类似raft那样支持幂等重试的功能 , 每个请求需要带上标识唯一性的clientid和自增seq , 这些metadata和本身的data会被当成一个writebatch写入到rocksdb中 , 后续还会同步到slave上 , 从而保证整条链路上请求的原子性 。复合命令
由于Redis命令本身的限制 , 有些业务方反馈实现一个功能 , 比如对hashkey进行超时处理需要进行2次操作 , 一次设置值 , 一次设置超时 。虽然中间件将这层逻辑封装之后对外只提供一个api , 但内部执行仍然是2个命令 , 可能存在原子性问题 。TRocks针对这种情况增加了一些复合功能的命令 , 调用这些命令可以实现相同的效果并保证原子性 , 同时这些功能对用户是透明的 , 直接调用客户端相应api即可使用 。
2、可用性增强可调一致性
Kvrocks本身的主从复制逻辑与Redis相似 , 都是通过异步方式进行的 。在这种方式下 , 如果出现网络断开或者master宕机 , 数据还未来得及同步 , 就会出现数据丢失的情况 。为了避免此类问题 , TRocks加入了类似Mysql的半同步复制来提高数据的一致性 。我们可以通过打开半同步方式并指定至少需要参与的半同步slave的数量来启用该功能 , 提高灾备能力 。
例如一个1主4从的集群 , 设定需要等待任意2台Slave响应 。
如图3所示 , 当满足响应的slave为2的时候 , 半同步即可认为完成 , 即使此时另外两台slave可能还未完成同步工作 。
携程持久化KV存储挑战Redis,狂省90%成本
文章图片

文章图片
图3
但这种方式在多机房部署的情况仍然可能存在问题 。因为距离的关系 , 相同机房的数据传输速率会更高 , 所以master复制到和其在同一个机房的slave通常情况会更快(图4) 。