Почему InnoDB блокирует больше записей в случае вторичного индекса? - PullRequest
0 голосов
/ 31 января 2020

Я использую MySQL таблицы InnoDB и пытаюсь понять причины некоторой блокировки на уровне строк в случае сканирования диапазона индекса. Я обнаружил, что дополнительная запись индекса (вне диапазона) может быть заблокирована в зависимости от уникальности используемого индекса. См. Пример ниже (проверено в версии 8.0.18).

CREATE TABLE foo (
  a INT NOT NULL,
  b INT NOT NULL,
  c CHAR(1) NOT NULL,
  PRIMARY KEY (a),
  UNIQUE KEY (b)
) ENGINE=InnoDB;

INSERT INTO foo VALUES (1,1,'A'), (3,3,'B'), (5,5,'C'), (7,7,'D'), (9,9,'E');

Контрольный пример 1

Сессия 1:

START TRANSACTION;
SELECT * FROM foo WHERE a < 2 FOR UPDATE;

Сессия 2:

DELETE FROM foo WHERE a = 3;  -- Success

Контрольный пример 2

При этом используются исходные строки таблицы с возвращенной удаленной записью.

Сессия 1:

START TRANSACTION;
SELECT * FROM foo WHERE b < 2 FOR UPDATE;

Сессия 2:

DELETE FROM foo WHERE b = 3;  -- Blocks

Блокировка записи вторичного индекса с b = 3 во втором тестовом случае выглядит ненужной.

Почему InnoDB блокирует следующий индекс запись справа от сканируемого диапазона в случае вторичного индекса? Есть ли практическая причина для этого? Может кто-нибудь привести пример проблемы, которая может возникнуть, если запись с b = 3 не заблокирована во втором тестовом примере?

Ответы [ 2 ]

0 голосов
/ 21 февраля 2020

Наконец я нашел ответ. Короче говоря, нет никаких существенных причин для такой дополнительной блокировки во втором тестовом примере. Когда выполняется считывание блокировки диапазона вторичного индекса, устанавливаются достаточные блокировки, но не необходимый минимум. Таким образом, дополнительные блокировки устанавливаются только потому, что некоторым программистам InnoDB было проще писать код. Кого волнуют дополнительные блокировки, если все работает по большей части?

Я опубликовал отчет об ошибке по этой проблеме: https://bugs.mysql.com/bug.php?id=98639 К сожалению, их сотрудник не хочет регистрировать эту ошибку. Он не понимает моих аргументов и приводит ошибочные объяснения. Он сделал мои последние аргументы приватными и перестал отвечать.

Я также спросил об этой проблеме на официальном форуме и получил следующий ответ: https://forums.mysql.com/read.php?22, 684356 684482 Короче, значительные усилия необходимо исправить эту ошибку. Но так как это очень маленькая и незначительная ошибка (точнее, проблема с производительностью), они пока не хотят ее исправлять. Однако в версии 8.0.18 они исправили аналогичную проблему для кластерного индекса, и это заняло у них более месяца.

Я очень удивлен, что исправление такого простого одноуровневого алгоритма сканирования занимает так много времени и вызывает столько трудностей. Возможно, доработка InnoDB сейчас выполняется программистами младшего или среднего уровня.

0 голосов
/ 01 февраля 2020

Как уже упоминалось @Barmar, это так, потому что MySQL устанавливает GAP или блокировку следующей клавиши. Я предполагаю, что вы используете уровень изоляции innodb по умолчанию REPEATABLE READ

Документация MySQL гласит:

Для блокировки чтения (ВЫБРАТЬ с помощью FOR UPDATE или LOCK IN SHARE MODE) Для операторов UPDATE и DELETE блокировка зависит от того, использует ли инструкция уникальный индекс с уникальным условием поиска, или условие поиска типа диапазона.

  • Для уникального индекса с уникальным условием поиска: InnoDB блокирует только найденную запись индекса, а не промежуток перед ней.
  • Для других условий поиска InnoDB блокирует сканированный диапазон индекса, используя блокировки промежутка или блокировки следующего ключа, чтобы блокировать вставки другими сеансами в охватываемые промежутки по дальности. Для получения информации о блокировках пробелов и блокировках следующего ключа см. Раздел 14.7.1, «Блокировка InnoDB».

См .: https://dev.mysql.com/doc/refman/5.6/en/innodb-transaction-isolation-levels.html#isolevel_repeatable -читать

В данный момент я ищу информацию о том, как установлена ​​блокировка зазора, но я также получил эту информацию, и я думаю, что это полезная информация:

Имейте в виду, что блокировка основана на внутреннем индексе, поскольку столбец, который вы используете в своих критериях, не индексируется однозначно.

Блокировка разрыва - это блокировка на разрыве между индексными записями , или блокировка на разрыве перед первым или после последнего индекса record

Разрывные блокировки в InnoDB являются «чисто тормозящими», что означает, что их единственная цель - предотвратить вставку других транзакций в разрыв. Разрывные замки могут сосуществовать. Блокировка промежутка, принятая одной транзакцией, не препятствует тому, чтобы другая транзакция взяла блокировку промежутка в том же самом промежутке. Нет разницы между общими и эксклюзивными блокировками. Они не конфликтуют друг с другом и выполняют одну и ту же функцию.

Разрыв может быть явно отключен. Это происходит, если вы измените уровень изоляции транзакции на READ COMMITTED или включите системную переменную innodb_locks_unsafe_for_binlog (которая в настоящее время устарела). В этих условиях блокировка пробела отключается для поиска и сканирования индекса и используется только для проверки ограничений внешнего ключа и проверки дубликата ключа.

Существуют также другие эффекты использования уровня изоляции READ COMMITTED или включения innodb_locks_unsafe_for_binlog. , Блокировки записей для несовпадающих строк снимаются после того, как MySQL оценил условие WHERE. Для операторов UPDATE InnoDB выполняет «полунепротиворечивое» чтение, так что он возвращает последнюю зафиксированную версию в MySQL, чтобы MySQL мог определить, соответствует ли строка условию WHERE в UPDATE.

см. https://dev.mysql.com/doc/refman/5.6/en/innodb-locking.html#innodb -замки-замки

...