SELECT… FOR UPDATE выбор старых данных после фиксации

0

Добрый день,

Это обновление для моего старого сообщения в SQL. Одновременные транзакции игнорируют блокировки друг друга??? DEADLOCK [InnoDB, Python], как я понял позже, что проблема не имеет ничего общего с тем, что я думал о проблеме. Я пытаюсь создать эквивалентный MySQL для сценария на основе T-SQL для клиента.

У меня два идентичных сценария, Алиса и Барри работают одновременно. Их цель -

  1. SELECT * FROM job WHERE status = 0 LIMIT 1 FOR UPDATE
  2. UPDATE job SET status = 1 где идентификаторы job совпадают, делают некоторые другие вещи, а затем...
  3. 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 распечатать журнал изменений и блокировок, но если я это сделаю, я обновлю это сообщение.

Любые идеи, предложения или комментарии будут потрясающими.

Хорошего дня!

  • 1
    Если вы «выполняете этот код в закрытой среде», это работает, если вы добавляете больше кода, это не так? Нам сложно диагностировать, если вы покажете нам только ту часть, которая работает. В идеале вы начинаете с рабочего кода, затем добавляете код, пока он не перестанет работать (см. Mvce ). Вы часто сами найдете проблему таким образом. Я бы проверил все, что может вызвать исключение; проверьте, есть ли у вас код create / alter ; попробуйте connection.start_transaction() вместо execute (сделайте это первым); проверить настройки уровня изоляции; проверьте, используете ли вы пул соединений.
Теги:
transactions
locking
commit

1 ответ

0
Лучший ответ

Следуя совету Solarflare по сокращению бит программы, я в конце концов обнаружил проблему, приводящую к удаленной части кода, которая функционировала без ошибок, но совершенно по-другому после перехода от T-SQL к SQL.

Сначала я подумал, что проблема с SQL-сервером, но это была просто неприятная ошибка. По какой-то причине я получаю ужасные тупики, но еще одно удовольствие смотреть.

Спасибо за комментарий и время, проведенное вами, объясняя, что может быть неправильно, я сожалею, что это была просто личная глупая проблема

Хорошего дня!

  • 0
    В случае, если это кому-нибудь поможет: чтобы это работало (то есть, чтобы избежать взаимоблокировок), я должен был установить уровень изоляции (не больше, чем) READ COMMITTED .

Ещё вопросы

Сообщество Overcoder
Наверх
Меню