GaussDB性能调优之分区表调优

举报
开发者空间小蜜蜂 发表于 2024/11/27 17:22:52 2024/11/27
【摘要】 GaussDB分区表按照业务场景合理的使用分区策略,可以改善查询性能、增强可用性、方便维护等特性,同时按照分区表索引机制合理创建local与global索引来提高查询性能。

一、问题现象

某金融公司的核心交易应用系统已上线2年多,有一张分区日志表随着业务的增长将达到5+TB的数据量,数据库的运维人员想对此日志表做数据归档,对2年以后的数据做数据迁移到磁盘,然后对分区表做了删除,此时发现访问这张表的性能下降导致整个系统运行缓慢。经过现场分析,他们创建的分区索引不恰当。 

二、技术背景

分区表实际上就是一种数据组织方式,是把逻辑上的一张表根据某种方案分成几张物理块进行存储。这张逻辑上的表称之为分区表,物理块称之为分区。分区表是一张逻辑表,不存储数据,数据实际是存储在分区上的。分区的小表独立管理,可以具有独立的索引和存储空间。分区表增强了数据库应用程序的性能、可管理性和可用性,并有助于降低存储大量数据的总体拥有成本。分区允许将表、索引和索引组织的表细分为更小的部分,使这些数据库对象能够在更精细的粒度级别上进行管理和访问。

GaussDB提供了丰富的分区策略和扩展,以满足不同业务场景的需求。由于分区策略的实现完全由数据库内部实现,对用户是完全透明的,因此它几乎可以在实施分区表优化策略以后做平滑迁移,无需潜在耗费人力物力的应用程序更改。

2.1 分区表的性能优化

2.1.1  查询优化

  • 使用分区键:查询语句中应包含分区键,以便GaussDB能够快速定位到相关分区,减少扫描的分区数量。
  • 避免跨分区查询:尽量避免跨多个分区的查询,因为跨分区查询会增加扫描的分区数量,影响查询性能。

示例:

SELECT * FROM sales WHERE sale_date BETWEEN '2021-01-01' AND '2021-12-31';

2.1.1  数据导入优化

  • 批量插入:使用批量插入语句可以提高数据导入效率,减少分区切换的开销。
  • 关闭索引:在大量数据导入前,可以临时关闭索引,以减少索引维护的开销,导入完成后再重新启用索引。

示例:

ALTER TABLE sales DISABLE KEYS;
-- 批量插入数据
ALTER TABLE sales ENABLE KEYS;

2.2 分区表相关系统视图和函数

2.2.1  所有分区视图

  • ADM_PART_TABLES:所有分区表信息。
  • ADM_TAB_PARTITIONS:所有一级分区信息。
  • ADM_TAB_SUBPARTITIONS:所有二级分区信息。
  • ADM_PART_INDEXES:所有Local索引信息。
  • ADM_IND_PARTITIONS:所有一级分区表索引分区信息。
  • ADM_IND_SUBPARTITIONS:所有二级分区表索引分区信息。

2.2.2  当前用户可访问的视图

  • DB_PART_TABLES:当前用户可访问的分区表信息。
  • DB_TAB_PARTITIONS:当前用户可访问的一级分区信息。
  • DB_TAB_SUBPARTITIONS:当前用户可访问的二级分区信息。
  • DB_PART_INDEXES:当前用户可访问的Local索引信息。
  • DB_IND_PARTITIONS:当前用户可访问的一级分区表索引分区信息。
  • DB_IND_SUBPARTITIONS:当前用户可访问的二级分区表索引分区信息。

2.2.3  当前用户拥有的视图

  • MY_PART_TABLES:当前用户拥有的分区表信息。
  • MY_TAB_PARTITIONS:当前用户拥有的一级分区信息。
  • MY_TAB_SUBPARTITIONS:当前用户拥有的二级分区信息。
  • MY_PART_INDEXES:当前用户拥有的Local索引信息。
  • MY_IND_PARTITIONS:当前用户拥有的一级分区表索引分区信息。
  • MY_IND_SUBPARTITIONS:当前用户拥有的二级分区表索引分区信息。

