searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

PgSQL 索引介绍

2023-07-25 06:53:03
40
0

为了后续分析索引执行情况,本文首先简单介绍一下pg中的explain语句的相关分析

explain输出分析

通过下列语句我们通常能够从pgsql获得一段json文本:

explain (analyze,verbose,timing,costs,buffers,format json) SELECT * FROM 
yfr_test2

WHERE coordinates <@ box '(0, 0, 10, 10)';
[
  {
    "Plan": {
      "Node Type": "Seq Scan", // 执行节点类型,此处为顺序扫描
      "Parallel Aware": false, // 是否可以并行执行
      "Relation Name": "yfr_test2", // 关系名,即表名
      "Schema": "public", // 所在schema
      "Alias": "yfr_test2", // 关系的别名 
      "Startup Cost": 0.00, // 启动花费成本估计
      "Total Cost": 1.04, // 总成本估计
      "Plan Rows": 1, // 计划行数估计
      "Plan Width": 166, // 计划宽度,一行输出的平均大小 
      "Actual Startup Time": 0.013, // 实际启动时间
      "Actual Total Time": 0.015, // 实际总时间 
      "Actual Rows": 3, // 实际扫描行数
      "Actual Loops": 1, // 实际循环次数
      "Output": ["id", "name", "coordinates"], // 输出列
      "Filter": "(yfr_test2.coordinates <@ '(10,10),(0,0)'::box)", // 过滤条件
      "Rows Removed by Filter": 0, // 过滤移除的行数
      "Shared Hit Blocks": 1,  // 共享块命中数
      "Shared Read Blocks": 0, // 共享块读取数
      "Shared Dirtied Blocks": 0, // 共享块脏块数
      "Shared Written Blocks": 0, // 共享块写入数
      "Local Hit Blocks": 0, // 本地块命中数
      "Local Read Blocks": 0, // 本地块读取数 
      "Local Dirtied Blocks": 0, // 本地块脏块数
      "Local Written Blocks": 0, // 本地块写入数
      "Temp Read Blocks": 0, // 临时块读取数
      "Temp Written Blocks": 0 // 临时块写入数
    },
    "Planning Time": 0.066, // 查询规划时间
    "Triggers": [], // 触发器
    "Execution Time": 0.045  // 执行时间
  }
]
字段 说明 实际含义
Node Type 执行节点类型,此处为顺序扫描  
Parallel Aware 是否可以并行执行 取值为true更好,表示查询可以并行加速
Relation Name 关系名,即表名  
Schema 所在schema  
Alias 关系的别名  
Startup Cost 启动花费成本估计 数值越小越好,表示启动查询的代价越低
Total Cost 总成本估计 数值越小越好,表示查询总成本越低
Plan Rows 计划行数估计 数值越接近Actual Rows越好,表示行数估计越准确
Plan Width 计划宽度,一行输出的平均大小 数值越小表示查询处理的平均数据越少
Actual Startup Time 实际启动时间 数值越小表示启动时间越短
Actual Total Time 实际总时间 数值越小表示查询时间越短
Actual Rows 实际扫描行数  
Actual Loops 实际循环次数 数值越小表示查询的循环次数越少
Output 输出列  
Filter 过滤条件  
Rows Removed by Filter 过滤移除的行数 数值越小表示过滤的行越少
Shared Hit Blocks 共享块命中数 数值越大表示共享缓存利用越高
Shared Read Blocks 共享块读取数 数值越小表示共享IO读取越少
Shared Dirtied Blocks 共享块脏块数 数值越小表示共享缓存污染越少
Shared Written Blocks 共享块写入数 数值越小表示共享写入IO越少
Local Hit Blocks 本地块命中数 数值越大表示本地缓存命中率越高
Local Read Blocks 本地块读取数 数值越小表示本地IO读取越少
Local Dirtied Blocks 本地块脏块数 数值越小表示本地缓存污染越少
Local Written Blocks 本地块写入数 数值越小表示本地写入IO越少
Temp Read Blocks 临时块读取数 数值越小表示通过临时表减少IO读次数
Temp Written Blocks 临时块写入数 数值越小表示通过临时表减少IO写次数
Planning Time 查询规划时间 数值越小表示规划时间更短
Triggers 触发器  
Execution Time 执行时间 数值越小表示执行时间更短

