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
我有一种随机选择的彩票系统,我正在尝试优化。
我有以下限制:
- 我需要将 SELECT...FOR UPDATE 仅应用于 deal_id 是我的应用程序当前交易的行(即我不将其应用于整个 table/on table 的所有行,仅在那些 deal_id= 3 例如)
- 我只需要 select 可用的行=true
- 我只需要输出 1 行(当玩家买票时,我必须去检查这 100 万行并随机为他选择一个(只有一个这么多的 Whosebug 解决方案,如 here 或 TABLESAMPLE 不轻松工作)
- 我通常有大约 100 万行匹配 deal_id = 3(例如 3)并且可用 =true(在任何给定时间总计大约 3000 万行中)
- 我有非常高的 READS 和 WRITES => 在 table 上大约有 50 到 100+ 个并发读取,因此大约相同数量的写入(一旦选择,available= true 更改为 'false' 在 SELECT.. 更新)
- 我在执行某行的 select/update 时锁定了。现在我使用 SELECT..FOR UPDATE 和 pg_try_advisory_xact_lock (当 postgresql 9.5 结束测试时,我将使用 SKIP LOCKED)
- 我需要极快的速度。我定位查询 < 5ms
- 关于 ID,整个 table 中的 ID 之间可能存在巨大差距,但在 'tickets from a specific deal' 内部(请参阅下面的查询)ID 之间没有任何差距(即使是最小的),我认为这对于找到最合适的查询很重要。
这是我当前的查询。这是一个 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
.
的 MIN
和 MAX
值
SELECT MIN(id) AS MinID, MAX(id) AS MaxID
FROM tickets
WHERE deal_id = #{@deal.id}
AND available
如果此查询导致索引扫描而不是查找,请对 MIN
和 MAX
使用两个单独的查询。
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_id
和 available=true
.
一旦第一个随机行设置为 available=false
,ID 就会出现间隙。
第二次尝试
将 float
列 RandomNumber
添加到 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
我有一种随机选择的彩票系统,我正在尝试优化。
我有以下限制:
- 我需要将 SELECT...FOR UPDATE 仅应用于 deal_id 是我的应用程序当前交易的行(即我不将其应用于整个 table/on table 的所有行,仅在那些 deal_id= 3 例如)
- 我只需要 select 可用的行=true
- 我只需要输出 1 行(当玩家买票时,我必须去检查这 100 万行并随机为他选择一个(只有一个这么多的 Whosebug 解决方案,如 here 或 TABLESAMPLE 不轻松工作)
- 我通常有大约 100 万行匹配 deal_id = 3(例如 3)并且可用 =true(在任何给定时间总计大约 3000 万行中)
- 我有非常高的 READS 和 WRITES => 在 table 上大约有 50 到 100+ 个并发读取,因此大约相同数量的写入(一旦选择,available= true 更改为 'false' 在 SELECT.. 更新)
- 我在执行某行的 select/update 时锁定了。现在我使用 SELECT..FOR UPDATE 和 pg_try_advisory_xact_lock (当 postgresql 9.5 结束测试时,我将使用 SKIP LOCKED)
- 我需要极快的速度。我定位查询 < 5ms
- 关于 ID,整个 table 中的 ID 之间可能存在巨大差距,但在 'tickets from a specific deal' 内部(请参阅下面的查询)ID 之间没有任何差距(即使是最小的),我认为这对于找到最合适的查询很重要。
这是我当前的查询。这是一个 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
.
MIN
和 MAX
值
SELECT MIN(id) AS MinID, MAX(id) AS MaxID
FROM tickets
WHERE deal_id = #{@deal.id}
AND available
如果此查询导致索引扫描而不是查找,请对 MIN
和 MAX
使用两个单独的查询。
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_id
和 available=true
.
一旦第一个随机行设置为 available=false
,ID 就会出现间隙。
第二次尝试
将 float
列 RandomNumber
添加到 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