Я обнаружил несоответствие в поведении между обычной установкой MariaDB и кластером MariaDB Galera. Блокировка не работает должным образом для операторов INSERT ... SELECT с кластером Galera. Это приводит к дублированию идентификаторов в нашем приложении.
Все соединения используют уровень изоляции 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 для создания уникальных идентификаторов.
Для меня это выглядит как ошибка или, по крайней мере, очень нежелательное и неожиданное поведение.
Можно ли это исправить с помощью другой конфигурации кластера Galera?
Я попытался повысить уровень изоляции до SERIALIZABLE для обоих сеансов, и это решило проблему. Но это крайне нежелательный обходной путь, здесь для нас важна производительность. И похоже, что мне все равно просто повезло: уровень изоляции SERIALIZABLE соблюдается только между транзакциями, выпущенными на одном узле, и поэтому его следует избегать. (цитата из документации Galera Cluster)
Какие лучшие обходные пути вы можете придумать?
Должен ли я попытаться сообщить об этом как об ошибке в MariaDB?