Galera 中的自增长

让我们从考虑这样一个场景开始:记录通过多主集群的不同节点插入到单个自增长表中。可能出现的一个问题是不同节点上生成的自增长值发生‘冲突’,这正是本文的主题。

由于集群是多主的,它允许在所有主节点上写入。因此,在不同的节点上执行 INSERT 操作时,表可能会获得相同的自增长值。这个问题只有在写入集复制完成后才会被发现,这是个问题!

Galera 集群也面临类似的问题。

让我们尝试在一个 2 节点 Galera 集群上模拟这个问题

1) On node #1:

MariaDB [test]> CREATE TABLE t1(c1 INT AUTO_INCREMENT PRIMARY KEY, c2 INT)ENGINE=InnoDB;
Query OK, 0 rows affected (0.07 sec)

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

MariaDB [test]> INSERT INTO t1(c2) VALUES (1);
Query OK, 1 row affected (0.05 sec)

2) On node #2:

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

MariaDB [test]> INSERT INTO t1(c2) VALUES(2);
Query OK, 1 row affected (0.00 sec)

MariaDB [test]> COMMIT;
Query OK, 0 rows affected (0.05 sec)

3) On node #1

MariaDB [test]> COMMIT;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

MariaDB [test]> SELECT * FROM t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 |    2 |
+----+------+
1 row in set (0.00 sec)

正如预期的那样,由于冲突,第二次提交未能成功。

那么,我们如何处理这个问题呢?引入 @@auto_increment_increment 和 @@auto_increment_offset!使用这两个系统变量,可以在 MySQL/MariaDB 服务器上控制自增长值的序列。技巧在于以一种方式设置它们,使得集群中的每个节点生成一个不冲突的数值序列。

例如,让我们为一个 3 节点集群讨论这个问题 (n=3)

Node 1: @@auto_increment_increment=3, @@auto_increment_offset=1 => Sequence : 1, 4, 7, 10, ...
Node 2: @@auto_increment_increment=3, @@auto_increment_offset=2 => Sequence : 2, 5, 8, 11, ...
Node 3: @@auto_increment_increment=3, @@auto_increment_offset=3 => Sequence : 3, 6, 9, 12, ...

如您所见,通过将每个节点的 auto_increment_increment 设置为集群中的总节点数 (n),并将 auto_increment_offset 设置为 [1,n] 之间的一个数字,我们可以确保这样生成的自增长值在整个集群中是唯一的,从而避免任何冲突或碰撞。

在 Galera 集群中,默认情况下已经处理了这个问题。当一个节点加入集群时,这两个自增长变量会自动调整以避免冲突。但是,这个功能可以通过使用 wsrep_auto_increment_control 变量来控制。

Node #1:

MariaDB [test]> show variables like '%auto_increment%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| auto_increment_increment     | 3     |
| auto_increment_offset        | 1     |
| wsrep_auto_increment_control | ON    |
+------------------------------+-------+
3 rows in set (0.00 sec)

Node #2:

MariaDB [test]> show variables like '%auto_increment%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| auto_increment_increment     | 3     |
| auto_increment_offset        | 2     |
| wsrep_auto_increment_control | ON    |
+------------------------------+-------+
3 rows in set (0.00 sec)

Node #3:

MariaDB [test]> show variables like '%auto_increment%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| auto_increment_increment     | 3     |
| auto_increment_offset        | 3     |
| wsrep_auto_increment_control | ON    |
+------------------------------+-------+
3 rows in set (0.00 sec)

通过此设置,上面示例中的最后一次 COMMIT 将会成功。