SQLAlchemy未返回选定数据
我在一个自己搭建的应用中使用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个查询,MySQLdb会为每个查询创建一个结果集。你需要先获取第一个结果,然后调用 cursor.nextset()
来获取第二个结果,依此类推。
这虽然回答了你的问题,但对你来说并没有太大帮助,因为它无法解决锁定的问题。你需要先理解一下 FOR UPDATE
是怎么回事:它会锁定返回的行,直到事务结束。为了避免长时间等待锁定,你需要尽量缩短这个过程:先执行 SELECT ... FOR UPDATE
,然后 UPDATE SET in_use=1 ...
,最后 COMMIT
。其实你不需要把这三条语句放在一个SQL语句里,三次 execute()
调用也是可以的。但你必须在进行长时间计算之前提交事务,否则锁会保持太久,而更新 in_use
(离线锁)就没有意义了。当然,你也可以使用ORM来做同样的事情。