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 结果)。
想知道为什么会出现这种情况。
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.