常用索引介绍

btree

b-tree适合所有的数据类型,支持排序,支持大于、小于、等于、大于或等于、小于或等于的搜索。
索引与递归查询结合,还能实现快速的稀疏检索。

hash

hash索引存储的是被索引字段VALUE的哈希值,只支持等值查询。
hash索引特别适用于字段VALUE非常长(不适合b-tree索引,因为b-tree一个PAGE至少要存储3个ENTRY,所以不支持特别长的VALUE)的场景,例如很长的字符串,并且用户只需要等值搜索,建议使用hash index。

gin

1、当需要搜索多值类型内的VALUE时,适合多值类型,例如数组、全文检索、TOKEN。(根据不同的类型,支持相交、包含、大于、在左边、在右边等搜索)
2、当用户的数据比较稀疏时,如果要搜索某个VALUE的值,可以适应btree_gin支持普通btree支持的类型。(支持btree的操作符)
3、当用户需要按任意列进行搜索时,gin支持多列展开单独建立索引域,同时支持内部多域索引的bitmapAnd, bitmapOr合并,快速的返回按任意列搜索请求的数据。

案例

给出三组使用 GIN 索引的案例,每组包括建表、插入数据、创建索引和查询语句,并说明与 Btree 索引的对比:

  1. 全文搜索

(1) 建表

CREATE TABLE documents (
  id serial PRIMARY KEY,
  title text,
  content text  
);

(2) 插入数据

INSERT INTO documents (title, content) VALUES
('PostgreSQL Tutorial', 'This is a PostgreSQL tutorial'),  
('MySQL Tutorial', 'This is a MySQL tutorial');

(3) 创建索引

CREATE INDEX idx_search ON documents USING GIN (to_tsvector(content));

(4) 查询

SELECT * FROM documents
WHERE to_tsvector(content) @@ to_tsquery('postgresql');

如果使用 Btree,需要为每个词创建独立索引,查询效率低。

  1. 数组包含查找

(1) 建表

CREATE TABLE table (
  id serial PRIMARY KEY,
  int_array integer[] 
);

(2) 插入数据

INSERT INTO table (int_array) VALUES  
('{1,2,3}'),
('{4,5,6}');

(3) 创建索引

CREATE INDEX idx_gin ON table USING GIN (int_array);

(4) 查询

SELECT * FROM table WHERE int_array @> '{3}'; 

Btree 索引无法有效支持数组包含查找。

  1. 多列组合索引

(1) 建表

CREATE TABLE table (
  id serial PRIMARY KEY,
  col1 text,
  col2 text
);

(2) 插入数据

INSERT INTO table VALUES  
(1, 'abc', 'def'),
(2, 'ghi', 'jkl');

(3) 创建索引

CREATE INDEX idx_gin ON table USING GIN (col1, col2);

(4) 查询

SELECT * FROM table WHERE col1 = 'abc' AND col2 = 'def';

Btree组合列索引效果不佳,只能单列创建索引。

总结:

GIN索引支持Btree索引不擅长的查询场景,可以显著提升效率。

gist

支持各种数据类型如文本、地理位置等的索引。可以通过操作符类自定义索引方法。

GiST是一个通用的索引接口,可以使用GiST实现b-tree, r-tree等索引结构。不同的类型,支持的索引检索也各不一样。例如:
1、几何类型,支持位置搜索(包含、相交、在上下左右等),按距离排序。
2、范围类型,支持位置搜索(包含、相交、在左右等)。
3、IP类型,支持位置搜索(包含、相交、在左右等)。
4、空间类型(PostGIS),支持位置搜索(包含、相交、在上下左右等),按距离排序。
5、标量类型,支持按距离排序。

案例

  1. 建表
CREATE TABLE locations (
  id serial PRIMARY KEY,
  name varchar(64),
  coordinates point
);

包含地理坐标的点数据。

  1. 插入数据
INSERT INTO locations (name, coordinates)
VALUES 
  ('Store 1', point(3.2, 5.1)),
  ('Store 2', point(7.3, 2.4)),
  ('Store 3', point(5.1, 6.5)); 
  1. 创建GiST索引
