MariaDB 最终一致性?

背景

最终一致性是一种在许多大型分布式数据库中使用的 一致性模型,它要求对复制数据的所有更改最终会到达所有受影响的副本;冲突解决不在此模型中处理,发生冲突更新时责任被推给应用程序作者 [13]。

最终一致性是一种特定形式的弱一致性;存储系统保证如果对象没有新的更新,最终所有访问都会返回最后更新的值 [14]。如果没有发生故障,不一致窗口的最大大小可以根据通信延迟、系统负载以及复制方案中涉及的副本数量等因素来确定 [3]。

最终一致性系统的一些示例

  • DNS
  • RDBMS(例如 MariaDB)上的异步主/从复制
  • MariaDB 前的 memcached,用于缓存读取

最终一致性指出,在可更新的复制数据库中,每个数据项的所有副本最终会收敛到相同的值。最终一致性的起源可以追溯到 Thomas 的多数共识算法 [12]。该术语由 Terry 等人 [11] 首次提出,后来因亚马逊在其 Dynamo 系统中流行起来,该系统仅支持最终一致性 [7]。

CAP 定理,也由其作者 Erik A. Brewer 博士称为 Brewer 定理,于 2000 年在 PODC 上提出 [4, 5]。该定理由 Gilbert 和 Lynch [8] 正式证明。Brewer 将一致性 (Consistency)、可用性 (Availability) 和分区容错性 (Partition Tolerance) 引入为任何共享数据系统的三个期望属性,并推测最多只能同时保证其中两个 [6]。

一般来说,这个定理完美地契合了当今互联网系统的需求。理想情况下,我们期望服务在通过网络连接到互联网的整个期间都是可用的 [1]。因此,如果网络连接可用,服务也应该可用 [9,10]。为了获得良好的性能,请求需要由分布式系统处理。如果服务器数量增加,服务器故障或网络故障的概率也会增加。因此,系统需要考虑到这一点,并设计成使这些故障对客户端透明,并尽量减少此类故障的影响 [2]。该定理的缩写来自于这三个属性

  • 一致性 (Consistency):该属性要求在数据分散在多个服务器中的分布式系统中执行的每个操作,其结果与在包含所有数据的一个服务器上执行的结果相同。
  • 可用性 (Availability):分布式系统的此属性要求,向任何功能正常的节点发送请求,请求者都应该能够获得响应。符合此属性的系统对例如由网络吞吐量问题引起的任何节点故障具有容错能力。
  • 分区容错性 (Partition Tolerance):分布式系统由通过网络互连的许多服务器组成。一个常见的要求是将系统分布在更多数据中心,以消除其中一个数据中心的故障。在网络通信过程中,故障是常见的。因此,系统需要能够抵御服务器之间任意数量的消息失败。服务器集合之间的临时通信中断不得导致整个系统响应不正确 [9]。

最终一致性定义如下

定义 1:最终一致性

  • 最终交付 (Eventual delivery):在一个节点上执行的更新最终会在所有节点上执行。
  • 终止 (Termination):所有更新执行都会终止。
  • 收敛 (Convergence):执行相同更新的节点最终会达到等价状态(并保持)。

示例 1:考虑数据项 R=0 存储在所有三个节点上的情况。假设我们在 node0 上有以下写入和提交序列:W(R=3) C W(R=5) C W(R=7) C。现在从 node1 读取可能返回 R=3,从 node2 读取可能返回 R=5。只要所有节点的读取最终返回相同的值,这就是最终一致的。请注意,最终值可以是 R=3。最终一致性不限制写入必须执行的顺序。

MariaDB 演示

如前所述,MariaDB 上的常规主从设置是最终一致的。在本文中,我们关注的是一个具有多主设置的情况。我们将使用 MariaDB 10.0。这里可以考虑几种可能的拓扑结构,但我们选择了环形拓扑 (见图 1)。

Diagram1

