postgresql 9.4 / 9.5 - 选择...用于更新具有高读取和写入

时间:2015-10-26 19:24:54

标签: sql postgresql random postgresql-9.4

我有一种带有随机选择的彩票系统,我正在努力优化。

我有以下限制:

  • 我需要将SELECT ... FOR UPDATE仅应用于其中deal_id是我的app当前交易的行(即我不将它应用于WHOLE表/表的所有行,仅例如,在例如deal_id = 3的情况下)
  • 我只需要选择available = true
  • 的行
  • 我只需要输出1行(当玩家买票时我必须检查这些100万行并随意为他选择一行(只有一个如此多的Stackoverflow解决方案,如here或TABLESAMPLE不能轻松工作)
  • 我通常有大约100万行匹配deal_id = 3(例如3)和available = true(在任何给定时间总共约30M行)
  • 我有非常高的READS和WRITES =>在表上大约有50到100多个并发读取,因此相同数量的大约写入(如果选择,可用= true在SELECT..for UPDATE中更改为'false')
  • 我正在实施一行上的选择/更新时锁定。现在我正在使用SELECT..FOR UPDATE和pg_try_advisory_xact_lock(当postgresql 9.5退出测试版时,我将使用SKIP LOCKED)
  • 我需要快速的速度。我的目标是查询< 5ms的
  • 尊重ID,整个表中的ID之间可能存在巨大差距但是在“来自特定交易的票据”中(参见下面的查询)ID之间没有任何差距(甚至不是最小的),其中我presume可以找到最合适的查询。

这是我当前的查询。它是一个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选择并尽可能快地查询?

如果可能的话,我想要有形的查询建议,我会在我的应用中尝试。

1 个答案:

答案 0 :(得分:1)

  在关注ID时,表中的ID之间可能存在巨大的差距   整个但在“特定交易的门票”内(见下面的查询)   ID之间没有任何差距(甚至不是最小的),我   假设可以找到最合适的查询。

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

0)在(deal_id, available, id)上创建索引。

1)获取给定MIN的ID的MAXdeal_id值。

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

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

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

只要第一个随机行设置为available=false,ID就会有差距。

第二次尝试

floatRandomNumber添加到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