2.2.4  内置工具函数

gaussdb=>
gaussdb=> create table test_range_pt(a int,b int,c int)
gaussdb-> partition by range(a)
gaussdb-> (
gaussdb(>     partition p1 values less than(2000),
gaussdb(> partition p2 values less than(3000),
gaussdb(> partition p3 values less than(4000),
gaussdb(> partition p4 values less than(5000),
gaussdb(> partition p5 values less than(maxvalue)
gaussdb(> );
NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'a' as the distribution column by default.
HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
CREATE TABLE
gaussdb=>	
gaussdb=> select oid from pg_class where relname='test_range_pt';
  oid
-------
 49243
(1 row)

gaussdb=>
gaussdb=> select oid,relname,parttype,parentid,boundaries from pg_partition where parentid=49243;
  oid  |    relname    | parttype | parentid | boundaries
-------+---------------+----------+----------+------------
 49246 | test_range_pt | r        |    49243 |
 49247 | p1            | p        |    49243 | {2000}
 49248 | p2            | p        |    49243 | {3000}
 49249 | p3            | p        |    49243 | {4000}
 49250 | p4            | p        |    49243 | {5000}
 49251 | p5            | p        |    49243 | {NULL}
(6 rows)

gaussdb=>
gaussdb=> create index idx_range_a on test_range_pt(a) local;
CREATE INDEX
gaussdb=>
gaussdb=> select oid from pg_class where relname='idx_range_a';
  oid
-------
 49252
(1 row)

gaussdb=>
gaussdb=> select oid,relname,parttype,parentid,boundaries,indextblid from pg_partition
where parentid=49252;
  oid  | relname  | parttype | parentid | boundaries | indextblid
-------+----------+----------+----------+------------+------------
 49253 | p1_a_idx | x        |    49252 |            |      49247
 49254 | p2_a_idx | x        |    49252 |            |      49248
 49255 | p3_a_idx | x        |    49252 |            |      49249
 49256 | p4_a_idx | x        |    49252 |            |      49250
 49257 | p5_a_idx | x        |    49252 |            |      49251
(5 rows)

gaussdb=>
  • pg_get_tabledef获取分区表的定义,入参可以为表的OID或者表名
gaussdb=> select pg_get_tabledef('test_range_pt');
                                                                                       pg_get_tabledef

--------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------
 SET search_path = root;
                                    +
 CREATE TABLE test_range_pt (
                                    +
     a integer,
                                    +
     b integer,
                                    +
     c integer
                                    +
 )
                                    +
 WITH (orientation=row, compression=no)
                                    +
 DISTRIBUTE BY HASH(a)
                                    +
 TO GROUP group_version1
                                    +
 PARTITION BY RANGE (a)
                                    +
 (
                                    +
     PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default,
                                    +
     PARTITION p2 VALUES LESS THAN (3000) TABLESPACE pg_default,
                                    +
     PARTITION p3 VALUES LESS THAN (4000) TABLESPACE pg_default,
                                    +
     PARTITION p4 VALUES LESS THAN (5000) TABLESPACE pg_default,
                                    +
     PARTITION p5 VALUES LESS THAN (MAXVALUE) TABLESPACE pg_default
                                    +
 );
                                    +
 CREATE INDEX idx_range_a ON test_range_pt USING btree (a) LOCAL(PARTITION p1_a_idx, PARTITION p2_a_idx, PARTITION p3_a_idx, PARTITION p4_a_idx, PARTITI
ON p5_a_idx)  TABLESPACE pg_default;
(1 row)

gaussdb=>
gaussdb=> select pg_get_tabledef(49243);
                                                                                       pg_get_tabledef

--------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------
 SET search_path = root;
                                    +
 CREATE TABLE test_range_pt (
                                    +
     a integer,
                                    +
     b integer,
                                    +
     c integer
                                    +
 )
                                    +
 WITH (orientation=row, compression=no)
                                    +
 DISTRIBUTE BY HASH(a)
                                    +
 TO GROUP group_version1
                                    +
 PARTITION BY RANGE (a)
                                    +
 (
                                    +
     PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default,
                                    +
     PARTITION p2 VALUES LESS THAN (3000) TABLESPACE pg_default,
                                    +
     PARTITION p3 VALUES LESS THAN (4000) TABLESPACE pg_default,
                                    +
     PARTITION p4 VALUES LESS THAN (5000) TABLESPACE pg_default,
                                    +
     PARTITION p5 VALUES LESS THAN (MAXVALUE) TABLESPACE pg_default
                                    +
 );
                                    +
 CREATE INDEX idx_range_a ON test_range_pt USING btree (a) LOCAL(PARTITION p1_a_idx, PARTITION p2_a_idx, PARTITION p3_a_idx, PARTITION p4_a_idx, PARTITION
ON p5_a_idx)  TABLESPACE pg_default;
(1 row)

gaussdb=>
  • pg_partition_size(oid,oid)指定OID代表的分区使用的磁盘空间,其中,第一个oid为表的OID,第二个oid为分区的OID

查看分区p1的磁盘空间

gaussdb=> select oid,relname,parttype,parentid,boundaries,indextblid from pg_partition
where parentid=49252;
  oid  | relname  | parttype | parentid | boundaries | indextblid
-------+----------+----------+----------+------------+------------
 49253 | p1_a_idx | x        |    49252 |            |      49247
 49254 | p2_a_idx | x        |    49252 |            |      49248
 49255 | p3_a_idx | x        |    49252 |            |      49249
 49256 | p4_a_idx | x        |    49252 |            |      49250
 49257 | p5_a_idx | x        |    49252 |            |      49251
(5 rows)

gaussdb=> select oid,relname,parttype,parentid,boundaries,indextblid from pg_partition
where parentid=49243;
  oid  |    relname    | parttype | parentid | boundaries | indextblid
-------+---------------+----------+----------+------------+------------
 49246 | test_range_pt | r        |    49243 |            |          0
 49247 | p1            | p        |    49243 | {2000}     |          0
 49248 | p2            | p        |    49243 | {3000}     |          0
 49249 | p3            | p        |    49243 | {4000}     |          0
 49250 | p4            | p        |    49243 | {5000}     |          0
 49251 | p5            | p        |    49243 | {NULL}     |          0
(6 rows)

gaussdb=>
gaussdb=> insert into test_range_pt values(generate_series(1,10),1,1);
INSERT 0 10
gaussdb=> select pg_partition_size(49252,49253);
 pg_partition_size
-------------------
              8192
(1 row)

gaussdb=>
  • pg_partition_size(text, text)指定名称的分区使用的磁盘空间,其中,第一个text为表名,第二个text为分区名。

查看分区p1的磁盘空间

gaussdb=> select pg_partition_size('test_range_pt','p1');
 pg_partition_size
-------------------
             24576
(1 row)

gaussdb=>
  • pg_partition_indexes_size(oid,oid)指定OID代表的分区索引使用的磁盘空间,其中,第一个oid为表的OID,第二个oid为分区的OID

查看分区p1的索引分区磁盘空间

gaussdb=> select pg_partition_indexes_size(49252,49253);
 pg_partition_indexes_size
---------------------------
                         0
(1 row)

gaussdb=>
  • pg_partition_indexes_size(text,text)指定名称的分区索引使用的磁盘空间,其中,第一个text为表名,第二个text为分区名。

查看分区p1的索引分区磁盘空间

gaussdb=> select pg_partition_indexes_size('test_range_pt','p1');
 pg_partition_indexes_size
---------------------------
                     49152
(1 row)

gaussdb=>

2.3 分区表查询性能异常

2.3.1 常见原因

使用分区表进行相关查询业务,SQL性能慢时,常见的原因有以下几种:

  • 分区索引失效,顺序扫描导致的SQL性能慢
  • 分区表无法进行分区剪枝导致的SQL性能慢
  • SQL计划选择非最优导致的SQL性能慢

2.3.2  处理方法

2.3.2.1 判断是否存在索引异常的行为

部分分区DDL如果不带UPDATE GLOBAL INDEX子句,会导致分区表Global索引失效。同时用户也可以使用ALTER TABLE或者ALTER INDEX使分区表索引失效。当分区表或者部分分区上索引不可用后,会导致查询SQL无法走索引扫描,从而出现慢SQL场景。

1. 查询索引类型和状态

  • 分区表索引分为LOCAL索引GLOBAL索引LOCAL索引与某个具体分区绑定,而GLOBAL索引则对应整个分区表
  • 唯一约束主键约束的约束键包含所有分区键则创建LOCAL索引,否则创建GLOBAL索引。当查询语句在查询数据涉及多个目标分区时,建议使用GLOBAL索引,反之建议使用LOCAL索引。但需要注意GLOBAL索引在分区维护语法中存在额外的开销。
  • 在创建LOCAL索引时,可以通过FOR { partition_name | ( partition_value [, ...] ) }子句,指定在单个分区上创建LOCAL索引,此类索引在其他分区上不生效,后续新增的分区也不会自动创建该索引。需要注意的是,当前仅静态剪枝到单个分区的计划支持生成分类索引的查询路径。

Local索引会在每个分区创建一个索引,Global索引会在整个分区表上创建一个全局索引。对于Local索引,需要确认整个索引自身状态和目标查询分区的索引状态;对于Global索引,只需要确认整个索引自身状态。

可以通过系统表pg_index查询分区表的所有索引,并获取索引是否可用的状态,其索引类型在pg_class中给出。下面给出了查询分区表t1的全部索引状态及索引类型的命令参考:

select c.relname, i.indisusable, c.relkind
  from pg_class c
  join pg_index i
    on c.oid = i.indexrelid
  join pg_class r
    on i.indrelid = r.oid
 where r.relname = 'test_range_pt';

图片1.png

其中indisusable字段为't'表示该索引可用,为'f'表示索引已经失效,在查询业务中该索引无法使用;relkind'i'表示该索引为Local索引,为'I'表示该索引为Global索引。

或者通过元语句\d命令查询分区表的索引信息:

图片2.png

可以看到表t1的两条索引t1_c1_idxt1_c2_tableoid_idx被列出。其中t1_c1_idx带有LOCAL关键字,表示该索引为Local索引,t1_c2_tableoid_idx没有LOCAL关键字,表示该索引为Global索引。同时t1_c2_tableoid_idx标记了UNUSABLE,表示该索引不可用。

2. 查询索引分区状态

对于Local索引,还需要确认目标查询分区的索引状态,这个信息可以从系统表pg_partition中查询。

gaussdb=# select p.relname,p.indisusable from pg_partition p join pg_class c on p.parentid=c.oid where 
c.relname='t1_c1_idx';
  relname  | indisusable 
-----------+-------------
 p1_c1_idx | t
 p2_c1_idx | f
(2 rows)

可以看到索引分区p1_c1_idx可用,索引分区p2_c1_idx不可用。在分区p2上的查询无法使用索引t1_c1_idx

3. 重建异常的索引/索引分区

如果分区表的索引/索引分区状态异常,会导致查询SQL无法走索引扫描,需要重建索引/索引分区。重建索引的命令如下:

ALTER INDEX t1_c2_tableoid_idx REBUILD;

重建索引分区的命令如下:

ALTER INDEX t1_c1_idx REBUILD PARTITION p2_c1_idx;

i. 分区表索引重建/不可用

用户可以通过命令使得一个分区表索引或者一个索引分区不可用,此时该索引/索引分区不再维护。使用重建索引命令可以重建分区表索引,恢复索引的正常功能。

常见能使索引失效的分区操作:

  • 删除drop分区
  • 交换exchange分区
  • 清空truncate分区
  • 分割split分区
  • 合并merge分区

ii. local索引分区重建/不可用

  • 使用ALTER INDEX PARTITION可以设置Local索引分区是否可用。
  • 使用ALTER TABLE MODIFY PARTITION可以设置分区表上指定分区的所有索引分区是否可用。

例如,假设分区表range_sales上存在两张Local索引range_sales_idx1range_sales_idx2,假设其在分区date_202001上对应的索引分区名分别为range_sales_idx1_part1range_sales_idx2_part1

下面给出了维护分区表分区索引的语法:

  • 可以通过如下命令设置分区date_202001上的所有索引分区均不可用。
  • ALTER TABLE range_sales MODIFY PARTITION date_202001 UNUSABLE LOCAL INDEXES;
  • 或者通过如下命令单独设置分区date_202001上的索引分区range_sales_idx1_part1不可用。
  • ALTER INDEX range_sales_idx1 MODIFY PARTITION range_sales_idx1_part1 UNUSABLE;
  • 可以通过如下命令重建分区date_202001上的所有索引分区。
  • ALTER TABLE range_sales MODIFY PARTITION date_202001 REBUILD UNUSABLE LOCAL INDEXES;
  • 或者通过如下命令单独重建分区date_202001上的索引分区range_sales_idx1_part1
  • ALTER INDEX range_sales_idx1 REBUILD PARTITION range_sales_idx1_part1;

2.3.2.2 判断分区表是否存在剪枝异常的场景

当分区表的分区键所在列存在条件时,可以触发分区剪枝。数据库会在优化器/执行器阶段识别到需要扫描的分区,而不会扫描全部分区。只有分区表的业务是顺序扫描,或者使用Local索引进行扫描时候,才可能触发分区剪枝;Global索引不会触发分区剪枝,这是因为Global索引是在整个分区表上创建的单个索引,不存在剪枝的概念。

剪枝是被动触发的,当查询业务满足分区剪枝条件时,会自动触发分区剪枝。

1、判断是否触发了分区剪枝

可以通过查询计划来判断是否触发了分区剪枝。当扫描分区数少于分区总数时,即触发了分区剪枝。分区剪枝分为静态剪枝和动态剪枝,静态剪枝是指在优化器阶段就能识别到裁剪的分区;动态剪枝是指优化器阶段只能确定可以进行剪枝,但不知道具体裁剪到哪一个分区,在执行器阶段才会确定裁剪的目标分区。

下面的业务触发了分区静态剪枝,计划中的'Iterations: 1'表示扫描了1个分区,'Selected Partitions: 1'表示扫描的目标分区下标是1

这个下标是一个逻辑数组下标,对于范围分区/间隔分区,按照分区定义上界值升序排列;对于列表分区,按照第一个枚举值升序排列;对于哈希分区,按照计算后的哈希值升序排列。可以通过函数pg_get_tabledef获取分区定义,其列出的分区即为该对应下标。

gaussdb=# explain select * from t1 where c1 < 100;
                              QUERY PLAN                               
-----------------------------------------------------------------------
 Partition Iterator  (cost=0.00..27.86 rows=716 width=8)
   Iterations: 1
   ->  Partitioned Seq Scan on t1  (cost=0.00..27.86 rows=716 width=8)
         Filter: (c1 < 100)
         Selected Partitions:  1
(5 rows)

下面的业务没有触发分区剪枝,计划中的'Iterations: 2'表示扫描了2个分区,'Selected Partitions: 1..2'表示扫描的目标分区下标是12。可以看到扫描的分区数等于分区表的总分区数,表示未进行任何分区剪枝。

gaussdb=# explain select * from t1;
                               QUERY PLAN                               
------------------------------------------------------------------------
 Partition Iterator  (cost=0.00..31.49 rows=2149 width=8)
   Iterations: 2
   ->  Partitioned Seq Scan on t1  (cost=0.00..31.49 rows=2149 width=8)
         Selected Partitions:  1..2
(4 rows)

下面的业务触发了分区动态剪枝,计划中的'Iterations: PART''Selected Partitions: PART'表示优化器识别到分区表可以进行分区剪枝。

gaussdb=# prepare p1 as select * from t1 where c1 < $1;
gaussdb=# explain execute p1(100);
                                         QUERY PLAN                                         
--------------------------------------------------------------------------------------------
 Partition Iterator  (cost=9.80..28.75 rows=716 width=8)
   Iterations: PART
   ->  Partitioned Bitmap Heap Scan on t1  (cost=9.80..28.75 rows=716 width=8)
         Recheck Cond: (c1 < $1)
         Selected Partitions:  PART
         ->  Partitioned Bitmap Index Scan on t1_c1_idx  (cost=0.00..9.62 rows=716 width=0)
               Index Cond: (c1 < $1)
               Selected Partitions:  PART
(8 rows)

2、支持分区剪枝的场景

当分区键所在列存在条件时,可以进行分区剪枝。条件剪枝支持场景为:比较表达式(<<==>=>)、逻辑表达式(ANDOR)、数组表达式。需要注意的是,列表和哈希分区不支持除等号外的其他比较表达式的剪枝。如果条件全为常量,则进行静态剪枝;如果条件带有参数、部分隐式转换、immutable函数,则进行动态剪枝。

当分区表作为参数化路径计划的内表且分区键所在列为索引检索条件时,支持动态剪枝。

  • 不支持分区剪枝的场景

分区键所在列的条件为子查询表达式、无法直接强转的类型转换、stable/volatile函数时,不支持分区剪枝。

分区表在生成除参数化路径外的其他JOIN计划时,不支持动态剪枝。

  • 业务改写适配分区剪枝

当业务设计不合理,导致原逻辑上可以走分区剪枝的计划,最终未走分区剪枝时,可以改写业务以适配分区剪枝,从而提升分区表查询业务性能。比如使用HINT固定计划、在客户端修改绑参类型等方法。

三、案例剖析

3.1 drop分区导致Global索引失效

3.1.1 创建分区表

gaussdb=# CREATE TABLE test_range1(
gaussdb(#     id INT,
gaussdb(#     info VARCHAR(20)
gaussdb(# ) PARTITION BY RANGE (id) (
gaussdb(#     PARTITION p1 VALUES LESS THAN (100000) ,
gaussdb(#     PARTITION p2 VALUES LESS THAN (200000) ,
gaussdb(#     PARTITION p3 VALUES LESS THAN (300000) ,
gaussdb(#     PARTITION p4 VALUES LESS THAN (400000) ,
gaussdb(#     PARTITION p5 VALUES LESS THAN (500000) ,
gaussdb(#     PARTITION p6 VALUES LESS THAN (600000) ,
gaussdb(#     PARTITION pmax VALUES LESS THAN (MAXVALUE)
gaussdb(# );
CREATE TABLE
gaussdb=# \d+ test_range1
                             Table "public.test_range1"
 Column |         Type          | Modifiers | Storage  | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
 id     | integer               |           | plain    |              |
 info   | character varying(20) |           | extended |              |
Partition By RANGE(id)
Number of partitions: 7 (View pg_partition to check each partition range.)
Has OIDs: no
Options: orientation=row, compression=no, storage_type=USTORE, segment=off

3.1.2  向分区表中插入数据

gaussdb=# insert into public.test_range1 select generate_series(1,700000) ,'range-'||generate_series(1,700000) ;
INSERT 0 700000
gaussdb=# select * from public.test_range1 limit 10;
 id |   info
----+----------
  1 | range-1
  2 | range-2
  3 | range-3
  4 | range-4
  5 | range-5
  6 | range-6
  7 | range-7
  8 | range-8
  9 | range-9
 10 | range-10
(10 rows)

3.1.3 创建Global索引

gaussdb=# create index idx_test_info on test_range1(info);
CREATE INDEX
gaussdb=# select c.relname, i.indisusable, c.relkind
gaussdb-#   from pg_class c
gaussdb-#   join pg_index i
gaussdb-#     on c.oid = i.indexrelid
gaussdb-#   join pg_class r
gaussdb-#     on i.indrelid = r.oid
gaussdb-#  where r.relname = 'test_range1';
    relname    | indisusable | relkind
---------------+-------------+---------
 idx_test_info | t           | I
(1 row)

gaussdb=# \d+ test_range1
                             Table "public.test_range1"
 Column |         Type          | Modifiers | Storage  | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
 id     | integer               |           | plain    |              |
 info   | character varying(20) |           | extended |              |
Indexes:
    "idx_test_info" ubtree (info) GLOBAL WITH (storage_type=USTORE) TABLESPACE pg_default
Partition By RANGE(id)
Number of partitions: 7 (View pg_partition to check each partition range.)
Has OIDs: no
Options: orientation=row, compression=no, storage_type=USTORE, segment=off

从查询系统视图的结果,可以看到表中索引为Global索引,并且状态正常。

3.1.4 执行sql查看执行计划

gaussdb=# explain analyze select * from public.test_range1 where info = 'range-7777';
 id |                     operation                     | A-time | A-rows | E-rows | Peak Memory | A-width | E-width |   E-costs
----+---------------------------------------------------+--------+--------+--------+-------------+---------+---------+--------------
  1 | ->  Index Scan using idx_test_info on test_range1 | 0.083  |      1 |      1 | 81KB        |         |      16 | 0.000..8.268
(1 row)

      Predicate Information (identified by plan id)
---------------------------------------------------------
   1 --Index Scan using idx_test_info on test_range1
         Index Cond: ((info)::text = 'range-7777'::text)
(2 rows)

       ====== Query Summary =====
----------------------------------------
 Datanode executor start time: 0.027 ms
 Datanode executor run time: 0.097 ms
 Datanode executor end time: 0.007 ms
 Planner runtime: 0.188 ms
 Query Id: 1947243888889306092
 Total runtime: 0.141 ms
(6 rows)

从执行计划可以看到,以info条件查询时,走的是索引扫描。

3.1.5 模似问题重现

修改分区表,删除其中一个分区后,再检查表结构和索引状态

gaussdb=# ALTER TABLE test_range1 DROP PARTITION p2;
ALTER TABLE
gaussdb=# select c.relname, i.indisusable, c.relkind
  from pg_class c
  join pg_index i
    on c.oid = i.indexrelid
  join pg_class r
    on i.indrelid = r.oid
 where r.relname = 'test_range1';
    relname    | indisusable | relkind
---------------+-------------+---------
 idx_test_info | f           | I
(1 row)

gaussdb=# \d+ test_range1
                             Table "public.test_range1"
 Column |         Type          | Modifiers | Storage  | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
 id     | integer               |           | plain    |              |
 info   | character varying(20) |           | extended |              |
Indexes:
    "idx_test_info" ubtree (info) GLOBAL WITH (storage_type=USTORE) TABLESPACE pg_default UNUSABLE
Partition By RANGE(id)
Number of partitions: 6 (View pg_partition to check each partition range.)
Has OIDs: no
Options: orientation=row, compression=no, storage_type=USTORE, segment=off

删除分区后,从查询系统视图的结果,可以看到表中Global索引状态已经变成UNUSABLE了。

3.1.6 再次执行sql查看执行计划

gaussdb=# explain analyze select * from public.test_range1 where info = 'range-7777';
 id |                 operation                  | A-time  | A-rows | E-rows | Peak Memory | A-width | E-width |     E-costs
----+--------------------------------------------+---------+--------+--------+-------------+---------+---------+------------------
  1 | ->  Partition Iterator                     | 106.120 |      1 |      1 | 14KB        |         |      16 | 0.000..11793.000
  2 |    ->  Partitioned Seq Scan on test_range1 | 106.047 |      1 |      1 | 47KB        |         |      16 | 0.000..11793.000
(2 rows)

    Predicate Information (identified by plan id)
-----------------------------------------------------
   1 --Partition Iterator
         Iterations: 6
   2 --Partitioned Seq Scan on test_range1
         Filter: ((info)::text = 'range-7777'::text)
         Rows Removed by Filter: 599999
         Selected Partitions:  1..6
(6 rows)

       ====== Query Summary =====
----------------------------------------
 Datanode executor start time: 0.045 ms
 Datanode executor run time: 106.129 ms
 Datanode executor end time: 0.008 ms
 Planner runtime: 1.774 ms
 Query Id: 1947243888889306500
 Total runtime: 106.187 ms
(6 rows)

从执行计划可以看到,以info条件查询时,走的是顺序扫描,单sql总耗时:106.187ms

四、总结

   GaussDB分区表按照业务场景合理的使用分区策略,可以改善查询性能、增强可用性、方便维护等特性,同时按照分区表索引机制合理创建localglobal索引来提高查询性能。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。