Добрый день,
Это обновление для моего старого сообщения в SQL. Одновременные транзакции игнорируют блокировки друг друга??? DEADLOCK [InnoDB, Python], как я понял позже, что проблема не имеет ничего общего с тем, что я думал о проблеме. Я пытаюсь создать эквивалентный MySQL для сценария на основе T-SQL для клиента.
У меня два идентичных сценария, Алиса и Барри работают одновременно. Их цель -
SELECT * FROM job WHERE status = 0 LIMIT 1 FOR UPDATE
UPDATE job SET status = 1
где идентификаторы job совпадают, делают некоторые другие вещи, а затем...COMMIT
изменения и продолжите работу Проблема в том, что Алиса делает блокировку, читает задание, UPDATES
ее статус 1, но как только она совершит изменение, Барри берет замок и читает статус 0, исходный статус... Даже Алиса иногда видит статус 0 сразу после ее COMMIT
Это небольшой сегмент из моего сценария python, но этого должно быть достаточно, чтобы понять процедуру:
connection = MySQLdb.connect(host=..., user=..., [...])
cursor = connection.cursor(MySQLdb.cursors.DictCursor)
[...]
execute("START TRANSACTION")
execute("SELECT * FROM job WHERE status = %s LIMIT 1 FOR UPDATE", 0)
job_data = cursor.fetchone()
# debug("Made lock")
if not job_data:
connection.commit()
# debug("No rows")
else:
# debug("Locked row with status "+str(job_data['status']))
execute("SELECT status FROM job")
# debug("Double checked status is "+str(cursor.fetchone()))
execute("UPDATE job SET status = %s WHERE jobID = %s", 1, job_data['jobID'])
time.sleep(5)
execute("SELECT status FROM job")
# debug("Status before commit "+str(cursor.fetchone()))
connection.commit()
# debug('Committed')
execute("SELECT status FROM job")
# debug("Status after commit "+str(cursor.fetchone()))
Эта раздутая версия оригинального скрипта полна методов отладки, чтобы попытаться понять, что происходит, с помощью time.sleep
чтобы быть в состоянии идти в ногу с тем, что происходит. Я прокомментировал функции отладки в этом извлечении, чтобы упростить чтение того, что на самом деле происходит.
Это результаты от Алисы и Барри:
Алиса
41,351161: Made lock
41,351161: Locked row with status 0
41,351161: Double checked status is {'status': 0}
46,352156: Status before commit {'status': 1}
46,370601: Committed
46,370601: Status after commit {'status': 1} (Sometimes Alice sees 0 here)
Барри
46,352682: Made lock
46,353184: No rows
48,365044: Made lock
48,365044: Locked row with status 0
48,365044: Double checked status is {'status': 0}
53,365062: Status before commit {'status': 1}
53,386910: Committed
53,388846: Status after commit {'status': 1}
Числа в начале вывода - это метка времени, как SECONDS,MICROSECONDS
.
Алиса держит замок на пять секунд, и как только она COMMITS
, Барри берет замок. Тем не менее, Барри видит состояние 0 (во время тестирования есть только одна строка). Это заканчивается тем, что они думают, что могут обрабатывать работу.
Я не уверен, почему Барри читает статус 0 после фиксации. Это откат? Он читает кеш старого значения, когда он назвал его SELECT
? Помог бы другой уровень изоляции (не казался)?
Когда я выполняю этот код в закрытой среде, без остальной части программы, он работает !!! Барри не сообщает никаких строк, как только он наконец получит замок. Я не понимаю, как это может измениться? Я предполагаю, что это означает, что в другом месте скрипта что-то не так. Но что это может быть? Это довольно изолированная транзакция. Вызов COMMIT
перед этим кодом ничего не меняет, я подумал, что предыдущая транзакция не была закрыта должным образом.
Алиса и Барри являются единственными процессами, обращающимися к базе данных при тестировании (кроме phpmyadmin).
Я запускаю механизм MariaDB
InnoDB, с MySQLdb
(mysqlclient) в качестве соединителя для Python. Насколько я могу судить, AUTOCOMMIT
выключен. Сценарий довольно длинный, поэтому я только отправил несколько строк. Я попытаюсь разделить его в ближайшие несколько дней, чтобы попытаться изолировать проблему или создать небольшой пример скрипта. Я не могу понять, как заставить MariaDB распечатать журнал изменений и блокировок, но если я это сделаю, я обновлю это сообщение.
Любые идеи, предложения или комментарии будут потрясающими.
Хорошего дня!
Следуя совету Solarflare по сокращению бит программы, я в конце концов обнаружил проблему, приводящую к удаленной части кода, которая функционировала без ошибок, но совершенно по-другому после перехода от T-SQL к SQL.
Сначала я подумал, что проблема с SQL-сервером, но это была просто неприятная ошибка. По какой-то причине я получаю ужасные тупики, но еще одно удовольствие смотреть.
Спасибо за комментарий и время, проведенное вами, объясняя, что может быть неправильно, я сожалею, что это была просто личная глупая проблема
Хорошего дня!
READ COMMITTED
.
create
/alter
; попробуйтеconnection.start_transaction()
вместоexecute
(сделайте это первым); проверить настройки уровня изоляции; проверьте, используете ли вы пул соединений.