postgresql 9.4/9.5 - Select...用于更新具有高读取和写入的大型数据集上的单个随机行

postgresql 9.4/9.5 - Select...for update one single random row on a large dataset with high Reads and Writes

我有一种随机选择的彩票系统,我正在尝试优化。

我有以下限制:

这是我当前的查询。这是一个 ARBITRARY PICK 但现在我想改变 it/recreate 它想要一个 RANDOM PICK (但避免通常的 random() 限制 1 需要遍历所有 1M 行并且非常慢,甚至可能避免使用 offset(?),因为它在大型数据集上的运行速度非常慢。

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

如何更改此查询以从任意选择移动到随机选择并使用最快的查询?

如果可能的话,我想要具体的查询建议,我会在我的应用程序中尝试。

regardind IDs, there can be huge gaps between ids in the table as a whole BUT inside the 'tickets from a specific deal' (see query below) there is not any gap between IDs (not even the smallest), which i presume can matter to find the most appropriate query.

这会让您的生活更轻松。我会使用以下方法。

0) 在 (deal_id, available, id).

上创建索引

1) 获取给定 deal_id.

MINMAX
SELECT MIN(id) AS MinID, MAX(id) AS MaxID
FROM   tickets
WHERE  deal_id = #{@deal.id}
AND    available

如果此查询导致索引扫描而不是查找,请对 MINMAX 使用两个单独的查询。

2) 在[MinID; MaxID].

范围内生成一个随机整数RandID

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_idavailable=true.

一旦第一个随机行设置为 available=false,ID 就会出现间隙。


第二次尝试

floatRandomNumber 添加到 tickets table 中,该列应包含 (0,1) 范围内的随机数。每当您向此 table 添加一行时,都会生成这样的随机数并将其保存在此列中。

(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