图 1:MariaDB 环形拓扑。

在此拓扑中,Node0 是主节点,Node1Node0 的从节点。类似地,Node2Node1 的从节点。我们从 Node0 开始配置节点

[mysqld]
port = 4000
socket=/home/jan/mysql/mm/node0/mysql.4000.sock
datadir=/home/jan/mysql/mm/node0
basedir=/usr/local/mysql
log_error=/home/jan/mysql/mm/node0/error.log
binlog_format=ROW
log-bin
log-slave-updates=1
gtid-strict-mode=1
server_id=1
gtid-domain-id=1

类似地,Node1

[mysqld]
port = 5000
socket=/home/jan/mysql/mm/node1/mysql.5000.sock
datadir=/home/jan/mysql/mm/node1
basedir=/usr/local/mysql
log_error=/home/jan/mysql/mm/node1/error.log
binlog_format=ROW
log-bin
log-slave-updates=1
gtid-strict-mode=1
server_id=2
gtid-domain-id=2

最后是 Node2

[mysqld]
port = 6000
socket=/home/jan/mysql/mm/node2/mysql.6000.sock
datadir=/home/jan/mysql/mm/node2
basedir=/usr/local/mysql
log_error=/home/jan/mysql/mm/node2/error.log
binlog_format=ROW
log-bin
log-slave-updates=1
gtid-strict-mode=1
server_id=3
gtid-domain-id=3

完成后,我们可以安装 MariaDB 数据库并启动服务器。

/usr/local/mysql/scripts/mysql_install_db 
  --datadir=/home/jan/mysql/mm/node0 
  --defaults-file=/home/jan/mysql/mm/node0 
  --user=jan --basedir=/usr/local/mysql

/usr/local/mysql/scripts/mysql_install_db 
  --datadir=/home/jan/mysql/mm/node1 
  --defaults-file=/home/jan/mysql/mm/node1 
  --user=jan --basedir=/usr/local/mysql

/usr/local/mysql/scripts/mysql_install_db 
  --datadir=/home/jan/mysql/mm/node2 
  --defaults-file=/home/jan/mysql/mm/node2 
  --user=jan --basedir=/usr/local/mysql

/usr/local/mysql/bin/mysqld --defaults-file=/home/jan/mysql/mm/node0/my.cnf &
/usr/local/mysql/bin/mysqld --defaults-file=/home/jan/mysql/mm/node1/my.cnf &
/usr/local/mysql/bin/mysqld --defaults-file=/home/jan/mysql/mm/node2/my.cnf &

现在服务器已启动并运行,我们在 Node0 上设置第一个主节点

jan-GE70-0NC-0ND ~ $ /usr/local/mysql/bin/mysql -u root 
> -S /home/jan/mysql/mm/node0/mysql.4000.sock 

Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 3
Server version: 10.0.10-MariaDB-log Source distribution

Copyright (c) 2000, 2014, Oracle, SkySQL Ab and others.

Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

MariaDB [(none)]> use test;
Database changed
MariaDB [test]> SHOW MASTER STATUS;
+-----------------------------+----------+--------------+------------------+
| File                        | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-----------------------------+----------+--------------+------------------+
| jan-GE70-0NC-0ND-bin.000001 |      323 |              |                  |
+-----------------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

好的,现在我们需要将 Node1 设置为其从节点,使用

jan@jan-GE70-0NC-0ND ~/Desktop $ /usr/local/mysql/bin/mysql 
> -u root -S /home/jan/mysql/mm/node1/mysql.5000.sock 

Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 3
Server version: 10.0.10-MariaDB-log Source distribution

Copyright (c) 2000, 2014, Oracle, SkySQL Ab and others.

Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

MariaDB [(none)]> use test;
Database changed
MariaDB [test]> CHANGE MASTER TO
    -> MASTER_HOST='127.0.0.1',
    -> MASTER_USER='root',
    -> MASTER_LOG_FILE='jan-GE70-0NC-0ND-bin.000001',
    -> MASTER_LOG_POS=323,
    -> MASTER_PORT=4000;
