我发现常规MariaDB安装和MariaDB Galera集群之间的行为不一致。对于使用Galera集群的INSERT ... SELECT语句,锁定无法正常工作。这会导致我们的应用程序中出现重复的ID。
所有连接都使用隔离级别REPEATABLE-READ(默认值),使用此查询进行验证:
SELECT * FROM information_schema.session_variables WHERE variable_name = 'tx_isolation';
测试设置:
CREATE TABLE `TestTab` (`id` int(10) unsigned NOT NULL, `name` varchar(100) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;
预期行为:两个并行连接应该在' id'中创建两个具有两个不同值的记录。字段。
使用常规MariaDB安装(非群集)演示预期行为的命令序列:
Connection 1 Connection 2
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 1' FROM TestTab Limit 0,1;
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 2' FROM TestTab Limit 0,1;
-- *** GOOD *** INSERT blocks (ensures repeatable read for the SELECT)
COMMIT;
-- only now connection 2 completes the the INSERT
COMMIT;
SELECT * FROM TestTab;
SELECT * FROM TestTab;
+----+----------+
| id | name |
+----+----------+
| 1 | insert 1 |
| 2 | insert 2 |
+----+----------+
演示MariaDB Galera集群问题的命令序列:
Connection 1 Connection 2
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 1' FROM TestTab Limit 0,1;
SET autocommit=0;
START TRANSACTION;
INSERT INTO TestTab (`id`, `name`)
SELECT IFNULL(max(id)+1, 1), 'insert 2' FROM TestTab Limit 0,1;
-- *** BAD *** INSERT completes immediately, ignoring connection 1
COMMIT;
COMMIT;
SELECT * FROM TestTab;
SELECT * FROM TestTab;
SELECT * FROM TestTab;
+----+----------+
| id | name |
+----+----------+
| 1 | insert 1 |
| 1 | insert 2 | -- !!! same id for both records !!! --
+----+----------+
显然,Galera群集的问题在于两个记录在' id'中都有相同的值。字段。在Galera集群上,INSERT ... SELECT模式用于生成唯一ID。
对我而言,这看起来像是一个错误,或者至少是非常不受欢迎和意外的行为。
是否可以使用不同的Galera群集配置修复此内容?
我尝试将两个会话的隔离级别提高到SERIALIZABLE并修复了问题。但这是一个非常不受欢迎的解决方法,性能在这里对我们很重要。而且似乎我还是幸运的是: SERIALIZABLE隔离级别仅在同一节点上发布的事务之间得到尊重,因此应该避免。(quote from the Galera Cluster documentation)
您能想到哪些更好的解决方法?
我应该尝试将此报告为MariaDB的错误吗?
答案 0 :(得分:1)
请注意,Galera在COMMIT
时间之前不会检入其他节点。与此同时,MAX(id)
很高兴获得了旧的价值,并且无法阻止其他人查看MAX(id)
。
可能的解决方案(我没有测试过任何这些,也没有检查相同的问题和解决方案是否可以在没有 Galera的单个服务器上发生。) :
UNIQUE(id)
- 这会导致COMMIT
中止。READ-COMMITTED
查看最新的MAX(id)
。SELECT
移出INSERT
并使用FOR UPDATE
。