我有一种带有随机选择的彩票系统,我正在努力优化。
我有以下限制:
这是我当前的查询。它是一个ARBITRARY PICK但现在我想改变它/重新创建它以便想要一个RANDOM PICK(但是避免通常的random()限制1需要经过所有1M行并且非常慢,甚至可能避免偏移(?)因为大型数据集的速度非常慢。)
UPDATE tickets s
SET available = false
FROM (
SELECT id
FROM tickets
WHERE deal_id = #{@deal.id}
AND available
AND pg_try_advisory_xact_lock(id)
LIMIT 1
FOR UPDATE
) sub
WHERE s.id = sub.id
RETURNING s.name, s.id
如何更改此查询以从任意选择移动到RANDOM选择并尽可能快地查询?
如果可能的话,我想要有形的查询建议,我会在我的应用中尝试。
答案 0 :(得分:1)
在关注ID时,表中的ID之间可能存在巨大的差距 整个但在“特定交易的门票”内(见下面的查询) ID之间没有任何差距(甚至不是最小的),我 假设可以找到最合适的查询。
这让您的生活更轻松。我会使用以下方法。
0)在(deal_id, available, id)
上创建索引。
1)获取给定MIN
的ID的MAX
和deal_id
值。
SELECT MIN(id) AS MinID, MAX(id) AS MaxID
FROM tickets
WHERE deal_id = #{@deal.id}
AND available
如果此查询导致索引扫描而不是搜索,请对MIN
和MAX
使用两个单独的查询。
2)在找到的范围RandID
中生成一个随机整数[MinID; MaxID]
。
3)选择ID=RandID
行。查询应该寻找索引。
UPDATE tickets s
SET available = false
FROM (
SELECT id
FROM tickets
WHERE deal_id = #{@deal.id}
AND available
AND id = @RandID
AND pg_try_advisory_xact_lock(id)
LIMIT 1
FOR UPDATE
) sub
WHERE s.id = sub.id
RETURNING s.name, s.id
如果存在可以添加或删除行的并发进程,请考虑将事务隔离级别提高到可序列化。
说完这一切后,我意识到它不起作用。当您说,ID没有间隙时,您很可能意味着具有相同deal_id
的ID没有间隙(无论available
列的值如何),而不是ID具有相同的deal_id
和available=true
。
只要第一个随机行设置为available=false
,ID就会有差距。
第二次尝试
将float
列RandomNumber
添加到tickets
表中,该表应包含范围(0,1)中的随机数。无论何时向此表添加行,都会生成此随机数并将其保存在此列中。
在(deal_id, available, RandomNumber)
上创建索引。
按此RandomNumber
订购,以选择仍然可用的随机行。查询应该寻找索引。
UPDATE tickets s
SET available = false
FROM (
SELECT id
FROM tickets
WHERE deal_id = #{@deal.id}
AND available
AND pg_try_advisory_xact_lock(id)
ORDER BY RandomNumber
LIMIT 1
FOR UPDATE
) sub
WHERE s.id = sub.id
RETURNING s.name, s.id