SQLAlchemy未返回选定数据

1 投票
1 回答
2622 浏览
提问于 2025-04-15 16:31

我在一个自己搭建的应用中使用SQLAlchemy作为ORM(对象关系映射工具)。

到目前为止,使用起来还算顺利,不过最近我在做一个功能,需要一个持久的、分布式的队列(类似列表和工作者的模式),我已经在MySQL和Python中实现了这个功能。

在小规模测试时一切都很顺利,但当我在更大规模的环境中测试时,就遇到了一些问题。

我使用了InnoDB的行级锁定,确保每一行数据在被锁定时只会被读取一次,同时我会更新一个叫做'in_use'的值,以防其他人同时访问这条记录。

但是,由于MySQL没有像Postgre或Oracle那样的“NOWAIT”方法,我遇到了锁定问题,导致工作线程挂起,等待被锁定的行变得可用。

为了克服这个限制,我尝试把所有需要的处理放在一个语句中,并通过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能够接收到响应呢?

谢谢!

1 个回答

3

你执行了3个查询,MySQLdb会为每个查询创建一个结果集。你需要先获取第一个结果,然后调用 cursor.nextset() 来获取第二个结果,依此类推。

这虽然回答了你的问题,但对你来说并没有太大帮助,因为它无法解决锁定的问题。你需要先理解一下 FOR UPDATE 是怎么回事:它会锁定返回的行,直到事务结束。为了避免长时间等待锁定,你需要尽量缩短这个过程:先执行 SELECT ... FOR UPDATE,然后 UPDATE SET in_use=1 ...,最后 COMMIT。其实你不需要把这三条语句放在一个SQL语句里,三次 execute() 调用也是可以的。但你必须在进行长时间计算之前提交事务,否则锁会保持太久,而更新 in_use(离线锁)就没有意义了。当然,你也可以使用ORM来做同样的事情。

撰写回答