CREATED 2021 11 13 12:15PM
Single-Leader
集群中有一个leader节点和多个follower节点,写操作都必须经过leader节点完成,读操作则都可以处理。
同步复制 (Synchronous Replication)
数据在副本上落盘才返回成功。
- 优点:保证在副本上的数据有最新数据。
- 缺点:延迟高,响应慢,如果有节点宕机则整个系统被halt,所有写请求无法再进行。
异步复制 (Asynchronous Replication)
数据不保证在副本上落盘。
- 优点:延迟低
- 缺点:不能保证在副本上的数据最新,主节点宕机有可能有数据丢失风险。
读自己的写(reading your own writes)
用户在写入数据不久就马上查看数据,而新数据并未到达从节点,这样在用户看来可能读到了旧的数据。这样情况需要"写后读一致性"(Read-after-write consistency),该机制保证用户能读到的自己最近更新的数据,但是对其他用户写的数据则没有保证。
解决方法
(1) 访问用户自己的主页信息通过主节点,而访问其他用户的主页信息则走的从节点。
(2) 更新后一分钟之内的请求都走的主节点。
(3) 客户端可以记住自己最近更新数据的时间戳,在请求数据时带上时间戳,如果副本上没有至少包含该时间戳的数据则转发给其他副本处理或者直到副本本身收到更新能处理为止。
(4) 如果副本分布在多数据中心,必须将请求路由到主节点所在的数据中心。
单调读(Monotonic reads)
用户的多个读请求被分配到了不同的followers(replicas)上,导致读的数据前后不一致,甚至出现重新读到旧数据的倒退现象。
解决方法
同一用户的读请求总是到同一对应的replica上,如果outage,则reroute到另一个replica上。
前缀一致读 (Consistent prefix reads)
前缀一致性读保证,对于一系列按照某个顺序发生的写请求,读取这些内容时也会按照当时写入的顺序来保证顺序的正确性。
在分布式数据库中,不同的分区独立运行,因此全局写入不保证顺序性。
解决方法
确保任何具有因果顺序关系的写入都交给同一个partition来完成,但实际上效率并不高。
Leaderless
无主从节点结构,例如Dyanmo, Cassandra。
读和写请求都发到不同节点上同时进行。(Write and read requests are sent to several nodes in parallel.)
Dyanmo采用两种模式:Read repair 和 anti-entropy process,来修复一些replica上未更新的旧数据。
Quorum
如果有n个副本,写入需要w个节点确认,读取必须至少查询r个节点,则只需要w+r>n,那么读取的节点中一定包含最新的数据。
通常n为奇数,w = r = (n + 1)/2。
最后写入者胜出(Last Write Win,简称LWW)
为每个写请求附件一个时间戳,然后选择最新即最大的时间戳,丢弃较早时间戳的写入,这种方案称为Last Write Win。
这种方案的问题在于:物理时间本身就不可信任,一个机器上的时间到了另一个机器上并不就精准。另外,牺牲了部分的写入数据,比如某客户端写入时返回成功,但是会被后面并发写入但是被认为更晚时间的写入给覆盖掉,这样这部分认为写入成功的数据就丢失了。
要确保LWW无副作用的唯一办法是:只写入一次,然后写入值视为不可变。这样就能避免对同一个主键的覆盖。例如Cassandra的推荐使用方式是使用UUID做为主键,这样能确保是系统唯一的主键。
Reference
[1] https://www.codedump.info/post/20181118-ddia-chapter05-replication/