我有两列:column1 有两个不同的值 (0, 1),column2 有 3 个不同的值 ('A', 'B', 'C')。对于列 2 中的“A”和“B”,列 1 中的值始终为 0,但如果列 2 中的值为“C”,则列 1 具有下一个分布:(0:15%,1:85%)。但我还有另一个列departmentid。在某些部门,如果column2 = 'C',column1 的分布是(0: 0%, 1: 100%)。所以在某些情况下当我有疑问时
SELECT * FROM mytable WHERE departmenid = 42 AND column2 = 'C' AND column1 = 0 ORDER BY id LIMIT 10;
PostgreSQL 选择按 id 进行索引扫描,并假设将有约 25000 行,但在表中,没有包含此过滤器的行。所以查询通过索引扫描来扫描全表,耗时太长。如果 db 选择位图扫描,速度会快 50 倍(基于此表上的其他查询)。所有这些列上的索引都存在。
我有两个问题:
PostgreSQL 16.1,每天自动真空和分析,表中约 300 万行
更新: 表定义(超过100列,超过50个索引,仅显示相关):
CREATE TABLE mytable
(
id SERIAL
PRIMARY KEY,
column1 INTEGER DEFAULT 0 NOT NULL,
column2 TEXT NOT NULL,
departmentid INTEGER
);
CREATE INDEX mytable_departmentid_index
ON mytable (departmentid);
CREATE INDEX mytable_column1_index
ON mytable (column1);
CREATE INDEX mytable_column2_index
ON mytable (column2);
-- tested statistic
CREATE STATISTICS mytable_column2_column1 ON column2, column1 FROM mytable;
CREATE STATISTICS mytable_departmentid_column1_column2 ON departmentid, column1, column2 FROM mytable;
departmentid - 有 < 0.0002% nulls, statistic says there are no nulls. Index on id exist due to id is primary key. All indexes are btree.
查询计划(其他列已删除,数字为实际值):
Limit (cost=0.68..501.79 rows=10 width=2690) (actual time=35351.049..47175.738 rows=1 loops=1)
Output: id, column1, column2, departmentid
Buffers: shared hit=1682274 read=1646793 dirtied=1640 written=980
I/O Timings: shared/local read=39193.882 write=9.565
WAL: records=1637 fpi=1637 bytes=3034199
-> Index Scan using mytable_pkey on public.mytable (cost=0.68..1392081.41 rows=27780 width=2690) (actual time=35351.048..47175.735 rows=1 loops=1)
Output: id, column1, column2, departmentid
Filter: ((mytable.departmentid = 42) AND (mytable.column2 = 'C'::text) AND (mytable.column1 = 0))
Rows Removed by Filter: 3536431
Buffers: shared hit=1682274 read=1646793 dirtied=1640 written=980
I/O Timings: shared/local read=39193.882 write=9.565
WAL: records=1637 fpi=1637 bytes=3034199
Settings: effective_cache_size = '8008368kB', effective_io_concurrency = '0', geqo_effort = '10', jit = 'off', random_page_cost = '1.2', search_path = 'public'
Planning Time: 0.503 ms
Execution Time: 47175.783 ms
我认为没有一种优雅的方法可以解决这个问题。
修复此查询的一种强力方法是将其指定为
ORDER BY id+0
。这将迫使它不使用主键索引来提供排序,并且必须求助于您提到的位图扫描之类的东西。当然,可能存在类似的情况,使用主键提供排序实际上是一个好主意,并且在其中编写所有查询可以防止这种情况发生,即使您希望它发生。
另一种选择是只使用索引
(departmentid, id)
。这不会像 4 列索引那么好,但仍然应该比现有的查询计划好得多。我不明白您对 4 列索引的反对意见是什么,所以我不知道该反对意见是否也适用于这个两列索引。