Query OK, 0 rows affected (0.52 sec)

MariaDB [test]> START SLAVE;
Query OK, 0 rows affected (0.00 sec)

MariaDB [test]> SHOW SLAVE STATUSG
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 127.0.0.1
                  Master_User: root
                  Master_Port: 4000
                Connect_Retry: 60
              Master_Log_File: jan-GE70-0NC-0ND-bin.000001
          Read_Master_Log_Pos: 323
               Relay_Log_File: jan-GE70-0NC-0ND-relay-bin.000002 
                Relay_Log_Pos: 546
        Relay_Master_Log_File: jan-GE70-0NC-0ND-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 323
              Relay_Log_Space: 854
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 1
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
                   Using_Gtid: No
                  Gtid_IO_Pos: 
1 row in set (0.00 sec)

MariaDB [test]> SHOW MASTER STATUS;
+-----------------------------+----------+--------------+------------------+
| File                        | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-----------------------------+----------+--------------+------------------+
| jan-GE70-0NC-0ND-bin.000001 |      323 |              |                  |
+-----------------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

类似地,将 Node2 设置为 Node1 的从节点

an@jan-GE70-0NC-0ND ~/Desktop $ /usr/local/mysql/bin/mysql 
> -u root -S /home/jan/mysql/mm/node2/mysql.6000.sock 

Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 3
Server version: 10.0.10-MariaDB-log Source distribution

Copyright (c) 2000, 2014, Oracle, SkySQL Ab and others.

Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

MariaDB [(none)]> use test;
Database changed
MariaDB [test]> CHANGE MASTER TO
    -> MASTER_HOST='127.0.0.1',
    -> MASTER_USER='root',
    -> MASTER_LOG_FILE='jan-GE70-0NC-0ND-bin.000001',
    -> MASTER_LOG_POS=323,
    -> MASTER_PORT=5000;
Query OK, 0 rows affected (0.55 sec)

MariaDB [test]> start slave;
Query OK, 0 rows affected (0.01 sec)

MariaDB [test]> show slave statusG
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 127.0.0.1
                  Master_User: root
                  Master_Port: 5000
                Connect_Retry: 60
              Master_Log_File: jan-GE70-0NC-0ND-bin.000001
          Read_Master_Log_Pos: 323
               Relay_Log_File: jan-GE70-0NC-0ND-relay-bin.000002
                Relay_Log_Pos: 546
        Relay_Master_Log_File: jan-GE70-0NC-0ND-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 323
              Relay_Log_Space: 854
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 2
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
                   Using_Gtid: No
                  Gtid_IO_Pos: 
1 row in set (0.00 sec)

MariaDB [test]> show master status;
+-----------------------------+----------+--------------+------------------+
| File                        | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-----------------------------+----------+--------------+------------------+
| jan-GE70-0NC-0ND-bin.000001 |      323 |              |                  |
+-----------------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

最后,将 Node0 设置为 Node2 的从节点

jan-GE70-0NC-0ND ~ $ /usr/local/mysql/bin/mysql 
> -u root -S /home/jan/mysql/mm/node0/mysql.4000.sock 

Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MariaDB connection id is 3
Server version: 10.0.10-MariaDB-log Source distribution

Copyright (c) 2000, 2014, Oracle, SkySQL Ab and others.

Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

MariaDB [(none)]> use test;
Database changed
MariaDB [test]> CHANGE MASTER TO 
    -> MASTER_HOST='127.0.0.1', 
    -> MASTER_USER='root', 
    -> MASTER_LOG_FILE='jan-GE70-0NC-0ND-bin.000001', 
    -> MASTER_LOG_POS=  323, 
    -> MASTER_PORT=6000;
Query OK, 0 rows affected (0.52 sec)

