有一个学生的type
属性为 4,type
属性的最小值可以是 1。
在邮政中
在会话 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;
在第 2 节中,我同时运行:
UPDATE students SET type = type - 1 WHERE id = 122;
我得到的结果是一个例外,即学生的type
不能低于1在会话 2 中,因为我已经将同一学生的type
设置为值1,并且由于会话处于该学生的独占锁定状态,会话 2 必须等待。
In 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()
在会话 2 中:
user = Student.query.filter(Student.id == 122).first()
user.type -= 1
db.session.commit()
我得到的结果是用户的type
等于 3,而我应该得到一个例外。 会话 1 中的事务更改将被会话 2 中的事务更改覆盖,即使在会话 2 中的事务中db.session.commit()
后,它会等到会话 1 中的事务结束。
但是在会话 2 中,当我同时运行会话 1 时:
user = Student.query.filter(Student.id == 122).update({"type": Student.type - 1})
db.session.commit()
我得到了正确的输出,即完整性错误显示尝试将学生 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()
和
user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type -= 1
db.session.commit()
为了使FOR UPDATE
正常工作,所有打算更新行的涉及事务都需要使用它。
在您的示例中,会话 2 未使用with_for_update
。 由于您没有告诉它使用FOR UPDATE
,因此可以自由读取行的旧值(因为新值尚未提交,并且锁不会阻止纯读取器(,然后修改该内存值,然后将其写回。
如果您不想在读取行的所有位置使用FOR UPDATE
以更改它,则可以改为在任何地方使用isolation level serializable
。 但是,如果这样做,事情可能不会阻塞,而是在提交之前看起来会成功,然后抛出需要捕获和处理的序列化错误。
注意:您的预编辑示例应该有效,因为两个会话都标有with_for_update
。