SQL行锁定不起作用with_for_update?



有一个学生的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

最新更新