MariaDB [test]> start slave;
Query OK, 0 rows affected (0.01 sec)

MariaDB [test]> show slave statusG
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 127.0.0.1
                  Master_User: root
                  Master_Port: 6000
                Connect_Retry: 60
              Master_Log_File: jan-GE70-0NC-0ND-bin.000001
          Read_Master_Log_Pos: 323
               Relay_Log_File: jan-GE70-0NC-0ND-relay-bin.000002
                Relay_Log_Pos: 546
        Relay_Master_Log_File: jan-GE70-0NC-0ND-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 323
              Relay_Log_Space: 854
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 3
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
                   Using_Gtid: No
                  Gtid_IO_Pos: 
1 row in set (0.00 sec)

MariaDB [test]> show master status;
+-----------------------------+----------+--------------+------------------+
| File                        | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-----------------------------+----------+--------------+------------------+
| jan-GE70-0NC-0ND-bin.000001 |      323 |              |                  |
+-----------------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

现在我们创建一个表,并从不同的节点向其中添加一些数据

/usr/local/mysql/bin/mysql test -u root 
  -S /home/jan/mysql/mm/node0/mysql.4000.sock 
  -e "create table a(a integer not null primary key) engine=innodb" 

/usr/local/mysql/bin/mysql test -u root 
  -S /home/jan/mysql/mm/node0/mysql.4000.sock 
  -e "insert into a values(1)" 

/usr/local/mysql/bin/mysql test -u root 
  -S /home/jan/mysql/mm/node1/mysql.5000.sock 
  -e "insert into a values(2)" 

/usr/local/mysql/bin/mysql test -u root 
  -S /home/jan/mysql/mm/node2/mysql.6000.sock 
  -e "insert into a values(3)"

之后所有节点都达到最终一致性,并返回相同的结果集,例如

MariaDB [test]> select * from a;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.00 sec)

由此我们可以得出结论,在没有冲突操作的情况下,MariaDB 在多主设置中也是最终一致的。

但是如果发生冲突怎么办?我们可以通过尝试向表 a 插入重复键来测试这种情况。我们尝试向 node0node2 都插入值 5,以便最终提交命令几乎同时发出。

MariaDB [test]> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)

MariaDB [test]> insert into a values (5);
Query OK, 1 row affected (0.00 sec)

MariaDB [test]> commit;
Query OK, 0 rows affected (0.27 sec)

因为我们使用了 InnoDB 存储引擎并且 autocommit 关闭,所以在提交时两个客户端连接都没有显示错误消息。这是因为 MariaDB 不支持延迟约束检查,并且在以下情况下不会出现错误

  • 你在 node0 上的服务器插入 5,成功。
  • 在插入复制到 node2 上的服务器之前,你在 node2 上的服务器插入 5,这也正常,因为这是异步复制。
  • 然后第二个插入从 node2 复制到 node0,由于重复的键值 5 导致冲突,复制线程捕获错误并回滚。

因此,所有三个节点上的结果集如下

MariaDB [test]> select * from a;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
| 5 |
+---+
4 rows in set (0.00 sec)

这也是最终一致的,因为所有服务器都返回完全相同的值,并且它们执行了完全相同的事务。从服务器日志中我们可以发现

140320 10:48:10 [ERROR] Slave SQL: Could not execute Write_rows_v1 event on
table test.a; Duplicate entry '5' for key 'PRIMARY', Error_code: 1062; handler
error HA_ERR_FOUND_DUPP_KEY; the event's master log
jan-GE70-0NC-0ND-bin.000001, end_log_pos 1009, Internal MariaDB error code:
1062

140320 10:48:10 [Warning] Slave: Duplicate entry '5' for key 'PRIMARY'
Error_code: 1062

140320 10:48:10 [ERROR] Error running query, slave SQL thread aborted. Fix the
problem, and restart the slave SQL thread with "SLAVE START". We stopped at log
'jan-GE70-0NC-0ND-bin.000001' position 897

