4300 万张 PostgreSQL 表上的复合索引

Dr.*_*YSG 4 postgresql index index-tuning

这个问题与我之前问过的问题有关:PostgreSQL 中复合索引中的列顺序(和查询顺序)

我想我可以在这里尖锐和限制我的问题,而不是超载这个问题。鉴于以下查询(和 EXPLAIN ANALYZE),我正在创建的复合索引有帮助吗?

第一个查询仅使用简单索引(大纲上的 GIST)和(pid 上的 BTREE)运行。

查询是:

EXPLAIN ANALYZE SELECT DISTINCT ON (path) oid, pid, product_name, type, path, size 
FROM portal.inventory AS inv 
WHERE ST_Intersects(st_geogfromtext('SRID=4326;POLYGON((21.51947021484375 51.55059814453125, 18.9129638671875 51.55059814453125, 18.9129638671875 48.8287353515625, 21.51947021484375 48.8287353515625, 21.51947021484375 51.55059814453125))'), inv.outline) 
AND (inv.pid in (20010,20046)) 
Run Code Online (Sandbox Code Playgroud)

——

结果如下(速度更快,但也许这只是因为数据库是热的)。

"Unique  (cost=581.76..581.76 rows=1 width=89) (actual time=110.436..110.655 rows=249 loops=1)"
"  ->  Sort  (cost=581.76..581.76 rows=1 width=89) (actual time=110.434..110.477 rows=1377 loops=1)"
"        Sort Key: path"
"        Sort Method: quicksort  Memory: 242kB"
"        ->  Bitmap Heap Scan on inventory inv  (cost=577.48..581.75 rows=1 width=89) (actual time=39.257..105.878 rows=1377 loops=1)"
"              Recheck Cond: ((pid = ANY ('{20010,20046}'::integer[])) AND ('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline))"
"              Rows Removed by Index Recheck: 3731"
"              Filter: (_st_distance('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography, outline, 0::double precision, false) < 1e-005::double precision)"
"              Rows Removed by Filter: 533"
"              ->  BitmapAnd  (cost=577.48..577.48 rows=1 width=0) (actual time=38.972..38.972 rows=0 loops=1)"
"                    ->  Bitmap Index Scan on inventory_pid_idx  (cost=0.00..123.82 rows=6204 width=0) (actual time=1.116..1.116 rows=7836 loops=1)"
"                          Index Cond: (pid = ANY ('{20010,20046}'::integer[]))"
"                    ->  Bitmap Index Scan on inventory_outline_idx  (cost=0.00..453.41 rows=8212 width=0) (actual time=37.765..37.765 rows=63112 loops=1)"
"                          Index Cond: ('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline)"
"Total runtime: 110.731 ms"
Run Code Online (Sandbox Code Playgroud)

现在这里是添加复合索引的结果:(注意绝对时间更慢)

"Unique  (cost=37.81..37.82 rows=1 width=89) (actual time=2464.353..2464.561 rows=249 loops=1)"
"  ->  Sort  (cost=37.81..37.82 rows=1 width=89) (actual time=2464.349..2464.389 rows=1377 loops=1)"
"        Sort Key: path"
"        Sort Method: quicksort  Memory: 242kB"
"        ->  Bitmap Heap Scan on inventory inv  (cost=33.54..37.80 rows=1 width=89) (actual time=2361.018..2459.653 rows=1377 loops=1)"
"              Recheck Cond: (('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline) AND (pid = ANY ('{20010,20046}'::integer[])))"
"              Filter: (_st_distance('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography, outline, 0::double precision, false) < 1e-005::double precision)"
"              Rows Removed by Filter: 533"
"              ->  Bitmap Index Scan on inventory_compound_idx  (cost=0.00..33.53 rows=1 width=0) (actual time=2321.684..2321.684 rows=1910 loops=1)"
"                    Index Cond: (('0103000020E6100000010000000500000000000000FC843540000000007AC6494000000000B8E93240000000007AC6494000000000B8E9324000000000146A484000000000FC84354000000000146A484000000000FC843540000000007AC64940'::geography && outline) AND (pid = ANY ('{20010,20046}'::integer[])))"
"Total runtime: 2558.022 ms"
Run Code Online (Sandbox Code Playgroud)

