【发布时间】:2018-11-07 08:42:32
【问题描述】:
我有接收汽车预订请求的应用程序,cars 表中的预订汽车状态应设置为in_use。
通常会这样做:
def reserve_car(user_id)
car = Car.find_by(status: 'available')
car.update_columns(user_id: user_id, status: 'in_use')
car
end
但是这种解决方案在选择可用汽车和更新其状态之间造成了差距,然后当应用程序必须处理相对大量的请求时就会出现两难境地,因此在同一时间很少有请求尝试预订同一辆车。
为了降低这种风险,我在同一个 SQL 查询中查找并更新了可用的汽车。此外,我将可用汽车列表顺序随机化以进一步减少它。为了随机化结果,我不使用ORDER BY RAND() LIMIT 1,因为AFAIK它为每条记录生成随机ID,仅在将结果限制为指定数字-1时对其进行排序。这效率不高,因为汽车表预计会有大量记录未来(100k+)。
所以我想出了这个解决方案:
def reserve_car(user_id)
sql = <<-SQL
UPDATE
cars AS r0,
(
SELECT
r1.id
FROM
cars AS r1
JOIN (
SELECT
(
RAND() * (
SELECT
MAX(id)
FROM
cars
)
) AS id
) AS r2
WHERE
r1.status = 'available'
AND r1.id >= r2.id
LIMIT
1
) AS r3
SET
r0.status = 'in_use',
r0.user_id = #{ActiveRecord::Base.connection.quote(user_id)}
WHERE
r0.id = r3.id
SQL
updates = ActiveRecord::Base.connection.exec_update(sql)
car = Car.find_by(user_id: user_id, status: 'in_use')
if car.present?
car
else
raise "Failed to reserve car. Updates: #{updates}"
end
end
但我经常收到异常“无法预订汽车。更新 0”,尽管事实上我知道有很多可用的汽车。
可能出了什么问题?也许有人可以提出更好的解决方案?
谢谢你????
【问题讨论】:
标签: mysql sql ruby-on-rails ruby