1.主从备份
备份数据,可以将数据的变化同步到其它机器或节点上,从而提供更好的读性能以及数据冗余,提高可用性。
1.1.单主
同步或异步备份
单主时每个备份只有一个主节点,其它为从节点。备份时有同步或异步两种不同的方式。同步方式指客户端在写入时,只有从节点也成功收到数据后,主节点才会返回成功。异步方式则只要主节点写入成功后就能立即返回成功,从节点的数据会在后续补充。
异步备份的好处是提高了系统的性能。若有任意从节点性能有问题,那么整个集群的写功能都会受到影响,而异步备份的缺点则在于主写入完成但未主从同步时,主节点下线,则会造成数据丢失的情况。
一般情况下可以设置一个从节点使用同步备份,其余使用异步备份,以此来获得性能与数据完整性的平衡。
主备切换
主备切换时一般有以下几个步骤:首先确定主节点已经挂了,通常通过超时等机制;其次选择一个新的主节点,一般需要选择数据完整的从节点,否则会丢失数据;最后让系统使用新的主节点进行写入。
在主备时有以下一些难点:1. 如果使用了异步备份,那么有可能存在未备份的数据,导致没有从节点是数据完整的;2. 有可能发生脑裂,即多个节点均认为自己的主节点,从而变成多主的情况,损坏数据;3.超时的度如何把握,太长的超时时间将延长系统的恢复时间,太短则又容易因为网络或负载等原因产生不必要的切换,降低集群稳定性。
一致性问题1:读自己的写
客户端写入数据,但在读的过程中有可能由从节点读取数据,但此时从节点可能仍未完成备份,仍然有可能读到写之前的旧数据。
解决方法是通过id判断,若读取的是刚刚写入的实体时,则使用主节点,否则使用从节点来读取其它实体的数据;或在读取时提交上一次写入的时间截,由系统在读取时判断,若从节点的版本早于时间截,则转由主节点或其它节点处理,以保证能读到比写入时更新的数据。
一致性问题2:单调读
读取任意其它客户端写入的数据时,有可能从不同的从节点上读取,而每个从节点备份的进度不同,可能造成每次读取的结果不同,甚至读到倒退的历史。
解决方法是对于同一个id实体,始终从相同的节点上读取数据;但若此节点下线,则不得不从其它节点读。
一致性问题3:前缀一致
若有多次写操作,每次写的是不同的实体(可能在不同的分区中),但实体之间有逻辑关联。此时对于第三方观察者而言,由于读的均是从节点,且从节点之间的备份顺序不同,因此有可能先读到结果实体的数据,然后才能读到问题实体的数据。
解决方法是对于有逻辑顺序的多个实体,让他们被划分到同一分区中。
1.2.多主
多主是指有多个主节点可同时提供写操作。
多主的优缺点
1. 多主时可以提高写性能;2. 可以实现跨数据中心的写场景;3. 协同编辑或线下编辑都属于多主场景。
解决冲突
可以使用以下4种方式解决冲突:1. 给每个写操作分配一个id,id大者获胜。2. 给每个备份分配一个id,以备份id大的数据为准。3. 自动合并多个写操作的结果。4.保留多个写操作的结果,由客户端自行解决冲突。
备份拓扑
主要有树型和点对点两种。树型比较简单,同步代价也比较小,但是存在单点问题,即任意两个节点之间只有一条通路,任何一个节点故障会导致所有通路失效;点对点则指任意两个节点之间都能相互同步,好处是没有单点,坏处是同步的代价较大,使用更多资源。
1.3.无主
无主是指没有主节点,由客户端或协调者同时向所有节点写入数据。通过读写委员会的形式,设定在写入时成功写入w个节点,在读取时至少等待r个节点返回结果,若w+r>n,则能在一般情况下保证每次都能读到最新值。并且在读之后将所有非最新的节点数据写为最新。
无主的优缺点
不存在主备切换或故障恢复等复杂问题;写时可以容忍n-w个节点故障、读时可以容忍n-r个节点故障,可用性均较高。
缺点在于1. 仍然存在多主时的冲突问题,若两个客户端同时写入,则无法判断预测哪个写会生效;2. 如果读和写同时进行,那么写操作的中间过程会影响读操作的结果,有可能写操作最终失败,但仍然被读操作读到;3. 若写操作的成功数量少于w,但写整体失败,那么这些成功的节点也不会回滚,依然可能读到这些数据;4. 如果含有新数据的节点故障且丢失了数据,那么恢复时有可能使用了含有旧数据的节点,此时会使w+r<n,有可能读不到新数据。
2.数据分区
2.1.分区方式
按范围
按范围即通过划定key的范围来进行分区。其优点是可以顺序扫描,但缺点是容易出现热点分区。
按hash
按hash即通过使用hash的方式来进行分区。其优点是可以减少热点的情况,但热点仍然可能存在,并且无法再进行顺序扫描。
对于特定的热点,可以增加随机值来进一步划入不同分区。但在读取时,需要同时读取所有分区,再合并结果。
2.2.索引分区
按文档
即在同一个分区内建索引,在搜索时需要同时查询所有分区内的索引,再合并索引。
好处是比较简单,且不存在跨分区的事务或数据一致性问题;坏处是查询时性能较低,需要查询所有分区。
按词汇
即建立全局索引,包含了所有分区中的数据。但需要注意,索引也可以分布在不同的分区中,即每个分区含有不同term的全量结果。
好处是查询性能较高,可以精确定位;坏处是写时需要同时修改索引所在的分区,往往此类操作是异步的,容易存在数据不一致的情况,导致查询结果延后。
2.3.查询路由
由于数据被分区,因此在查询时需要定位到相应的数据节点上。
一般情况下都会借助外部配置管理工具,如ZK,来管理各个分区在各个节点上的owner情况,然后由客户端直接向目标节点发起请求,或经过集群提供用路由层转发。
此外,也可通过gossip协议等让各个节点传播分区的owner情况,此时客户端可以向任意节点发出请求,节点会自动转发请求到相应目标节点上。