Кластер MariaDB Galera: блокировка INSERT SELECT нарушена?

Я обнаружил несоответствие в поведении между обычной установкой 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?


person StaticNoiseLog    schedule 15.11.2016    source источник


Ответы (1)


Имейте в виду, что Galera не связывается с другими узлами до COMMIT времени. Между тем, MAX(id) с радостью получает то, что будет старым значением, и ничего не делает, чтобы помешать кому-то другому взглянуть на MAX(id).

Возможные решения (я не проверял ни одно из них и не проверял, может ли та же проблема и решение возникнуть на одном сервере без Galera.):

  • UNIQUE(id) -- Это должно привести к прерыванию COMMIT.
  • Используйте READ-COMMITTED, чтобы увидеть последние MAX(id).
  • Переместите SELECT из INSERT и используйте FOR UPDATE.
person Rick James    schedule 16.11.2016
comment
Спасибо за совет. Также можно указать FOR UPDATE непосредственно внутри оператора INSERT ... SELECT следующим образом: INSERT INTO TestTab (id, name) SELECT IFNULL(max(id)+1, 1), 'insert 1' FROM TestTab Limit 0 ,1 НА ОБНОВЛЕНИЕ; В нашем сценарии это, по-видимому, вызывает много блокировок (кто-то еще пробовал), и производительность страдает. - person StaticNoiseLog; 05.12.2016
comment
Моя интуиция говорит, что этого недостаточно. max(id)+1 не будет учитывать действия на других узлах. Возможно COMMIT прервется, если такое действие не сработает с max, но я не уверен. - person Rick James; 05.12.2016