【问题标题】:Postgres not using index inside "IN" clause for UUIDPostgres没有在UUID的“IN”子句中使用索引
【发布时间】:2015-06-29 15:36:49
【问题描述】:

我有一个表users,索引在id

我有另一个表 orders 属于 users 并且有一个 user_id 字段也被索引

我有以下疑问:

SELECT * FROM users
WHERE id IN (
    SELECT user_id FROM orders
    WHERE user_id = '4CF93940-390D-4D70-BE62-61AFC73663BF'
);

这里 Postgres 正确使用了这两个索引(通过检查 EXPLAIN 的输出)。

但是,如果我这样做:

SELECT * FROM users
WHERE id IN (
    SELECT user_id FROM orders
);

(从内部 Select 中移除 where 子句)

它根本不使用任何索引。它不使用orders.user_id 上的索引,因为我选择了所有它,但是为什么它不使用users.id 上的索引呢?

这是两个查询的解释输出:

Nested Loop Semi Join  (cost=4.36..22.54 rows=1 width=16)
   ->  Index Only Scan using user_id on users  (cost=0.15..8.17 rows=1 width=16)
         Index Cond: (id = '4cf93940-390d-4d70-be62-61afc73663bf'::uuid)
   ->  Bitmap Heap Scan on orders  (cost=4.21..14.35 rows=7 width=16)
         Recheck Cond: (user_id = '4cf93940-390d-4d70-be62-61afc73663bf'::uuid)
         ->  Bitmap Index Scan on order_user_id  (cost=0.00..4.21 rows=7 width=0)
               Index Cond: (user_id = '4cf93940-390d-4d70-be62-61afc73663bf'::uuid)



Hash Join  (cost=30.88..67.21 rows=885 width=16)
    Hash Cond: (users.id = orders.user_id)
    ->  Seq Scan on users  (cost=0.00..27.70 rows=1770 width=16)
    ->  Hash  (cost=28.38..28.38 rows=200 width=16)
        ->  HashAggregate  (cost=26.38..28.38 rows=200 width=16)
                Group Key: orders.user_id
                ->  Seq Scan on orders  (cost=0.00..23.10 rows=1310 width=16)

更新

所以运行这个查询:

EXPLAIN SELECT * FROM users
WHERE id IN ('4CF93940-390D-4D70-BE62-61AFC73663BF', '4CF93940-390D-4D70-BE62-61AFC73663BF');

正确使用索引。所以基本上为什么是

WHERE id IN ('4CF93940-390D-4D70-BE62-61AFC73663BF', '4CF93940-390D-4D70-BE62-61AFC73663BF')

不同于

WHERE id IN (SELECT user_id FROM orders);

似乎两者都只是一个 id 数组。

【问题讨论】:

  • 它不使用索引,因为当从表orders 返回所有行(或大部分行)时,seq 扫描效率更高。在第二个示例中,Postgres 期望返回 1310 行中的 885 行。
  • @a_horse_with_no_name 是的,我明白为什么它不使用orders 的索引,但是一旦它有一个数组(或集合)或user_ids 为什么不使用@987654338 上的索引@找到所有用户?
  • 正如我所说:因为如果需要检索表中的大部分行,使用 seq 扫描会更有效。索引扫描每行需要比 seq 扫描更多的随机 I/O。按索引查找行需要搜索叶节点的索引(1-3 次 I/O 操作),然后进行另一个 I/O 操作来检索表的行。如果你重复很多很多次,它会变得非常慢。另一方面,seq 扫描通过单个 I/O 操作检索多行 - 如果您需要的不仅仅是几行,效率会更高
  • @a_horse_with_no_name 感谢您的回复。是的,我知道在获取表的大部分内容时,seq 扫描效率更高。但是,1)这些是我现在创建的玩具表(以隐藏我的表的复杂性),它们没有任何内容,因此在 2 种情况下应该没有什么不同(请参阅我的更新)。 2)我将我的 id 列从 UUID 更改为整数,现在它按预期两次使用索引。似乎是 UUID 上的索引有问题。

标签: database postgresql indexing


【解决方案1】:

users 中有更多的条目相对于子选择返回的条目数,Postgres 可能会开始使用索引。

此外,id in (list) 可以与 id in (sub-select) 非常不同,因为 Postgres 能够将构造视为连接(它确实如此,就像 EXPLAINed)

【讨论】:

    猜你喜欢
    • 2018-06-07
    • 2018-03-23
    • 2016-11-01
    • 2013-02-03
    • 1970-01-01
    • 2010-10-09
    • 2016-08-13
    • 2020-10-02
    • 2021-08-28
    相关资源
    最近更新 更多