正文
但是presharding的缺点也很明显,做不到动态增容减容,而且无法高可用。不过其实只要稍加改造,就足以满足需求了。
不过,在谈具体的改造措施之前,我们先看之前提出的分片方案要解决的第二个问题——
work distribution
。
这个问题实际上是从另一种维度看分片,解决方案很多,但是如果从对架构的影响上来看,大概分为两种:
第一种方案的缺点显而易见——在整个架构中增加了额外的间接层,流程中增加了一趟round-trip。如果是像twemproxy或者Codis这种支持高可用的还好,但是github上随便一翻还能找到特别多的没法做到高可用的proxy-based方案,无缘无故多个单点,这样就完全搞不明白sharding的意义何在了。
第二种方案的缺点,小说君能想到的就是集群状态发生变化的时候没法即时通知到dbClient。
第一种方案,我们其实可以直接pass掉了。因为这种方案更适合私有云的情景,开发数据服务的部门有可能和业务部门相去甚远,因此需要统一的转发代理服务。但是对于一些简单的应用开发情景,数据服务逻辑服务都是一帮人写的,没什么增加额外中间层的必要。
那么,看起来只能选择第二种方案了。
将presharding与client sharding结合起来后,现在我们的成果是:数据服务是全局的,redis可以开多个实例,不相干的数据需要到不同的分片上存取,dbClient掌握这个映射关系。
不过目前的方案只能算是满足了应用对数据服务的基本需求。
游戏行业中,大部分采用redis的团队,一般最终会选定这个方案作为自己的数据服务。后续的扩展其实对他们来说不是不可以做,但是可能有维护上的复杂性与不确定性。
但是作为一名有操守的程序员,小说君选择继续扩展。
现在的这个方案存在两个问题:
-
首先,虽然我们没有支持在线数据迁移的必要,但是离线数据迁移是必须得有的,毕竟presharding做不到万无一失。而在这个方案中,如果用单纯的哈希算法,增加一个shard会导致原先的key到shard的对应关系变得非常乱,抬高数据迁移成本。
-
其次,分片方案固然可以将整个数据服务的崩溃风险分散在不同shard中,比如相比于不分片的数据服务,一台机器挂掉了,只影响到一部分client。但是,我们理应可以对数据服务做更深入的扩展,让其可用程度更强。
针对第一个问题,处理方式跟proxy-based采用的处理方式没太大区别,由于目前的数据服务方案比较简单,采用一致性哈希即可。或者采用一种比较简单的两段映射,第一段是静态的固定哈希,第二段是动态的可配置map。前者通过算法,后者通过map配置维护的方式,都能最小化影响到的key集合。
而对于第二个问题,解决方案就是实现高可用。
如何让数据服务高可用?在
讨论这个问题之前,我们首先看redis如何实现「
可用性
」。
对于redis来说,可用性的本质是什么?其实就是redis实例挂掉之后可以有后备节点顶上。
redis通过两种机制支持这一点。
第一种机制是replication。
通常的replication方案主要分为两种。
redis的replication方案采用的是一种一致性较弱的active-passive方案。也就是master自身维护log,将log向其他slave同步,master挂掉有可能导致部分log丢失,client写完master即可收到成功返回,是一种异步replication。
这个机制只能解决节点数据冗余的问题,redis要具有可用性就还得解决redis实例挂掉让备胎自动顶上的问题,毕竟由人肉去监控master状态再人肉切换是不现实的。 因此还需要第二种机制。
第二种机制是redis自带的能够自动化fail-over的redis sentinel。reds sentinel实际上是一种特殊的redis实例,其本身就是一种高可用服务——可以多开,可以自动服务发现(基于redis内置的pub-sub支持,sentinel并没有禁用掉pub-sub的command map),可以自主leader election(基于
raft算法