例如,从 node0 上你可以用以下命令查看

MariaDB [test]> show slave statusG
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 127.0.0.1
                  Master_User: root
                  Master_Port: 6000
                Connect_Retry: 60
              Master_Log_File: jan-GE70-0NC-0ND-bin.000001
          Read_Master_Log_Pos: 1036
               Relay_Log_File: jan-GE70-0NC-0ND-relay-bin.000002
                Relay_Log_Pos: 824
        Relay_Master_Log_File: jan-GE70-0NC-0ND-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 1062
                   Last_Error: Could not execute Write_rows_v1 event on table
                               test.a; Duplicate entry '5' for key 'PRIMARY',
                               Error_code: 1062; handler error
                               HA_ERR_FOUND_DUPP_KEY; the event's master log
                               jan-GE70-0NC-0ND-bin.000001, end_log_pos 1009
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 897
              Relay_Log_Space: 1271
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 1062
               Last_SQL_Error: Could not execute Write_rows_v1 event on table
                               test.a; Duplicate entry '5' for key 'PRIMARY',
                               Error_code: 1062; handler error
                               HA_ERR_FOUND_DUPP_KEY; the event's master log
                               jan-GE70-0NC-0ND-bin.000001, end_log_pos 1009
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 3
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
                   Using_Gtid: No
                  Gtid_IO_Pos: 
1 row in set (0.00 sec)

从日志可以看出,问题是节点之间的复制已经停止。然而,可以通过配置 –slave-skip-error=XXX–slave_exec_mode=IDEMPOTENT 来忽略应用程序错误导致的复制错误。 –slave_exec_mode 选项控制在复制冲突解决和错误检查中是使用 IDEMPOTENT  模式还是 STRICT  模式。 IDEMPOTENT  模式会抑制重复键和未找到键的错误。 多主复制和环形复制需要此模式。由应用程序引起的其他有效错误可以使用 –slave-skip-error  进行跳过。

为了演示,让我们在所有服务器上设置 –slave-skip-error=all  和 –slave-exec-mode=IDEMPOTENT  并重新启动它们。我们现在可以尝试让服务器进入不同的状态(即替代未来的状态)。在 node0 上执行以下命令

MariaDB [test]> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

MariaDB [test]> update a set a = 12 where a = 3;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1  Warnings: 0

MariaDB [test]> commit;
Query OK, 0 rows affected (0.09 sec)

MariaDB [test]> select * from a;
+----+
| a  |
+----+
|  1 |
|  2 |
|  5 |
|  9 |
| 12 |
+----+
5 rows in set (0.00 sec)

以及在 node1 上执行以下命令

MariaDB [test]> update a set a = 15 where a = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

MariaDB [test]> commit;
Query OK, 0 rows affected (0.13 sec)

MariaDB [test]> select * from a;
+----+
| a  |
+----+
|  1 |
|  2 |
|  5 |
|  9 |
| 15 |
+----+
5 rows in set (0.00 sec)

slave status  中我们没有看到任何问题

MariaDB [test]> show slave statusg
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 127.0.0.1
                  Master_User: root
                  Master_Port: 6000
                Connect_Retry: 60
              Master_Log_File: jan-GE70-0NC-0ND-bin.000002
          Read_Master_Log_Pos: 653
               Relay_Log_File: jan-GE70-0NC-0ND-relay-bin.000006
                Relay_Log_Pos: 812
        Relay_Master_Log_File: jan-GE70-0NC-0ND-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 653
              Relay_Log_Space: 1418
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 
                Last_IO_Error: 0
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 3
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
                   Using_Gtid: No
                  Gtid_IO_Pos: 
1 row in set (0.00 sec)

但在服务器日志中有一个警告