CREATE INDEX idx_locations_coordinates ON locations USING GIST(coordinates);

在点数据上创建GiST索引。

  1. 查询
SELECT * FROM locations
WHERE coordinates <@ box '(0, 0, 10, 10)'; 

利用GiST索引可以有效地进行二维区域查找。

对于地理空间数据,GiST可以建立最小边界区域的索引,从而进行空间搜索和操作的优化。

brin

BRIN 索引是块级索引,有别于B-TREE等索引,BRIN记录并不是以行号为单位记录索引明细,而是记录每个数据块或者每段连续的数据块的统计信息。因此BRIN索引空间占用特别的小,对数据写入、更新、删除的影响也很小。
BRIN属于LOSSLY索引,当被索引列的值与物理存储相关性很强时,BRIN索引的效果非常的好。例如时序数据,在时间或序列字段创建BRIN索引,进行等值、范围查询时效果很棒。

案例

  1. 建表
CREATE TABLE test_table (
  id bigint PRIMARY KEY,
  comment text,
  created_at timestamp
);
  1. 插入数据
INSERT INTO test_table 
SELECT id, 'test comment ' || id, '2020-01-01'::timestamp + interval '1 day' * idFROM generate_series(1, 1000000) id; 

这里插入100万条测试数据。

  1. 创建BRIN索引
CREATE INDEX idx_test_table_created_at ON test_table USING BRIN (created_at);

在created_at时间字段上创建BRIN索引。

  1. 解释查询
EXPLAIN ANALYZE 
SELECT * FROM test_table 
WHERE created_at > '2020-06-01' AND created_at < '2020-06-15';

这种范围查询可以利用BRIN索引进行块区间剪裁,避免全表扫描,提高查询效率。

BRIN索引占用空间很小,但可以对大表进行有效过滤,避免不必要的IO,提升范围查询性能。这展示了BRIN索引的典型优势。

参考文档

https://developer.aliyun.com/article/111793#slide-22

0条评论
作者已关闭评论
y****n
4文章数
0粉丝数
y****n
4 文章 | 0 粉丝
y****n
4文章数
0粉丝数
y****n
4 文章 | 0 粉丝
原创

PgSQL 索引介绍

2023-07-25 06:53:03
40
0

为了后续分析索引执行情况,本文首先简单介绍一下pg中的explain语句的相关分析

explain输出分析

通过下列语句我们通常能够从pgsql获得一段json文本:

explain (analyze,verbose,timing,costs,buffers,format json) SELECT * FROM 
yfr_test2

