SQLAlchemy with_for_update 行锁定不起作用?

San*_*ain 5 python postgresql locking sqlalchemy flask-sqlalchemy

有一个学生,其type属性为4,属性最小值type可以为1。

在 postgres 中

在会话 1 中,我独占锁定并更新学生表中的一行:

BEGIN;
LOCK TABLE students IN ROW EXCLUSIVE MODE;
SELECT * FROM students WHERE id = 122  FOR UPDATE;
UPDATE students SET type = 1 WHERE id = 122;
END;
Run Code Online (Sandbox Code Playgroud)

在第 2 节中,我同时运行:

UPDATE students SET type = type - 1 WHERE id = 122;
Run Code Online (Sandbox Code Playgroud)

我得到的结果是一个例外,即学生在会话 2 中type不能低于1 ,因为我已经将同一个学生的type值设置为1,并且由于会话处于该学生的独占锁定状态,因此会话 2 必须等待。


在 Flask-SQLAlchemy 中

我尝试使用用户重新创建相同的结果,type默认情况下属性设置为 4。

在第 1 节中:

user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type = 1
db.session.commit()
Run Code Online (Sandbox Code Playgroud)

在第 2 节中:

user = Student.query.filter(Student.id == 122).first()
user.type -= 1
db.session.commit()
Run Code Online (Sandbox Code Playgroud)

我得到的结果是 usertype等于 3,而我应该得到一个异常。会话 1 中的事务更改将被会话 2 中的事务更改覆盖,即使在db.session.commit()会话 2 中的事务之后它会等到会话 1 中的事务结束。


但在会话 2 中,当我与会话 1 同时运行此命令时:

user = Student.query.filter(Student.id == 122).update({"type": Student.type - 1})
db.session.commit()
Run Code Online (Sandbox Code Playgroud)

我得到了正确的输出,即完整性错误,显示尝试将学生 122stype属性设置为 0(不覆盖会话 1 结果)。

想知道为什么会出现这种情况。

jja*_*nes 6

2 个会话应该如下所示:

user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type = 1
db.session.commit()
Run Code Online (Sandbox Code Playgroud)

和

user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type -= 1
db.session.commit()
Run Code Online (Sandbox Code Playgroud)

为了FOR UPDATE正常工作,所有涉及更新该行的事务都需要使用它。

在您的示例中,会话 2 未使用with_for_update. 既然你没有告诉它使用FOR UPDATE,所以它可以自由地读取该行的旧值(因为新值尚未提交,并且锁不会阻止纯读取器),然后修改它的内存中值,然后写回来。

如果您不想FOR UPDATE在读取行的任何地方都使用它来更改它,则可以使用isolation level serializable随处。但是,如果您这样做,事情可能不会阻塞,而是在提交之前看起来会成功,然后抛出需要捕获和处理的序列化错误。

注意:您的预编辑示例应该可以正常工作,因为两个会话都标有with_for_update.