INSERT-SELECT и ОБНОВЛЕНИЯ в условиях гонки - PullRequest
0 голосов
/ 08 ноября 2018

# 1: если я бегу

INSERT INTO foo SELECT MAX(X) FROM bar;

Можно ли быть уверенным, что я только что вставил максимальное значение столбца X из таблицы bar? Что никакой другой сеанс не смог манипулировать таблицей bar после завершения части SELECT, но до завершения INSERT?

# 2: если я бегу

UPDATE foo SET x = 0 WHERE y = 100;

когда часы достигают 00:00, а запрос занимает 2 минуты, могу ли я быть уверен, что все строки, которые имели y = 100 в 00:00, были обновлены? Что никакому другому сеансу не удалось изменить y = 100 на y = 80 до того, как мой запрос завершился?

# 3: Это связано с # 2. Если в 00:01 другой сеанс делает UPDATE с строкой, изменяя y = 99 на y = 100, мой предыдущий запрос пытается UPDATE этой строки?

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Если вы не используете явные транзакции, каждый запрос обрабатывается как отдельная транзакция. Таким образом, запрос, который объединяет INSERT и SELECT, как ваш # 1, может зависеть от согласованности. Это примерно эквивалентно:

START TRANSACTION;
SET @max = (SELECT MAX(x) FROM bar);
INSERT INTO foo VALUES (@max);
COMMIT;

Однако транзакции не создают моментальный снимок всей базы данных во время ее запуска. InnoDB использует блокировку каждой записи. Так, в # 2 и # 3, если у вас есть сеанс A, обновляющий y, в то время как сеанс B выполняет запрос, который вы показываете, записи, которые B обновляет, могут включать или не включать те, которые были изменены A, в зависимости от относительного порядка этих конкретных изменения. MyISAM, с другой стороны, использует блокировки на уровне таблицы, поэтому это не должно быть возможным; какой бы запрос не запустился первым, будет заблокирована таблица foo, а другой запрос будет ждать его завершения, прежде чем начнет сканирование таблицы.

0 голосов
/ 08 ноября 2018

Транзакции используются для гарантии состояния базы данных. Повторяемое чтение, поэтому при считывании максимального значения вы можете прочитать его снова и снова, оно останется прежним.

https://dev.mysql.com/doc/refman/5.6/en/innodb-transaction-isolation-levels.html#isolevel_repeatable-read

Затем вы установите транзакцию

https://dev.mysql.com/doc/refman/5.6/en/set-transaction.html

Тогда ваш код обновится x = (выберите max (y) из z)

затем вы совершаете транзакцию

Возможно, я что-то пропустил, но в целом это то, как это было сделано для предварительного отчета.

...