WHERE coordinates <@ box '(0, 0, 10, 10)';
[
  {
    "Plan": {
      "Node Type": "Seq Scan", // 执行节点类型,此处为顺序扫描
      "Parallel Aware": false, // 是否可以并行执行
      "Relation Name": "yfr_test2", // 关系名,即表名
      "Schema": "public", // 所在schema
      "Alias": "yfr_test2", // 关系的别名 
      "Startup Cost": 0.00, // 启动花费成本估计
      "Total Cost": 1.04, // 总成本估计
      "Plan Rows": 1, // 计划行数估计
      "Plan Width": 166, // 计划宽度,一行输出的平均大小 
      "Actual Startup Time": 0.013, // 实际启动时间
      "Actual Total Time": 0.015, // 实际总时间 
      "Actual Rows": 3, // 实际扫描行数
      "Actual Loops": 1, // 实际循环次数
      "Output": ["id", "name", "coordinates"], // 输出列
      "Filter": "(yfr_test2.coordinates <@ '(10,10),(0,0)'::box)", // 过滤条件
      "Rows Removed by Filter": 0, // 过滤移除的行数
      "Shared Hit Blocks": 1,  // 共享块命中数
      "Shared Read Blocks": 0, // 共享块读取数
      "Shared Dirtied Blocks": 0, // 共享块脏块数
      "Shared Written Blocks": 0, // 共享块写入数
      "Local Hit Blocks": 0, // 本地块命中数
      "Local Read Blocks": 0, // 本地块读取数 
      "Local Dirtied Blocks": 0, // 本地块脏块数
      "Local Written Blocks": 0, // 本地块写入数
      "Temp Read Blocks": 0, // 临时块读取数
      "Temp Written Blocks": 0 // 临时块写入数
    },
    "Planning Time": 0.066, // 查询规划时间
    "Triggers": [], // 触发器
    "Execution Time": 0.045  // 执行时间
  }
]
字段 说明 实际含义
Node Type 执行节点类型,此处为顺序扫描  
Parallel Aware 是否可以并行执行 取值为true更好,表示查询可以并行加速
Relation Name 关系名,即表名  
Schema 所在schema  
Alias 关系的别名  
Startup Cost 启动花费成本估计 数值越小越好,表示启动查询的代价越低
Total Cost 总成本估计 数值越小越好,表示查询总成本越低
Plan Rows 计划行数估计 数值越接近Actual Rows越好,表示行数估计越准确
Plan Width 计划宽度,一行输出的平均大小 数值越小表示查询处理的平均数据越少
Actual Startup Time 实际启动时间 数值越小表示启动时间越短
Actual Total Time 实际总时间 数值越小表示查询时间越短
Actual Rows 实际扫描行数  
Actual Loops 实际循环次数 数值越小表示查询的循环次数越少
Output 输出列  
Filter 过滤条件  
Rows Removed by Filter 过滤移除的行数 数值越小表示过滤的行越少
Shared Hit Blocks 共享块命中数 数值越大表示共享缓存利用越高
Shared Read Blocks 共享块读取数 数值越小表示共享IO读取越少
Shared Dirtied Blocks 共享块脏块数 数值越小表示共享缓存污染越少
Shared Written Blocks 共享块写入数 数值越小表示共享写入IO越少
Local Hit Blocks 本地块命中数 数值越大表示本地缓存命中率越高
Local Read Blocks 本地块读取数 数值越小表示本地IO读取越少
Local Dirtied Blocks 本地块脏块数 数值越小表示本地缓存污染越少
Local Written Blocks 本地块写入数 数值越小表示本地写入IO越少
Temp Read Blocks 临时块读取数 数值越小表示通过临时表减少IO读次数
Temp Written Blocks 临时块写入数 数值越小表示通过临时表减少IO写次数
Planning Time 查询规划时间 数值越小表示规划时间更短
Triggers 触发器  
Execution Time 执行时间 数值越小表示执行时间更短

常用索引介绍

btree

b-tree适合所有的数据类型,支持排序,支持大于、小于、等于、大于或等于、小于或等于的搜索。
索引与递归查询结合,还能实现快速的稀疏检索。

hash

hash索引存储的是被索引字段VALUE的哈希值,只支持等值查询。
hash索引特别适用于字段VALUE非常长(不适合b-tree索引,因为b-tree一个PAGE至少要存储3个ENTRY,所以不支持特别长的VALUE)的场景,例如很长的字符串,并且用户只需要等值搜索,建议使用hash index。

gin

1、当需要搜索多值类型内的VALUE时,适合多值类型,例如数组、全文检索、TOKEN。(根据不同的类型,支持相交、包含、大于、在左边、在右边等搜索)
2、当用户的数据比较稀疏时,如果要搜索某个VALUE的值,可以适应btree_gin支持普通btree支持的类型。(支持btree的操作符)
3、当用户需要按任意列进行搜索时,gin支持多列展开单独建立索引域,同时支持内部多域索引的bitmapAnd, bitmapOr合并,快速的返回按任意列搜索请求的数据。

案例

给出三组使用 GIN 索引的案例,每组包括建表、插入数据、创建索引和查询语句,并说明与 Btree 索引的对比:

  1. 全文搜索

(1) 建表

CREATE TABLE documents (
  id serial PRIMARY KEY,
  title text,
  content text  
);

(2) 插入数据

INSERT INTO documents (title, content) VALUES
('PostgreSQL Tutorial', 'This is a PostgreSQL tutorial'),  
('MySQL Tutorial', 'This is a MySQL tutorial');

(3) 创建索引

CREATE INDEX idx_search ON documents USING GIN (to_tsvector(content));

(4) 查询

SELECT * FROM documents
WHERE to_tsvector(content) @@ to_tsquery('postgresql');

如果使用 Btree,需要为每个词创建独立索引,查询效率低。

  1. 数组包含查找

