【Distributed System】Consistency Analysist on Different Middlewares

Posted by 西维蜀黍 on 2021-10-12, Last Modified on 2022-12-10

Redis

需要注意的是,在所有 master-slave 系统中,可以是 CAP 中 CP 系统, 即 C(strong consistency)可以被保证。

CP 系统 - Strong Consistency

Solution 1

即 Master Slave 也可以是强一致性的。

比如:当我们写 Master 的时候,Master 负责先写自己,等成功后,再写 Slave,两者都写成功后,才返回写操作成功。

整个过程是同步的,如果写 Slave 失败了,那么两种方法:

  1. retry
  2. 或者 rollback master 的写入(如果 rollback 失败,则一直 retry)

你可以看到,如果 Master-Slave 需要做成强一致性有多复杂。

Solution 2

开启 Redis 的 AOF appendfsync always,这意味着每一个写操作在写入 memory 后,都会同步的写入 disk 中(不仅仅写入 kernal buffer,因为如果写入 kernal buffer,当突然断电时,强一致性仍然无法被保证)。

而且还要考虑长时间写入 AOF 的 Log Rewriting 问题和 Data Loading 问题,即

  • Log Rewriting 仍然需要耗费大量的 CPU 的
  • Data Loading 问题会导致当 Redis Node 重启后,需要先 Loading Data,这时候 Redis 是无法对外进行工作的。
    • 因而如果 Data 非常大,down time 时间也无法接受

Analysis

显然,这两种 solution 的性能都很差,因为都是 sync write。

而且,个人理解,从 Redis 的设计理念来说,其本身就不是为了保证的一致性的(甚至最终一致性都很难保证,除非牺牲大量的性能),其本身的设计始终优先最求性能和可用性,而数据的持久性的一致性相对比较次要。

No Consistency

通常的 master-slave architecture 就无法保证 Consistency,因为如果当一部分数据被写入 master 后,这一部分数据会被 async 地同步到 slave 中,而当这个同步还没有完成时,master node 挂了,这时候一个 slave node 被 promote,以代替这个挂了的 master node,并作为新的 master node。这时候因为丢到的数据不会在新的 master node 中(而且之后也不可能在),因而 eventual consistency 都没有被保证。

在无法保证 Consistency 的情况下,我们仍然可以做一些 trade-off,即性能与不一致的程度。

单机持久化

Redis 提供两种持久化方式分别是:RDB 和 AOF。需要说明的一点是 * 写入文件并不代表持久化成功,还需要将文件同步到磁盘。*

比如

  1. 开启 RDB,以定时打 snapshot
  2. AOF 的 appendfsync everysec 可以保证最多 1s 的数据被丢失

master-slave

这个是非常常用的 architecture。

MySQL

Master-slave MySQL

对于这种架构,Slave 一般是 Master 的备份。在这样的系统中,一般是如下设计的:

  1. 读写请求都由 Master 负责
  2. 写请求写到 Master 上后,由 Master 同步(sync)或者异步(async)地同步到 Slave 上。

从 Master 同步到 Slave 上,你可以使用异步,也可以使用同步;可以使用 Master 来 push,也可以使用 Slave 来 pull。 通常来说是 Slave 来周期性的 pull。

如果 Slave pull,如果 Master 被下次 pull 之后 down 了,那么这个时间片内的数据直接会丢失,且永远

Master-master MySQL

Master-Master,又叫 Multi-master,是指一个系统存在两个或多个 Master,每个 Master 都提供 read-write 服务。这个模型是 Master-Slave 的加强版,数据间同步一般是通过 Master 间的异步完成,所以是最终一致性。 Master-Master 的好处是,一台 Master 挂了,别的 Master 可以正常做读写服务,他和 Master-Slave 一样,当数据没有被复制到别的 Master 上时,数据会丢失。很多数据库都支持 Master-Master 的 Replication 的机制。

另外,如果多个 Master 对同一个数据进行修改的时候,这个模型的恶梦就出现了 —— 对数据间的冲突合并,这并不是一件容易的事情。看看 Dynamo 的 Vector Clock 的设计(记录数据的版本号和修改者)就知道这个事并不那么简单,而且 Dynamo 对数据冲突这个事是交给用户自己搞的。就像我们的 SVN 源码冲突一样,对于同一行代码的冲突,只能交给开发者自己来处理。

MySQL + Redis

数据更新时 - 异步更新缓存

  • single source of truth 为 DB

缓存与数据库双存储双写

数据更新时 - 缓存延时双删

即发生数据更新时,

  1. 先写数据库

  2. 删除缓存

  3. 休眠 1 秒,再次删除缓存

    • 这一步可以这样实现:

      • Solution 1:在第一次删除缓存后,开启一个线程,并让这个线程在 1s 后,执行再次删除
      • Solution 2:通过读取 DB 的 binlog 和一个消息队列来实现再次删除

Analysis

  • single source of truth 为 DB
  • 这里具体休眠多久要结合业务情况考虑。
  • 如果考虑到删除可能失败,再增加删除失败时的重试机制。

Reference