140320 13:33:40 [Warning] Slave SQL: Could not execute Update_rows_v1 event on
table test.a; Can't find record in 'a', Error_code: 1032; handler error
HA_ERR_KEY_NOT_FOUND; the event's master log jan-GE70-0NC-0ND-bin.000002,
end_log_pos 626, Internal MariaDB error code: 1032

这种情况不是最终一致的,MariaDB 无法自动解决这种情况。如果应用程序需要最终一致性,则需要解决此冲突,以便所有数据库再次回到符合应用程序规则的相同状态。

结论

最终一致性意味着,只要有足够的时间,期间没有进行任何更改,所有成功的更新都会传播到整个系统,所有副本都会同步。在任何给定时间,无法保证访问的数据是一致的,因此必须解决冲突。根据此定义,即使在复制错误导致复制停止的情况下,只要复制在某个时间点(有界时间)继续并且所有服务器返回相同的状态,如果不忽略复制错误,MariaDB 便是最终一致的。如果忽略复制错误,应用程序必须纠正两个或多个服务器处于不同状态的情况。

我们最初的问题是:MariaDB 是最终一致的吗?

答案: 对于大多数将所有数据复制到从节点的主从设置,MariaDB 是最终一致的。对于仅忽略应用程序处理的错误情况,并且应用程序确保服务器不会分化到替代未来的多主设置,MariaDB 是最终一致的。但是,存在一些复制配置,MariaDB 在其中不是最终一致的。

参考文献

[1] Bailis, P., and Ghodsi, A: Eventual consistency today: limitations, extensions, and beyond, In communications of the ACM vol. 56, no. 5, PP. 55-63, May 2013.

[2] Philip A. Bernstein, Sudipto Das: Rethinking Eventual Consistency, SIGMOD’13, June 22–27, 2013.

[3] Bermbach, D. and Tai S: Eventual Consistency: How soon is eventual? In Proceedings of ACM MW4SOC ’11 and 6 other workshop on Service Oriented Computing, New York, December, 2011, no.1.

[4] Brewer, E: PODC keynote. http://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf, 2000.

[5] Brewer, E.: Towards Robust Distributed Systems, (invited Talk) Principles of Distributed Computing, Portland, Oregon, SIGOPS, And SIGACT News, July 2000.

[6] Brewer, E.: CAP twelve years later: How the “rules” have changed. IEEE Computer, vol. 45, no. 2, pp. 23-29, February 2012.

[7] Decantia, G., Hastorun, D., Jampani, M., Kakulapati, G., Lakshman, A., Pilchin, A., Sivasubramanian, S., Vosshall, P., and Vogels, W: Dynamo: Amazon’s highly available key-value store. In Proceeding 21st ACM Symposium on Operating Systems Principles (SOSP), pp. 205-220, 2007.

[8] Lynch, S. Gilbert, N: Brewer’s conjecture and the feasibility of consistent, available, partition-tolerant web services. ACM SIGACT News. 2002, 33, 2, p. 5159.

[9] Hale, C.: You can’t sacrifice partition tolerance; Available from http://codahale.com/you-cant-sacrificepartition-tolerance.

[10] Marc Shapiro, Bettina Kemme: Eventual Consistency. Encyclopedia of Database Systems 2009:1071-1072.

[11] Terry, D. B., Demers, A. J., Petersen, K., Spreitzer, M.J., Theimer, M.M., Welch, B. B.: Session guarantees for Weakly Consistent Replicated Data. In PDIS, pp. 140-149, 1994.

[12] Thomas, R. H.: A majority consensus approach to concurrency control for multiple copy databases. ACM Trans. on Database Systems, vol. 4, no. 2, pp. 180–209, June 1979.

[13] Vogels, W.: Scalable Web services: Eventually Consistent, ACM Queue, vol. 6, no. 6, pp. 14-16, October 2009.

[14] Vogels, W.: Eventually consistent, Communications of the ACM, vol. 52, no. 1, pp. 40–44, January 2009.