我正在努力解决如何在这种情况下授予以下操作的一致性:我们正在开发一个预订门户,您可以在其中签署课程。每个课程都有一些直接预订:当可用的预订结束时,用户仍然可以签名,但是等待名单会有所下降。
所以情况就是这样:例如,让我们想象一下,有一个课程有15个可用的插槽,我可以预订。在一个简单的逻辑中,当我进行预订时,系统必须决定我是否在直接预订列表(15个可用插槽之一)或等待列表中。为了做到这一点,我们必须计算总直接预订是否小于可用的预留总量,如伪代码这样:
INSERT new_partecipant IN table_partecipants;
(my_id) = SELECT @@IDENTITY;
(total_reservations_already_made) = SELECT COUNT(*) FROM table_partecipants WHERE flag_direct_reservation=1;
if total_reservations_already_made <= total_reservations_available then
UPDATE table_partecipants SET flag_direct_reservation=1 WHERE id=my_id
问题是:如何管理并发以确保正确管理两个订阅?如果我只剩下一个预留而两个用户同时申请,我认为COUNT操作的结果可能会给两个请求提供相同的结果并插入列表中的两个用户。 / p>
正确的锁定方式(或类似程序)是什么,以确保如果用户启动订阅程序,在请求完成之前没有人可以完成相同的任务?
好的,似乎我们找到了一个没有锁定的解决方案,任何评论都表示赞赏:
插入
INSERT INTO table1 (field1, field2, ..., fieldN, flag_direct_reservation)
SELECT @field1, @field2, ..., @fieldN, ,CASE WHEN (SELECT COUNT(*) FROM sometable WHERE course=@course_id) > @max_part THEN 1 ELSE 0 END
UPDATE (仅用于确定订阅状态,如果订阅删除)
UPDATE corsi_mytable p1 INNER JOIN
(
SELECT COUNT(*) as actual_subscritions
FROM mytable
WHERE course=@course_id
)p2
SET p1.flag_direct_reservation= CASE WHEN p2.actual_subscritions > @max_part THEN 0 ELSE 1 END
WHERE p1.id =@first_waiting_id;
通过这种方式,操作只由一个SQL语句执行,事务引擎应该确保操作的一致性。
答案 0 :(得分:1)
不要锁定桌子。而是尝试预订一行,如果失败,将它们扔到等待名单上。例如:
UPDATE table_partecipants SET booking_id=? WHERE booking_id IS NULL LIMIT 1
此处的WHERE
条款应包含任何其他排除因素,例如,如果它是正确的日期。如果查询成功修改了预订的行。如果没有,它就卖光了。无需锁定。
这里的booking_id
是一些独特的值,可用于将此课程与预订者联系起来。您可以删除因超额预订而未使用的任何记录。
答案 1 :(得分:0)
我会建议你再次锁定整个表格。去行锁。
您可以拥有一个单独的表,其中只有1行,您的预订计数器为15,并在添加新预订之前更新此计数器。 因此,您确定锁是由这个单独的表管理的,这使您的预订表可以在您不关心计数器的任何其他情况下进行更新。