【问题标题】:Optimising UUID Lookups in Postgres在 Postgres 中优化 UUID 查找
【发布时间】:2015-05-24 06:09:54
【问题描述】:

以下所有 uuid 列都使用本机 Postgres uuid 列类型。

有一个查找表,其中 uuid(uuid 类型 4 - 尽可能随机)是主键。定期从该查找表中提取行序列,例如 10,000。

然后,希望使用从查找表中检索到的那组 uuid 来使用刚刚检索到的 UUID 来查询其他表,通常是另外两个表。其他表(表 A 和 B)中的 UUID 不是主键。其他表 A 和 B 中的 UUID 列具有 UNIQUE 约束(btree 索引)。

目前不使用任何类型的 JOIN 进行此合并,只是简单:

  1. 查询查找表,获取 uuid。
  2. 使用 (1) 中的 uuid 查询表 A
  3. 使用 (1) 中的 uuid 查询表 B

问题在于查询 (2) 和 (3) 的速度非常慢。因此,对于表 A 和 B 中的大约 4000 行,尤其是表 A,通常大约需要 30-50 秒。表 A 有大约 60M 行。

仅处理表 A,当使用 EXPLAIN ANALYZE 时,报告为对 A 列中的 uuid 列执行“索引扫描”,并在 EXPLAIN ANALYZE 输出中显示索引条件。

我尝试过各种 WHERE 子句:

  • uuid = ANY ('{
  • uuid = ANY(VALUES('
  • uuid ='uuid1' OR uuid='uuid2' 等等....

并在uuid、btree和hash index上试验了btree(distinct)、hash index table A。

到目前为止,最快(仍然相对较慢)是:btree 和在 WHERE 子句中使用 "ANY ('{" 。

我读过的各种意见:

  • 实际上是在做一个正确的 JOIN,例如跨三个表的 LEFT OUTER JOIN。
  • 使用 uuid 类型 4 是个问题,它是随机生成的 id,而不是基于序列的 id。
  • 可能正在尝试使用 work_mem。

无论如何。想知道其他人是否有任何其他建议?

表:“查找” uuid:输入 uuid。不为空。普通存储。 datetime_stamp:输入 bigint。不为空。普通存储。 Harvest_date_stamp:输入 bigint。不为空。普通存储。 状态:输入 smallint。不为空。普通存储。 索引: "lookup_pkey" 主键,btree (uuid) "lookup_32ff3898" btree (datetime_stamp) "lookup_6c8369bc" btree (harvest_date_stamp) “lookup_9ed39e2e” btree(状态) 有 OID:没有 表:“article_data”` int:整数类型。不为空默认 nextval('article_data_id_seq'::regclass)。普通存储。 标题:文字。 文字:文字。 插入日期:日期 收获日期:带有时区的时间戳。 uuid:uuid。 索引: "article_data_pkey" 主键,btree (id) "article_data_uuid_key" 唯一约束,btree (uuid) 有 OID:没有

lookup 和 article_data 都有大约 65m 行。两个查询:

SELECT uuid FROM lookup WHERE state = 200 LIMIT 4000;
解释的输出(分析,缓冲区): 限制(成本=0.00..4661.02 行=4000 宽度=16)(实际时间=0.009..1.036 行=4000 循环=1) 缓冲区:共享命中=42 -> Seq Scan on lookup (cost=0.00..1482857.00 rows=1272559 width=16) (实际时间=0.008..0.777 rows=4000 loops=1) 过滤器:(状态 = 200) 过滤器删除的行数:410 缓冲区:共享命中=42 总运行时间:1.196 毫秒 (7 行)

问题:为什么当 btree 处于状态时,它会进行序列扫描而不是索引扫描?

SELECT article_data.id, article_data.uuid, article_data.title, article_data.text 
FROM article_data 
WHERE uuid = ANY ('{f0d5e665-4f21-4337-a54b-cf0b4757db65,..... 3999 more uuid's ....}'::uuid[]);
解释的输出(分析,缓冲区): 在 article_data 上使用 article_data_uuid_key 进行索引扫描(成本=5.56..34277.00 行=4000 宽度=581)(实际时间=0.063..66029.031 行=400 0 循环=1) 指数电导率:(UUID = ANY('{f0d5e665-4f21-4337-a54b-cf0b4757db65,5618754f-544B-4700-9d24-c364fd0ba4e9,958e37e3-6e6e-4b2a-b854-48e88ac1fdb7,ba56b483-59b2-4ae5-ae44-910401f3221b, aa4 aca60-a320-4ed3-b7b4-829e6ca63592,05f1c0b9-1f9b-4e1c-8f41-07545d694e6b,7aa4dee9-be17-49df-b0ca-d6e63b0dc023,e9037826-86c4-4bbc-a9d5-6977ff7458af,db5852bf- a447-4a1d-9673-ead2f7045589 ,6704d89 ..}'::uuid[])) 缓冲区:共享命中=16060 读取=4084 脏=292 总运行时间:66041.443 毫秒 (4 行)

问题:为什么它这么慢,即使它是从磁盘读取的?

【问题讨论】:

标签: postgresql indexing query-optimization uuid b-tree


【解决方案1】:

在没有看到您的表结构和explain analyze... 的输出的情况下,我希望查找表上的内部连接能够提供最佳性能。 (我的 table_a 大约有 1000 万行。)

select * 
from table_a
inner join 
    -- Brain dead way to get about 1000 rows 
    -- from a renamed scratch table.
    (select test_uuid from lookup_table
     where test_id < 10000) t
on table_a.test_uuid = t.test_uuid;
“嵌套循环(成本=0.72..8208.85 行=972 宽度=36)(实际时间=0.041..11.825 行=999 循环=1)” “ -> 使用 uuid_test_2_test_id_key 在lookup_table 上进行索引扫描(成本=0.29..39.30 行=972 宽度=16)(实际时间=0.015..0.414 行=999 循环=1)” " Index Cond: (test_id Index Scan using uuid_test_test_uuid_key on table_a (cost=0.43..8.39 rows=1 width=20) (实际时间=0.010..0.011 rows=1 loops=999)" " 索引条件:(test_uuid = lookup_table.test_uuid)" “计划时间:0.503 ms” “执行时间:11.953 毫秒”

【讨论】:

    猜你喜欢
    • 2021-10-18
    • 2022-01-24
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多