如何设计一个高并发的客服预约系统?
How to design a booking system with high concurrency for customer services?
我正在为客户服务建立一个预订系统,但在处理某个特定时间的员工(将提供服务)的双重预订时,我遇到了困难。我研究了多个相关问题,但它们都解释了可预订或更新的房间、座位、产品等的并发解决方案。在我们的例子中,客户预订了一个时间段的服务,该时间段通过在给定时间至少有一名空闲员工来提供。让我解释一下...
我们的数据库现在看起来像这样(为简单起见,其他 table 和字段已被省略):
如果给定时间不与该员工的“不可用”中的行重叠,则该员工在给定时间有空。此外,我们通过向 Unavailable table 添加约束来防止为同一员工插入时间重叠的新行。
我们如何才能允许客户预订同一时间段的服务,只要那个时间有可用的员工,同时具有高并发性并避免为此类事务序列化整个 table?
添加一个新的 table 表示时隙是否更容易?如果是这样,这种设计将如何实现最佳并发性?
我会在 unavailable
中添加一列 expires_at timestamp
。最终预订的存储值为 infinity
,当您向客户建议时间段时,您添加了一个具有有限到期日期的条目。
现有的排除限制将确保没有两个建议的预订可以相互重叠并与最终预订重叠。
在您添加新预订或建议预订之前,运行
DELETE FROM unavailable WHERE expires_at < current_timestamp;
如果列上有索引,这会很快。
问题
所以问题是事务 1 和事务 2 将读取相同的可用员工列表,然后等待前一个事务提交,但是事务 2 将看不到事务 1 的预订插入。
备选方案 1
该问题的一个解决方案是将 select 拆分为三个不同的 sql。
Select * 来自员工更新。
这将使另一个事务 2 挂起,直到事务 1 完成插入。
然后查询unavailable和booked:
SELECT 员工 FROM 不可用 WHERE start_time2 和 end_time>?1)
SELECT 员工 FROM 预定 WHERE start_time2 and end_time>?1
从所有获取的员工的这两个查询中删除 ID。
然后在剩下的员工列表中插入第一个员工的预订。
一旦提交事务 1 现在将看到新插入的预订,因此可用员工列表将不包括事务 1 预订的员工。
Transaction 1
Transaction 2
read employee
read employee
read bookings
suspend
read unavailables
...
insert booking
...
commit
...
read bookings
read unavailables
insert booking
commit
选项 2
另一种解决方案是添加一个时间段 table,其中定义了服务的间隔,而不是使用更新 select 所有员工(它为每一行获取写锁在员工 table).
Select 预订的时间也在新的时间段中参考 table 并锁定该特定时间行,因此时间重叠的预订将不得不相互等待,而不重叠的预订时间可以同时预定
我正在为客户服务建立一个预订系统,但在处理某个特定时间的员工(将提供服务)的双重预订时,我遇到了困难。我研究了多个相关问题,但它们都解释了可预订或更新的房间、座位、产品等的并发解决方案。在我们的例子中,客户预订了一个时间段的服务,该时间段通过在给定时间至少有一名空闲员工来提供。让我解释一下...
我们的数据库现在看起来像这样(为简单起见,其他 table 和字段已被省略):
如果给定时间不与该员工的“不可用”中的行重叠,则该员工在给定时间有空。此外,我们通过向 Unavailable table 添加约束来防止为同一员工插入时间重叠的新行。
我们如何才能允许客户预订同一时间段的服务,只要那个时间有可用的员工,同时具有高并发性并避免为此类事务序列化整个 table?
添加一个新的 table 表示时隙是否更容易?如果是这样,这种设计将如何实现最佳并发性?
我会在 unavailable
中添加一列 expires_at timestamp
。最终预订的存储值为 infinity
,当您向客户建议时间段时,您添加了一个具有有限到期日期的条目。
现有的排除限制将确保没有两个建议的预订可以相互重叠并与最终预订重叠。
在您添加新预订或建议预订之前,运行
DELETE FROM unavailable WHERE expires_at < current_timestamp;
如果列上有索引,这会很快。
问题
所以问题是事务 1 和事务 2 将读取相同的可用员工列表,然后等待前一个事务提交,但是事务 2 将看不到事务 1 的预订插入。
备选方案 1
该问题的一个解决方案是将 select 拆分为三个不同的 sql。
Select * 来自员工更新。 这将使另一个事务 2 挂起,直到事务 1 完成插入。
然后查询unavailable和booked: SELECT 员工 FROM 不可用 WHERE start_time2 和 end_time>?1) SELECT 员工 FROM 预定 WHERE start_time2 and end_time>?1
从所有获取的员工的这两个查询中删除 ID。 然后在剩下的员工列表中插入第一个员工的预订。 一旦提交事务 1 现在将看到新插入的预订,因此可用员工列表将不包括事务 1 预订的员工。
Transaction 1 | Transaction 2 |
---|---|
read employee | read employee |
read bookings | suspend |
read unavailables | ... |
insert booking | ... |
commit | ... |
read bookings | |
read unavailables | |
insert booking | |
commit |
选项 2
另一种解决方案是添加一个时间段 table,其中定义了服务的间隔,而不是使用更新 select 所有员工(它为每一行获取写锁在员工 table).
Select 预订的时间也在新的时间段中参考 table 并锁定该特定时间行,因此时间重叠的预订将不得不相互等待,而不重叠的预订时间可以同时预定