(1) 建表

CREATE TABLE table (
  id serial PRIMARY KEY,
  int_array integer[] 
);

(2) 插入数据

INSERT INTO table (int_array) VALUES  
('{1,2,3}'),
('{4,5,6}');

(3) 创建索引

CREATE INDEX idx_gin ON table USING GIN (int_array);

(4) 查询

SELECT * FROM table WHERE int_array @> '{3}'; 

Btree 索引无法有效支持数组包含查找。

  1. 多列组合索引

(1) 建表

CREATE TABLE table (
  id serial PRIMARY KEY,
  col1 text,
  col2 text
);

(2) 插入数据

INSERT INTO table VALUES  
(1, 'abc', 'def'),
(2, 'ghi', 'jkl');

(3) 创建索引

CREATE INDEX idx_gin ON table USING GIN (col1, col2);

(4) 查询

SELECT * FROM table WHERE col1 = 'abc' AND col2 = 'def';

Btree组合列索引效果不佳,只能单列创建索引。

总结:

GIN索引支持Btree索引不擅长的查询场景,可以显著提升效率。

gist

支持各种数据类型如文本、地理位置等的索引。可以通过操作符类自定义索引方法。

GiST是一个通用的索引接口,可以使用GiST实现b-tree, r-tree等索引结构。不同的类型,支持的索引检索也各不一样。例如:
1、几何类型,支持位置搜索(包含、相交、在上下左右等),按距离排序。
2、范围类型,支持位置搜索(包含、相交、在左右等)。
3、IP类型,支持位置搜索(包含、相交、在左右等)。
4、空间类型(PostGIS),支持位置搜索(包含、相交、在上下左右等),按距离排序。
5、标量类型,支持按距离排序。

案例

  1. 建表
CREATE TABLE locations (
  id serial PRIMARY KEY,
  name varchar(64),
  coordinates point
);

包含地理坐标的点数据。

  1. 插入数据
INSERT INTO locations (name, coordinates)
VALUES 
  ('Store 1', point(3.2, 5.1)),
  ('Store 2', point(7.3, 2.4)),
  ('Store 3', point(5.1, 6.5)); 
  1. 创建GiST索引
CREATE INDEX idx_locations_coordinates ON locations USING GIST(coordinates);

在点数据上创建GiST索引。

  1. 查询
SELECT * FROM locations
WHERE coordinates <@ box '(0, 0, 10, 10)'; 

利用GiST索引可以有效地进行二维区域查找。

对于地理空间数据,GiST可以建立最小边界区域的索引,从而进行空间搜索和操作的优化。

brin

BRIN 索引是块级索引,有别于B-TREE等索引,BRIN记录并不是以行号为单位记录索引明细,而是记录每个数据块或者每段连续的数据块的统计信息。因此BRIN索引空间占用特别的小,对数据写入、更新、删除的影响也很小。
BRIN属于LOSSLY索引,当被索引列的值与物理存储相关性很强时,BRIN索引的效果非常的好。例如时序数据,在时间或序列字段创建BRIN索引,进行等值、范围查询时效果很棒。

案例

  1. 建表
CREATE TABLE test_table (
  id bigint PRIMARY KEY,
  comment text,
  created_at timestamp
);
  1. 插入数据
INSERT INTO test_table 
SELECT id, 'test comment ' || id, '2020-01-01'::timestamp + interval '1 day' * idFROM generate_series(1, 1000000) id; 

这里插入100万条测试数据。

  1. 创建BRIN索引
CREATE INDEX idx_test_table_created_at ON test_table USING BRIN (created_at);

在created_at时间字段上创建BRIN索引。

  1. 解释查询
EXPLAIN ANALYZE 
SELECT * FROM test_table 
WHERE created_at > '2020-06-01' AND created_at < '2020-06-15';

这种范围查询可以利用BRIN索引进行块区间剪裁,避免全表扫描,提高查询效率。

BRIN索引占用空间很小,但可以对大表进行有效过滤,避免不必要的IO,提升范围查询性能。这展示了BRIN索引的典型优势。

参考文档

https://developer.aliyun.com/article/111793#slide-22

文章来自个人专栏
FoTech技术学习
4 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0