SQLAlchemy не возвращает выбранные данные
-
10-07-2019 - |
Вопрос
Я использую SQLAlchemy в качестве ORM в приложении, которое я создавал в течение некоторого времени.
До сих пор внедрение и использование ORM было довольно безболезненным, однако недавняя функция, над которой я работаю, требует реализации в стиле постоянной и распределенной очереди (list & worker), которую я встроил в MySQL и Python.
Все это работало довольно хорошо, пока я не протестировал его в масштабируемой среде.Я использовал блокировку на уровне строк InnoDB, чтобы гарантировать, что каждая строка читается только один раз, пока строка заблокирована, я обновляю 'in_use
"ценность, чтобы убедиться, что другие не ухватятся за вход.
Поскольку MySQL не предлагает метод "NOWAIT", подобный Postgre или Oracle, я столкнулся с проблемами блокировки, когда рабочие потоки зависают и ждут, пока заблокированная строка станет доступной.
В попытке преодолеть это ограничение я попытался поместить всю необходимую обработку в один оператор и запустить его через ORM execute()
метод, однако, SQLAlchemy отказывается возвращать результат запроса.
Вот пример.
Мой оператор SQL является:
SELECT id INTO @update_id FROM myTable WHERE in_use=0 ORDER BY id LIMIT 1 FOR UPDATE;
UPDATE myTable SET in_use=1 WHERE id=@update_id;
SELECT * FROM myTable WHERE id=@update_id;
И я запускаю этот код в консоли:
engine = create_engine('mysql://<user details>@<server details>/myDatabase', pool_recycle=90, echo=True)
result = engine.execute(sqlStatement)
result.fetchall()
Только для того, чтобы получить этот результат
[]
Я уверен, что инструкция запущена, поскольку я вижу, как обновление вступает в силу в базе данных, и если я выполняю через терминал mysql или другие инструменты, я получаю измененную строку, возвращенную.Это просто похоже на SQLAlchemy, которая не хочет подтверждать возвращаемую строку.
Есть ли что-то конкретное, что необходимо сделать, чтобы гарантировать, что ORM получит ответ?
Ваше здоровье
Решение
Вы выполнили 3 запроса, и MySQLdb создает результирующий набор для каждого.Вы должны получить первый результат, затем вызвать cursor.nextset()
, принести вторую и так далее.
Это отвечает на ваш вопрос, но не будет полезно для вас, потому что это не решит проблему блокировки.Сначала вы должны понять, как работает FOR UPDATE:он блокирует возвращенные строки до конца транзакции.Чтобы избежать долгого ожидания блокировки, вы должны сделать его как можно короче: SELECT ... FOR UPDATE
, UPDATE SET in_use=1 ...
, COMMIT
.На самом деле вам не нужно помещать их в один оператор SQL, 3 execute()
звонки тоже будут в порядке вещей.Но вы должны зафиксировать перед длительным вычислением, иначе блокировка будет удерживаться слишком долго и обновление in_use
(автономная блокировка) не имеет смысла.И, конечно, вы можете сделать то же самое, используя ORM.