Вопрос

Я использую 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top