最后,这是表定义:

CREATE TABLE portal.inventory
(
  oid bigint,
  product_name character varying(100),
  type character varying(25),
  pid integer,
  size bigint,
  date timestamp without time zone,
  path character varying(200),
  outline geography(Polygon,4326)
)
WITH (
  OIDS=FALSE
);


CREATE INDEX inventory_compound_idx
  ON portal.inventory
  USING gist
  (outline, pid);


CREATE INDEX inventory_outline_idx
  ON portal.inventory
  USING gist
  (outline);


CREATE INDEX inventory_pid_idx
  ON portal.inventory
  USING btree
  (pid);
Run Code Online (Sandbox Code Playgroud)

更新:对下列问题的回答:

我可以调整表格,但我试图保持行细。您的建议各不相同,类型等是我想更改的内容。

基本上,每一行代表关于地理空间图像文件的一点元数据。我们管理着 5000 万,这很可能会增长到数亿或更多。在 DB 中,每个文件都由唯一的 OID 引用(对不起,术语重复)。它们按“产品”分组,其中 PID 是产品 ID。每个产品可能有大约 1,000 个 OID。每个图像文件都有一个地理空间边界框(轮廓)。这就是我搜索所需的全部内容。其余数据不会为空(type 是文本字符串,size 文件大小,date 是文件创建日期,path 是文件的 UNC 文件路径)。

现在这就是我按大纲排序查询的原因,然后是 PID。产品将按地理空间分组。因此,克拉科夫波兰的所有 OID 行都将在物理上位于同一区域。所以我假设如果我把桶缩小到一个小区域,第二个索引将非常小(比如一个城市区域大约 100 个产品)。IN( ..) 子句将退出。

PIDS 的实际值是从我在这里发布的另一个问题中提取的。但是那个表只针对产品,因此它的大小大约是 30K,这意味着快速搜索并且不需要复合查询。

我想知道 POSTGreSQL 规划器是否足够聪明,可以确定 (outline,pid) 的复合索引是否比 (pid, outline) 更快,如果两个索引都存在。好吧,我想我可以测试。

Erw*_*ter 7

在 GiST 索引中,列的顺序与在 B 树索引中的意义不同。根据文档:

多列 GiST 索引可以与涉及索引列的任何子集的查询条件一起使用。附加列上的条件限制了索引返回的条目,但第一列上的条件是确定需要扫描多少索引的最重要的条件。如果 GiST 索引的第一列只有几个不同的值,即使附加列中有许多不同的值,它也会相对无效。

简而言之:将最具选择性的列放在首位。

您的EXPLAIN输出显示,所述条件pid是更具选择性(rows=7836)比在一个outlinerows=63112)。如果可以推广(单个示例可能会产生误导),我建议使用以下替代方法:

CREATE INDEX inventory_compound_idx ON portal.inventory USING gist (pid, outline);
Run Code Online (Sandbox Code Playgroud)

如果您的大多数(重要)查询都包含两列的条件,则多列索引可能会很好地为您服务。否则,整体上单列可能更好。

表格布局

这是一个有根据的猜测,因为我没有完整的信息。

  • 不要oid用作列名。很容易与OID.

  • 不要使用date时间戳列的名称。或者说:不要使用名称date任何列,也不在所有标识符使用基本类型的名称。可能会导致令人困惑的错误和错误消息。

  • 为类型创建一个查找表,只将一个小整数type_id放入大表中。紧紧包装固定长度的类型,以免浪费空间填充。细节。

  • 我更喜欢类型text(或varchar没有长度限制)而不是varchar(n). 细节。

例如:

CREATE TABLE portal.inventory (
   inventory_id bigint PRIMARY KEY
  ,type_id      integer NOT NULL REFERENCES inv_type(type_id)
  ,pid          integer NOT NULL
  ,size         bigint NOT NULL
  ,ts           timestamp NOT NULL
  ,outline      geography(Polygon,4326)
  ,product_name text
  ,path         text
);
Run Code Online (Sandbox Code Playgroud)