如何限制行并防止预订系统的竞争条件

时间:2015-02-12 18:47:16

标签: python postgresql sqlalchemy

我正在建立产品预订系统。假设我有一个带有名称和数量字段的产品表(“my-tshirt”,2)。我还有一个保留表,它将产品ID映射到用户ID。如何确保该表只有两个保留条目?我怎样才能防止任何竞争条件?我需要担心吗?我读过航空公司和酒店人们已经说了15分钟的会议,但我更愿意做这个实时,一旦你点击保留按钮,我们可以立即(瞬间0.1-5.0秒)告诉你,如果你是否有预订。

我正在使用Tornado,SQLAlchemy,Postgres,并且还使用Redis进行各种操作。

1 个答案:

答案 0 :(得分:2)

要回答你的问题,是的,你需要担心竞争条件。我建议处理问题的方法是使用FOR UPDATE select语句。这将允许您找到您正在寻找的行,锁定它,并在没有其他线程进入的情况下更新它,并在您修改它时查询或修改它。这样的事情可以解决问题:

try:
    productToUpdate = session.query(Product).filter(Product.name == "my-tshirt", Product.quantity > 0).with_for_update().one() #assume Product.name is unique and name exists
except sqlalchemy.orm.exc.NoResultFound as e:
    return "No reservations available" #Handle no reservation case here
productToUpdate.quantity -= 1
newReservation = Reservation()
newReservation.product = productToUpdate #assume relationship setup
newReservation.user = userForReservation #assume relationship setup
session.commit()

编辑:

如果您不想更新产品数量并保留单独的预订数量,有两种方法可以处理。

正如您在问题中所提出的,第一个是从预订表中获取预订金额,并将其与可用的产品数量进行比较。我建议不要这样做,因为为了避免竞争条件,您需要锁定保留表以防止插入行,这会影响其他线程,并且可能会根据您的表使用情况大幅减慢速度。在没有锁定表的情况下,在您创建另一个条目之前,在查询总计之后,另一个线程可以插入预留。

第二种方法,我的建议是在Product表中添加一列用于预订计数,并在预订表的同时更新该列。这仍然可以在读取后将预留插入到表中,但如果您的应用程序没有插入到预订表中而没有获得产品表上的锁定,则可以确保您的数据准确无误。

try:
    productToUpdate = session.query(Product).filter(Product.name == "my-tshirt", Product.quantity - Product.reservations >= quantityToReserve).with_for_update().one() #assume Product.name is unique and name exists
except sqlalchemy.orm.exc.NoResultFound as e:
    return "No reservations available" #Handle no reservation case here
productToUpdate.reservations += quantityToReserve
#can put in another query and logic here to see if you're updating a reservation, or creating a new one
newReservation = Reservation(reservationQuantity = quantityToReserve)
newReservation.product = productToUpdate #assume relationship setup
newReservation.user = userForReservation #assume relationship setup
session.commit()

有关postgres的FOR UPDATE的一些阅读是here

对with_for_update()的一些阅读是here