华为云用户手册

  • 分区表介绍 分区表(Partitioned Table)指在单节点内对表数据内容按照分区键以及围绕分区键的分区策略对表进行逻辑切分。从数据分区的角度来看是一种水平分区(horizontal partition)策略方式。分区表增强了数据库应用程序的性能、可管理性和可用性,并有助于降低存储大量数据的总体拥有成本。分区允许将表、索引和索引组织的表细分为更小的部分,使这些数据库对象能够在更精细的粒度级别上进行管理和访问。 GaussDB 提供了丰富的分区策略和扩展,以满足不同业务场景的需求。由于分区策略的实现完全由数据库内部实现,对用户是完全透明的,因此它几乎可以在实施分区表优化策略以后做平滑迁移,无需潜在耗费人力物力的应用程序更改。本章围绕GaussDB分区表的基本概念从以下几个方面展开介绍: 分区表基本概念:从表分区的基本概念出发,介绍分区表的catalog存储方式以及内部对应原理。 分区策略:从分区表所支持的基本类型出发,介绍各种分区模式下对应的特性以及能够达到的优化特点和效果。 基本概念 分区策略 分区基本使用 父主题: 分区表
  • 示例 -- 修改表的默认类型 gaussdb=# set enable_default_ustore_table=off; --准备数据。 gaussdb=# CREATE TABLE t1(c1 int, c2 int); gaussdb=# INSERT INTO t1 VALUES(1, 1); gaussdb=# INSERT INTO t1 VALUES(2, 2); --创建全量物化视图。 gaussdb=# CREATE MATERIALIZED VIEW mv AS select count(*) from t1; CREATE MATERIALIZED VIEW --查询物化视图结果。 gaussdb=# SELECT * FROM mv; count ------- 2 (1 row) --向物化视图中基表插入数据。 gaussdb=# INSERT INTO t1 VALUES(3, 3); INSERT 0 1 --对全量物化视图做全量刷新。 gaussdb=# REFRESH MATERIALIZED VIEW mv; REFRESH MATERIALIZED VIEW --查询物化视图结果。 gaussdb=# SELECT * FROM mv; count ------- 3 (1 row) --删除物化视图,删除表。 gaussdb=# DROP MATERIALIZED VIEW mv; DROP MATERIALIZED VIEW gaussdb=# DROP TABLE t1; DROP TABLE
  • 范围分区 范围分区(Range Partition)根据为每个分区建立分区键的值范围将数据映射到分区。范围分区是生产系统中最常见的分区类型,通常在以时间维度(Date、Time Stamp)描述数据场景中使用。范围分区有两种语法格式,示例如下: VALUES LESS THAN的语法格式 对于从句是VALUE LESS THAN的语法格式,范围分区策略的分区键最多支持16列。 单列分区键示例如下: gaussdb=# CREATE TABLE range_sales_single_key ( product_id INT4 NOT NULL, customer_id INT4 NOT NULL, time DATE, channel_id CHAR(1), type_id INT4, quantity_sold NUMERIC(3), amount_sold NUMERIC(10,2) ) PARTITION BY RANGE (time) ( PARTITION date_202001 VALUES LESS THAN ('2020-02-01'), PARTITION date_202002 VALUES LESS THAN ('2020-03-01'), PARTITION date_202003 VALUES LESS THAN ('2020-04-01'), PARTITION date_202004 VALUES LESS THAN ('2020-05-01') ); gaussdb=# DROP TABLE range_sales_single_key; 其中date_202002表示2020年2月的分区,将包含分区键值从2020年2月1日到2020年2月29日的数据。 每个分区都有一个VALUES LESS子句,用于指定分区的非包含上限。大于或等于该分区键的任何值都将添加到下一个分区。除第一个分区外,所有分区都具有由前一个分区的VALUES LESS子句指定的隐式下限。可以为最高分区定义MAXVALUE关键字,MAXVALUE表示一个虚拟无限值,其排序高于分区键的任何其他可能值,包括空值。 多列分区键示例如下: gaussdb=# CREATE TABLE range_sales ( c1 INT4 NOT NULL, c2 INT4 NOT NULL, c3 CHAR(1) ) PARTITION BY RANGE (c1,c2) ( PARTITION p1 VALUES LESS THAN (10,10), PARTITION p2 VALUES LESS THAN (10,20), PARTITION p3 VALUES LESS THAN (20,10) ); INSERT INTO range_sales VALUES(9,5,'a'); INSERT INTO range_sales VALUES(9,20,'a'); INSERT INTO range_sales VALUES(9,21,'a'); INSERT INTO range_sales VALUES(10,5,'a'); INSERT INTO range_sales VALUES(10,15,'a'); INSERT INTO range_sales VALUES(10,20,'a'); INSERT INTO range_sales VALUES(10,21,'a'); INSERT INTO range_sales VALUES(11,5,'a'); INSERT INTO range_sales VALUES(11,20,'a'); INSERT INTO range_sales VALUES(11,21,'a'); gaussdb=# SELECT * FROM range_sales PARTITION (p1); c1 | c2 | c3 ----+----+---- 9 | 5 | a 9 | 20 | a 9 | 21 | a 10 | 5 | a (4 rows) gaussdb=# SELECT * FROM range_sales PARTITION (p2); c1 | c2 | c3 ----+----+---- 10 | 15 | a (1 row) gaussdb=# SELECT * FROM range_sales PARTITION (p3); c1 | c2 | c3 ----+----+---- 10 | 20 | a 10 | 21 | a 11 | 5 | a 11 | 20 | a 11 | 21 | a (5 rows) gaussdb=# DROP TABLE range_sales; 多列分区的分区规则如下: 从第一列开始比较。 如果插入的当前列小于分区当前列边界值,则直接插入。 如果插入的当前列等于分区当前列的边界值,则比较插入值的下一列与分区下一列边界值的大小。 如果插入的当前列大于分区当前列的边界值,则换下一个分区进行比较。 START END语法格式 对于从句是START END语法格式,范围分区策略的分区键最多支持1列。 示例如下: gaussdb=# -- 创建表空间 CREATE TABLESPACE startend_tbs1 LOCATION '/home/omm/startend_tbs1'; CREATE TABLESPACE startend_tbs2 LOCATION '/home/omm/startend_tbs2'; CREATE TABLESPACE startend_tbs3 LOCATION '/home/omm/startend_tbs3'; CREATE TABLESPACE startend_tbs4 LOCATION '/home/omm/startend_tbs4'; -- 创建临时schema CREATE SCHEMA tpcds; SET CURRENT_SCHEMA TO tpcds; -- 创建分区表,分区键是integer类型 CREATE TABLE tpcds.startend_pt (c1 INT, c2 INT) TABLESPACE startend_tbs1 PARTITION BY RANGE (c2) ( PARTITION p1 START(1) END(1000) EVERY(200) TABLESPACE startend_tbs2, PARTITION p2 END(2000), PARTITION p3 START(2000) END(2500) TABLESPACE startend_tbs3, PARTITION p4 START(2500), PARTITION p5 START(3000) END(5000) EVERY(1000) TABLESPACE startend_tbs4 ) ENABLE ROW MOVEMENT; -- 查看分区表信息 gaussdb=# SELECT relname, boundaries, spcname FROM pg_partition p JOIN pg_tablespace t ON p.reltablespace=t.oid and p.parentid='tpcds.startend_pt'::regclass ORDER BY 1; relname | boundaries | spcname -------------+------------+--------------- p1_0 | {1} | startend_tbs2 p1_1 | {201} | startend_tbs2 p1_2 | {401} | startend_tbs2 p1_3 | {601} | startend_tbs2 p1_4 | {801} | startend_tbs2 p1_5 | {1000} | startend_tbs2 p2 | {2000} | startend_tbs1 p3 | {2500} | startend_tbs3 p4 | {3000} | startend_tbs1 p5_1 | {4000} | startend_tbs4 p5_2 | {5000} | startend_tbs4 startend_pt | | startend_tbs1 (12 rows) --清理示例 gaussdb=# DROP TABLE tpcds.startend_pt; 父主题: 分区策略
  • 分区表统计信息 对于分区表,支持收集分区级统计信息,相关统计信息可以在pg_partition和pg_statistic系统表以及pg_stats和pg_ext_stats视图中查询。分区级统计信息适用于分区表进行静态剪枝后,分区表的扫描范围剪枝到单分区的场景下。分区级统计信息的支持范围为:分区级的page数和tuple数、单列统计信息、多列统计信息、表达式索引统计信息。 分区表统计信息有以下收集方式: 级联收集统计信息 指定具体单个分区收集统计信息 级联收集统计信息 分区级统计信息 父主题: 分区表查询优化
  • 场景描述 当对分区表使用Max/Min函数时,通常SQL引擎的实现方式是先通过Partition Iterator + PartitionScan对分区表做全量扫描然后进行Sort + Limit操作。如果分区是索引扫描,可以先对每个分区进行Limit操作,计算Max/Min值,最后在分区表上做Sort + Limit操作。这样分区表上做Sort时,由于每个分区已经获取Max/Min值,所以Sort的数据量跟分区数相同,这时极大的减少了Sort开销。
  • 对一级分区表移动分区 使用ALTER TABLE MOVE PARTITION可以对一级分区表移动分区。 例如,通过指定分区名将范围分区表range_sales的分区date_202001移动到表空间tb1中。 ALTER TABLE range_sales MOVE PARTITION date_202001 TABLESPACE tb1; 或者,通过指定分区值将列表分区表list_sales中'0'所对应的分区移动到表空间tb1中。 ALTER TABLE list_sales MOVE PARTITION FOR ('0') TABLESPACE tb1; 父主题: 移动分区
  • 静态编译架构 从整个数据库服务的组成构架来看,存储引擎向上对接SQL引擎,为SQL引擎提供或接收标准化的数据格式(元组或向量数组)。存储引擎向下对接存储介质,按照特定的数据组织方式,以页面、压缩单元(Compress Unit)或其他形式为单位,通过存储介质提供的特定接口,对存储介质中的数据完成读写操作。GaussDB通过静态编译使数据库专业人员可以为特定的应用程序需求选择专用的存储引擎。为了减少对执行引擎的干扰,提供行存访问接口层TableAM,用来屏蔽底层行存引擎带来的差异,使得不同行存引擎可以分别独立演进。如下图所示。 在此基础之上,存储引擎通过日志系统提供数据的持久化和可靠性能力。通过并发控制(事务)系统保证同时执行的、多个读写操作之间的原子性、一致性和隔离性,通过索引系统提供对特定数据的加速寻址和查询能力,通过主备复制系统提供整个数据库服务的高可用能力。 行存引擎主要面向OLTP(OnLine Transaction Processing)类业务应用场景,适合高并发、小数据量的单点或小范围数据读写操作。行存引擎向上为SQL引擎提供元组形式的读写接口,向下以页面为单位通过可扩展的介质管理器对存储介质进行读写操作,并通过页面粒度的共享缓冲区来优化读写操作的效率。对于读写并发操作,采用多版本并发控制(MVCC,Multi-Version Concurrency Control);对于写写并发操作,采用基于两阶段锁协议(2PL,Two-Phase Locking)的悲观并发控制(PCC,Pessimistic Concurrency Control)。当前,行存引擎默认的介质管理器采用磁盘文件系统接口,后续可扩展支持块设备等其他类型的存储介质。GaussDB行存引擎可以选择基于Append update 的Astore或基于In-place update的Ustore。 父主题: 存储引擎体系架构概述
  • 概述 Enhanced Toast是一种用于处理超大字段的技术。首先,减少了Toast Pointer中的冗余信息,存储支持单表超长字段列数超过500列。其次,优化了主表与线外存储表之间的映射关系,无需通过pg_toast_index来存储主表数据与线外存储表数据的关系,降低了用户存储空间。最后,Enhanced Toast技术通过让分割数据自链接,消除了Oid分配的依赖,极大地加快了写入效率。 Astore存储引擎不支持Enhanced Toast。 不支持对Enhanced Toast类型的线外存储表单独进行Vacuum Full操作。 父主题: Enhanced Toast
  • Ustore事务模型 GaussDB事务基础: 事务启动时不会自动分配XID,该事务中的第一条DML/DDL语句运行时才会真正为该事务分配XID。 事务结束时,会产生代表事务提交状态的C LOG (Commit Log),CLOG共有四种状态:事务运行中、事务提交、事务同步回滚、子事务提交。每个事务的 CLOG状态位为2 bits,CLOG页面上每个字节可以表示四个事务的提交状态。 事务结束时,还会产生代表事务提交顺序的 CS N(Commit sequence number),CSN为实例级变量,每个XID都有自己对应的唯一CSN。CSN可以标记事务的以下状态:事务提交中、事务提交、事务回滚、事务已冻结等。 事务提交 事务回滚 父主题: Ustore存储引擎
  • 分割分区 用户可以使用分割分区的命令来将一个分区分割为两个或多个新分区。当分区数据太大,或者需要对有MAXVALUE的范围分区/DEFAULT的列表分区新增分区时,可以考虑执行该操作。分割分区可以指定分割点将一个分区分割为两个新分区,也可以不指定分割点将一个分区分割为多个新分区。分割分区可以通过指定分区名或者分区值来进行。 分割分区不能作用于哈希分区上。 不支持对二级分区表的一级分区进行分割。 执行分割分区命令会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,或者用户自行重建Global索引。 分割的目标分区如果包含分类索引时,该分区不支持分割。 分割后的新分区,可以与源分区名字相同,比如将分区p1分割为p1,p2。但数据库不会将分割前后相同名的分区视为同一个分区,这会影响分割期间对源分区p1查询,具体请参见DQL/DML-DDL并发。 对范围分区表分割分区 对间隔分区表分割分区 对列表分区表分割分区 对*-RANGE二级分区表分割二级分区 对*-LIST二级分区表分割二级分区 父主题: 分区表运维管理
  • 数据分区运维管理 分区表技术为数据生命周期管理(Data Life Cycle Management,DLM)提供了灵活性的支持,数据生命周期管理是一组用于在数据的整个使用寿命中管理数据的过程和策略。其中一个重要组成部分是确定在数据生命周期的任何时间点存储数据的最合适和最经济高效的介质:日常操作中使用的较新数据存储在最快、可用性最高的存储层上,而不经常访问的较旧数据可能存储在成本较低、效率较低的存储层。较旧的数据也可能更新的频率较低,因此将数据压缩并存储为只读是有意义的。 分区表为实施DLM解决方案提供了理想的环境,通过不同分区使用不同表空间,最大限度在确保易用性的同时,实现了有效的数据生命周期的成本优化。这部分的设置由数据库运维人员在服务端设置操作完成,实际用户并不感知这一层面的优化设置,对用户而言逻辑上仍然是对同一张表的查询操作。此外不同分区可以分别实施备份、恢复、索引重建等运维性质的操作,能够对单个数据集不同子类进行分治操作,满足用户业务场景的差异化需求。 父主题: 大容量数据库
  • 对列表分区表分割分区 使用ALTER TABLE SPLIT PARTITION可以对列表分区表分割分区。 例如,假设列表分区表list_sales的分区channel2定义范围为('6', '7', '8', '9')。可以指定分割点('6', '7')将分区channel2分割为两个分区,并更新Global索引。 ALTER TABLE list_sales SPLIT PARTITION channel2 VALUES ('6', '7') INTO ( PARTITION channel2_1, --第一个分区范围是('6', '7') PARTITION channel2_2 --第二个分区范围是('8', '9') ) UPDATE GLOBAL INDEX; 或者,不指定分割点,将分区channel2分割为多个分区,并更新Global索引。 ALTER TABLE list_sales SPLIT PARTITION channel2 INTO ( PARTITION channel2_1 VALUES ('6'), PARTITION channel2_2 VALUES ('8'), PARTITION channel2_3 --第三个分区范围是('7', '9') )UPDATE GLOBAL INDEX; 又或者,通过指定分区值而不是指定分区名来分割分区。 ALTER TABLE list_sales SPLIT PARTITION FOR ('6') VALUES ('6', '7') INTO ( PARTITION channel2_1, --第一个分区范围是('6', '7') PARTITION channel2_2 --第二个分区范围是('8', '9') ) UPDATE GLOBAL INDEX; 若对DEFAULT分区进行分割,前面几个分区不能申明DEFAULT范围,最后一个分区会继承DEFAULT分区范围。 父主题: 分割分区
  • 使用Ustore的优势 最新版本和历史版本分离存储,相比Astore扫描范围小。去除Astore的HOT chain,非索引列/索引列更新,Heap均可原位更新,ROWID可保持不变。历史版本可批量回收,空间膨胀可控。 B-tree索引增加了事务信息,能够独立进行MVCC。增加了IndexOnlyScan的比例,大大减少回表次数。 不依赖Vacuum进行旧版本清理。独立的空间回收能力,索引与堆表解耦,可独立清理,IO平稳度更优。 大并发更新同一行的场景,相对于Astore的ROWID会偏移,Ustore的原位更新机制保证了元组ROWID稳定,先到先得,更新时延相对稳定。 支持闪回功能。 Ustore DML在修改数据页面时,也需要同步生成Undo,因此更新操作开销会稍大一些。此外单条Tuple扫描开销由于需要复制(Astore返回指针)也会大一些。
  • 文件组织结构 如需查询当前回滚段使用的存储方式是页式或段页式,可以查询系统表。当前仅支持页式。 示例: gaussdb=# SELECT * FROM gs_global_config WHERE name LIKE '%undostoragetype%'; name | value -----------------+--------- undostoragetype | page (1 row) 当回滚段使用的存储方式为页式: txn page所在文件组织结构: $node_dir/undo/{permanent|unlogged|temp}/$undo_zone_id.meta.$segno undo row所在文件组织结构: $node_dir/undo/{permanent|unlogged|temp}/$undo_zone_id.$segno 父主题: Undo
  • 物化视图 物化视图是一种特殊的物理表,物化视图是相对普通视图而言的。普通视图是虚拟表,应用的局限性较大,任何对视图的查询实际上都是转换为对SQL语句的查询,性能并没有实际提高。物化视图实际上就是存储SQL执行语句的结果,起到缓存的效果。物化视图常用的操作包括创建、查询、删除和刷新。 根据创建规则,物化视图分为全量物化视图和增量物化视图。全量物化视图只支持全量刷新;增量物化视图支持全量刷新和增量刷新两种方式。全量刷新会将基表中的数据全部重新刷入物化视图中,而增量刷新只会将两次刷新间隔期间的基表产生的增量数据刷入物化视图中。 目前Ustore引擎不支持创建、使用物化视图。 全量物化视图 增量物化视图
  • 使用示例 示例(需将undo_retention_time参数设置为大于0的值): gaussdb=# DROP TABLE IF EXISTS "public".flashtest; NOTICE: table "flashtest" does not exist, skipping DROP TABLE --创建表flashtest gaussdb=# CREATE TABLE "public".flashtest (col1 INT,col2 TEXT) WITH(storage_type=ustore); CREATE TABLE --查询csn gaussdb=# SELECT int8in(xidout(next_csn)) FROM gs_get_next_xid_csn(); int8in ---------- 79351682 (1 rows) --查询当前时间戳 gaussdb=# select now(); now ------------------------------- 2023-09-13 19:35:26.011986+08 (1 row) --插入数据 gaussdb=# INSERT INTO flashtest VALUES(1,'INSERT1'),(2,'INSERT2'),(3,'INSERT3'),(4,'INSERT4'),(5,'INSERT5'),(6,'INSERT6'); INSERT 0 6 gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 3 | INSERT3 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 6 | INSERT6 (6 rows) --闪回查询某个csn处的表 gaussdb=# SELECT * FROM flashtest TIMECAPSULE CSN 79351682; col1 | col2 ------+------ (0 rows) gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 3 | INSERT3 6 | INSERT6 (6 rows) --闪回查询某个时间戳处的表 gaussdb=# SELECT * FROM flashtest TIMECAPSULE TIMESTAMP '2023-09-13 19:35:26.011986'; col1 | col2 ------+------ (0 rows) gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 3 | INSERT3 6 | INSERT6 (6 rows) --闪回查询某个时间戳处的表 gaussdb=# SELECT * FROM flashtest TIMECAPSULE TIMESTAMP to_timestamp ('2023-09-13 19:35:26.011986', 'YYYY-MM-DD HH24:MI:SS.FF'); col1 | col2 ------+------ (0 rows) --闪回查询某个csn处的表,并对表进行重命名 gaussdb=# SELECT * FROM flashtest AS ft TIMECAPSULE CSN 79351682; col1 | col2 ------+------ (0 rows) gaussdb=# DROP TABLE IF EXISTS "public".flashtest; DROP TABLE
  • 语法 {[ ONLY ] table_name [ * ] [ partition_clause ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] [ TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] ] [TIMECAPSULE { TIMESTAMP | CSN } expression ] |( select ) [ AS ] alias [ ( column_alias [, ...] ) ] |with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ] |function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [, ...] | column_definition [, ...] ) ] |function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] ) |from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]}
  • 开启/关闭范围分区自动扩展 使用ALTER TABLE SET INTERVAL可以开启/关闭范围分区自动扩展。 例如,开启范围分区自动扩展。 gaussdb=# CREATE TABLE range_int (c1 int, c2 int) PARTITION BY RANGE (c1) ( PARTITION p1 VALUES LESS THAN (5), PARTITION p2 VALUES LESS THAN (10), PARTITION p3 VALUES LESS THAN (15) ); gaussdb=# ALTER TABLE range_int SET INTERVAL (5); 开启范围分区自动扩展要求分区表中不能存在分区键值为MAXVALUE的分区。 开启范围分区自动扩展只支持一级分区表、单列分区键。 关闭范围分区自动扩展。 gaussdb=# ALTER TABLE range_int SET INTERVAL (); -- 清理示例 gaussdb=# DROP TABLE range_int; 父主题: 开启/关闭分区自动扩展
  • 对二级分区表清空一级分区 使用ALTER TABLE TRUNCATE PARTITION可以清空二级分区表的一个一级分区,数据库会将这个一级分区下的所有二级分区都进行清空。 例如,通过指定分区名清空二级分区表range_list_sales的一级分区date_202005,并更新Global索引。 ALTER TABLE range_list_sales TRUNCATE PARTITION date_202005 UPDATE GLOBAL INDEX; 或者,通过指定分区值来清空二级分区表range_list_sales中('2020-05-08')所对应的一级分区。由于不带UPDATE GLOBAL INDEX子句,执行该命令后Global索引会失效。 ALTER TABLE range_list_sales TRUNCATE PARTITION FOR ('2020-05-08'); 父主题: 清空分区
  • Enhanced Toast相关DDL操作 Enhanced Toast表的创建 建表时指定Toast表的存储类型为Enhanced Toast或者Toast: gaussdb=# CREATE TABLE test_toast (id int, content text) WITH(toast.toast_storage_type=toast); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=toast gaussdb=# drop table test_toast; DROP TABLE gaussdb=# CREATE TABLE test_toast (id int, content text) WITH(toast.toast_storage_type=enhanced_toast); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=enhanced_toast gaussdb=# DROP TABLE test_toast; DROP TABLE 建表时不指定线外存储表的类型,则创建线外存储表类型依赖于GUC参数enable_enhance_toast_table: gaussdb=# show enable_enhance_toast_table; enable_enhance_toast_table ---------------------------- on (1 row) gaussdb=# CREATE TABLE test_toast (id int, content text); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=enhanced_toast gaussdb=# DROP TABLE test_toast; DROP TABLE gaussdb=# SET enable_enhance_toast_table = off; SET gaussdb=# show enable_enhance_toast_table; enable_enhance_toast_table ---------------------------- off (1 row) gaussdb=# CREATE TABLE test_toast (id int, content text); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=toast gaussdb=# DROP TABLE test_toast; DROP TABLE 线外存储表结构的升级 当GUC参数“enable_enhance_toast_table=on”时,线外存储表支持通过Vacuum Full操作将Toast升级为Enhanced Toast结构。 gaussdb=# CREATE TABLE test_toast (id int, content text); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=toast gaussdb=# VACUUM FULL test_toast; VACUUM gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=enhanced_toast gaussdb=# DROP TABLE test_toast; DROP TABLE 分区表merge操作 支持将分区表的分区间不同的线外存储表类型进行合并操作。 对于相同类型的线外存储分区,合并与原有逻辑保持一致,进行物理合并。 对于不同类型的线外存储分区,合并后的分区线外存储表为Enhanced Toast表,需要进行逻辑合并,性能劣于物理合并。 gaussdb=# CREATE TABLE test_partition_table(a int, b text)PARTITION BY range(a)(partition p1 values less than (2000),partition p2 values less than (3000)); gaussdb=# SELECT relfilenode FROM pg_partition WHERE relname='p1'; relfilenode ------------- 17529 (1 row) gaussdb=# \d+ pg_toast.pg_toast_part_17529 TOAST table "pg_toast.pg_toast_part_17529" Column | Type | Storage ------------+---------+--------- chunk_id | oid | plain chunk_seq | integer | plain chunk_data | bytea | plain Options: storage_type=ustore, toast_storage_type=toast gaussdb=# SELECT relfilenode from pg_partition WHERE relname='p2'; relfilenode ------------- 17528 (1 row) gaussdb=# \d+ pg_toast.pg_toast_part_17528 TOAST table "pg_toast.pg_toast_part_17528" Column | Type | Storage ------------+---------+--------- chunk_seq | integer | plain next_chunk | tid | plain chunk_data | bytea | plain Options: storage_type=ustore, toast_storage_type=enhanced_toast gaussdb=# ALTER TABLE test_partition_table MERGE PARTITIONS p1,p2 INTO partition p1_p2; ALTER TABLE gaussdb=# SELECT reltoastrelid::regclass FROM pg_partition WHERE relname='p1_p2'; reltoastrelid ------------------------------ pg_toast.pg_toast_part_17559 (1 row) gaussdb=# \d+ pg_toast.pg_toast_part_17559 TOAST table "pg_toast.pg_toast_part_17559" Column | Type | Storage ------------+---------+--------- chunk_seq | integer | plain next_chunk | tid | plain chunk_data | bytea | plain Options: storage_type=ustore, toast_storage_type=enhanced_toast gaussdb=# DROP TABLE test_partition_table; DROP TABLE 父主题: Enhanced Toast
  • 分区表运维管理 分区表运维管理包括分区管理、分区表管理、分区索引管理和分区表业务并发支持等。 分区管理:也称分区级DDL,包括新增(Add)、删除(Drop)、交换(Exchange)、清空(Truncate)、分割(Split)、合并(Merge)、移动(Move)、重命名(Rename)共8种。 对于哈希分区,涉及分区数的变更会导致数据re-shuffling,故当前GaussDB不支持导致Hash分区数变更的操作,包括新增(Add)、删除(Drop)、分割(Split)、合并(Merge)这4种。 涉及分区数据变更的操作会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,包括删除(Drop)、交换(Exchange)、清空(Truncate)、分割(Split)、合并(Merge)这5种。 大部分分区DDL支持partition/subpartition和partition/subpartition for指定分区两种写法,前者需要指定分区名,后者需要指定分区定义范围内的任一分区值。比如假设分区part1的范围定义为[100, 200),那么partition part1和partition for(150)这两种写法是等价的。 不同分区DDL的执行代价各不相同,由于在执行分区DDL过程中目标分区会被锁住,用户需要评估其代价以及对业务的影响。一般而言,分割(Split)、合并(Merge)的执行代价远大于其他分区DDL,与源分区的大小正相关;交换(Exchange)的代价主要源于Global索引的重建和validation校验;移动(Move)的代价限制于磁盘I/O;其余分区DDL的执行代价都很低。 分区表管理:除了继承普通表的功能外,还支持开启/关闭分区表行迁移的功能。 分区索引管理:支持用户设置索引/索引分区不可用,或者重建不可用的索引/索引分区,比如由于分区管理操作导致的Global索引失效场景。 分区表业务并发支持:当分区级DDL与分区DQL/DML作用于不同分区时,支持二者执行层面的并发。 新增分区 删除分区 交换分区 清空分区 分割分区 合并分区 移动分区 重命名分区 分区表行迁移 分区表索引重建/不可用 父主题: 分区表
  • 分区表(母表) 实际对用户体现的表,用户对该表进行常规DML语句的增、删、查、改操作。通常使用在建表DDL语句显式的使用PARTITION BY语句进行定义,创建成功以后在pg_class表中新增一个entry,并且parttype列内容为'p'(一级分区)或者's'(二级分区),表明该entry为分区表的母表。分区母表通常是一个逻辑形态,对应的表文件并不存放数据。 示例1:t1_hash为一个一级分区表,分区类型为hash: gaussdb=# CREATE TABLE t1_hash (c1 INT, c2 INT, c3 INT) PARTITION BY HASH(c1) ( PARTITION p0, PARTITION p1, PARTITION p2, PARTITION p3, PARTITION p4, PARTITION p5, PARTITION p6, PARTITION p7, PARTITION p8, PARTITION p9 ); gaussdb=# \d+ t1_hash Table "public.t1_hash" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+---------+--------------+------------- c1 | integer | | plain | | c2 | integer | | plain | | c3 | integer | | plain | | Partition By HASH(c1) Number of partitions: 10 (View pg_partition to check each partition range.) Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off --查询t1_hash分区类型 gaussdb=# SELECT relname, parttype FROM pg_class WHERE relname = 't1_hash'; relname | parttype ---------+---------- t1_hash | p (1 row) gaussdb=# DROP TABLE t1_hash; 示例2:t1_sub_rr为一个二级分区表,分区类型为range-list: gaussdb=# CREATE TABLE t1_sub_rr ( c1 INT, c2 INT, c3 INT ) PARTITION BY RANGE (c1) SUBPARTITION BY LIST (c2) ( PARTITION p_2021 VALUES LESS THAN (2022) ( SUBPARTITION p_2021_1 VALUES (1), SUBPARTITION p_2021_2 VALUES (2), SUBPARTITION p_2021_3 VALUES (3) ), PARTITION p_2022 VALUES LESS THAN (2023) ( SUBPARTITION p_2022_1 VALUES (1), SUBPARTITION p_2022_2 VALUES (2), SUBPARTITION p_2022_3 VALUES (3) ), PARTITION p_2023 VALUES LESS THAN (2024) ( SUBPARTITION p_2023_1 VALUES (1), SUBPARTITION p_2023_2 VALUES (2), SUBPARTITION p_2023_3 VALUES (3) ), PARTITION p_2024 VALUES LESS THAN (2025) ( SUBPARTITION p_2024_1 VALUES (1), SUBPARTITION p_2024_2 VALUES (2), SUBPARTITION p_2024_3 VALUES (3) ), PARTITION p_2025 VALUES LESS THAN (2026) ( SUBPARTITION p_2025_1 VALUES (1), SUBPARTITION p_2025_2 VALUES (2), SUBPARTITION p_2025_3 VALUES (3) ), PARTITION p_2026 VALUES LESS THAN (2027) ( SUBPARTITION p_2026_1 VALUES (1), SUBPARTITION p_2026_2 VALUES (2), SUBPARTITION p_2026_3 VALUES (3) ) ); gaussdb=# \d+ t1_sub_rr Table "public.t1_sub_rr" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+---------+--------------+------------- c1 | integer | | plain | | c2 | integer | | plain | | c3 | integer | | plain | | Partition By RANGE(c1) Subpartition By LIST(c2) Number of partitions: 6 (View pg_partition to check each partition range.) Number of subpartitions: 18 (View pg_partition to check each subpartition range.) Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off --查询t1_sub_rr分区类型 gaussdb=# SELECT relname, parttype FROM pg_class WHERE relname = 't1_sub_rr'; relname | parttype -----------+---------- t1_sub_rr | s (1 row) gaussdb=# DROP TABLE t1_sub_rr; 父主题: 基本概念
  • PCR UBTree 相比于RCR版本的UBTree,PCR版本的UBTree有以下特点。 索引元组的事务信息统一由TD槽进行管理。 增加了Undo操作,插入和删除前需要先写入Undo,事务abort时需要进行回滚操作。 支持闪回。 PCR UBTree通过在创建索引时with选项设置“index_txntype=pcr”或者设置GUC参数“index_txntype=pcr”进行创建,若没有显示指定with选项或者GUC,则默认创建RCR版本的UBTree。 注意,当前版本PCR索引在大数据量的回滚上耗时可能较长(回滚时间随数据量增长可能呈指数型增长,数据量太大可能导致会回滚无法完全执行),回滚时间会在新的版本进行优化。以下是当前版本回滚时间的具体规格: 表1 PCR索引回滚时间的规格 类型/数据量 100 1000 1万 10万 100万 带PCR索引的回滚时间 0.692 ms 9.610 ms 544.678 ms 52,963.754 ms 89,440,029.048 ms 不带PCR索引的回滚时间 0.226 ms 0.916 ms 8.974 ms 94.903 ms 1206.177 ms 两者比值 3.06 10.49 60.70 558.08 74,151.66 PCR UBTree多版本管理 PCR UBTree可见性机制 PCR UBTree增删改查 PCR UBTree空间管理 父主题: UBTree
  • 对范围分区表分割分区 使用ALTER TABLE SPLIT PARTITION可以对范围分区表分割分区。 例如,假设范围分区表range_sales的分区date_202001定义范围为['2020-01-01', '2020-02-01')。可以指定分割点'2020-01-16'将分区date_202001分割为两个分区,并更新Global索引。 ALTER TABLE range_sales SPLIT PARTITION date_202001 AT ('2020-01-16') INTO ( PARTITION date_202001_p1, --第一个分区上界是'2020-01-16' PARTITION date_202001_p2 --第二个分区上界是'2020-02-01' ) UPDATE GLOBAL INDEX; 或者,不指定分割点,将分区date_202001分割为多个分区,并更新Global索引。 ALTER TABLE range_sales SPLIT PARTITION date_202001 INTO ( PARTITION date_202001_p1 VALUES LESS THAN ('2020-01-11'), PARTITION date_202001_p2 VALUES LESS THAN ('2020-01-21'), PARTITION date_202001_p3 --第三个分区上界是'2020-02-01' )UPDATE GLOBAL INDEX; 又或者,通过指定分区值而不是指定分区名来分割分区。 ALTER TABLE range_sales SPLIT PARTITION FOR ('2020-01-15') AT ('2020-01-16') INTO ( PARTITION date_202001_p1, --第一个分区上界是'2020-01-16' PARTITION date_202001_p2 --第二个分区上界是'2020-02-01' ) UPDATE GLOBAL INDEX; 若对MAXVALUE分区进行分割,前面几个分区不能申明MAXVALUE范围,最后一个分区会继承MAXVALUE分区范围。 父主题: 分割分区
  • 清空分区 用户可以使用清空分区的命令来快速清空分区的数据。与删除分区功能类似,区别在于清空分区只会删除分区中的数据,分区的定义和物理文件都会保留。清空分区可以通过指定分区名或者分区值来进行。 执行清空分区命令会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,或者用户自行重建Global索引。 对一级分区表清空分区 对二级分区表清空一级分区 对二级分区表清空二级分区 父主题: 分区表运维管理
  • 对二级分区表重命名二级分区 使用ALTER TABLE RENAME SUBPARTITION可以对二级分区表重命名二级分区。 例如,通过指定分区名将二级分区表range_list_sales的分区date_202001_channel1重命名。 ALTER TABLE range_list_sales RENAME SUBPARTITION date_202001_channel1 TO date_202001_channelnew; 或者,通过指定分区值将二级分区表range_list_sales中('2020-01-08', '0')所对应的分区重命名。 ALTER TABLE range_list_sales RENAME SUBPARTITION FOR ('2020-01-08', '0') TO date_202001_channelnew; 父主题: 重命名分区
  • 二级分区表自动扩展 创建二级分区表时,可以在创建列表分区定义上指定AUTOMATIC关键字,以支持二级分区表的一级自动扩展/二级自动扩展。二级列表分区表自动扩展只支持单列分区键。 创建二级分区表时,在创建一级分区定义上指定AUTOMATIC,以支持一级自动扩展。 gaussdb=# CREATE TABLE autolist_range (c1 int, c2 int) PARTITION BY LIST (c1) AUTOMATIC SUBPARTITION BY RANGE (c2) ( PARTITION p1 VALUES (1, 2, 3) ( SUBPARTITION sp11 VALUES LESS THAN (5), SUBPARTITION sp12 VALUES LESS THAN (10) ), PARTITION p2 VALUES (4, 5, 6) ( SUBPARTITION sp21 VALUES LESS THAN (5), SUBPARTITION sp22 VALUES LESS THAN (10) ) ); 当插入数据无法匹配到已有的任意一级分区时,会自动创建一个新的一级分区,新一级分区的范围定义为单key(新数据对应的新分区键值),其下面会定义一个全集的二级分区。 --一级分区键插入数据9,因为现有的一级分区p1、p2的键值中不包含9,所以自动创建一个新的一级分区sys_p1,分区定义为VALUES (9) gaussdb=# INSERT INTO autolist_range VALUES (9, 0); 这一功能与如下命令等价: gaussdb=# ALTER TABLE autolist_range ADD PARTITION sys_p1 VALUES (9); gaussdb=# INSERT INTO autolist_range VALUES (9, 0); gaussdb=# DROP TABLE autolist_range; 创建二级分区表时,在二级分区定义上指定AUTOMATIC,以支持二级自动扩展。 gaussdb=# CREATE TABLE range_autolist (c1 int, c2 int) PARTITION BY RANGE (c1) SUBPARTITION BY LIST (c2) AUTOMATIC ( PARTITION p1 VALUES LESS THAN (5) ( SUBPARTITION sp11 VALUES (1, 2, 3), SUBPARTITION sp12 VALUES (4, 5, 6) ), PARTITION p2 VALUES LESS THAN (10) ( SUBPARTITION sp21 VALUES (1, 2, 3), SUBPARTITION sp22 VALUES (4, 5, 6) ) ); 当插入数据无法匹配到已有的任意二级分区时,会在对应的一级分区下自动创建一个新的二级分区,新二级分区的范围定义为单key(新数据对应的新分区键值)。 --二级分区键插入数据0,因为现有的二级分区的键值中不包含0,所以自动创建一个新的二级分区sys_sp1,分区定义为VALUES (0) gaussdb=# INSERT INTO range_autolist VALUES (4, 0); 这一功能与如下命令等价: gaussdb=# ALTER TABLE range_autolist MODIFY PARTITION p1 ADD SUBPARTITION sys_sp1 VALUES (0); gaussdb=# INSERT INTO range_autolist VALUES (4, 0); -- 清理示例 gaussdb=# DROP TABLE range_autolist; 创建二级分区表时,在一级/二级分区定义上同时指定AUTOMATIC,表示支持一级自动扩展/二级自动扩展。 gaussdb=# CREATE TABLE autolist_autolist (c1 int, c2 int) PARTITION BY LIST (c1) AUTOMATIC SUBPARTITION BY LIST (c2) AUTOMATIC ( PARTITION p1 VALUES (1, 2, 3) ( SUBPARTITION sp11 VALUES (1, 2, 3), SUBPARTITION sp12 VALUES (4, 5, 6) ), PARTITION p2 VALUES (4, 5, 6) ( SUBPARTITION sp21 VALUES (1, 2, 3), SUBPARTITION sp22 VALUES (4, 5, 6) ) ); 当插入数据无法匹配到已有的任意一级分区时,会自动创建一个新的一级分区,新一级分区的范围定义为单key(新数据对应的新分区键值),其下面会定义一个范围定义为单key的二级分区。 --一级分区键插入数据9,因为现有的一级分区p1、p2的键值中不包含9,所以自动创建一个新的一级分区sys_p1,分区定义为VALUES (9);同时二级分区键插入数据0,因为现有的二级分区的键值中不包含0,所以会在新的一级分区sys_p1定义一个新的二级分区sys_sp1,分区定义为VALUES (0)。 gaussdb=# INSERT INTO autolist_autolist VALUES (9, 0); 这一功能与如下命令等价: gaussdb=# ALTER TABLE autolist_autolist ADD PARTITION sys_p1 VALUES (9) (SUBPARTITION sys_sp1 VALUES (0)); gaussdb=# INSERT INTO autolist_autolist VALUES (9, 0); 当插入数据无法匹配到已有的任意二级分区时,会在对应的一级分区下自动创建一个新的二级分区,新二级分区的范围定义为单key(新数据对应的新分区键值)。 --二级分区键插入数据0,因为现有的二级分区的键值中不包含0,所以自动创建二级分区sys_sp2,分区定义为VALUES (0) gaussdb=# INSERT INTO autolist_autolist VALUES (4, 0); 这一功能与如下命令等价: gaussdb=# ALTER TABLE autolist_autolist MODIFY PARTITION p2 ADD SUBPARTITION sys_sp2 VALUES (0); gaussdb=# INSERT INTO autolist_autolist VALUES (4, 0); -- 清理示例 gaussdb=# DROP TABLE autolist_autolist; 二级分区表的列表分区自动扩展行为受AUTOMATIC关键字的指定位置影响: 若在一级分区后指定了AUTOMATIC关键字,则仅支持一级分区自动扩展,不支持二级分区的自动扩展,且不能定义有一级分区键值为DEFAULT分区。 若在二级分区后指定了AUTOMATIC关键字,则仅支持二级分区自动扩展,不支持一级分区的自动扩展,且不能定义有二级分区键值为DEFAULT分区。 若在一级分区和二级分区后同时指定了AUTOMATIC关键字,则同时支持一级分区和二级分区自动扩展,一级分区键值和二级分区键值均不能定义有DEFAULT分区。 父主题: 列表分区自动扩展
  • 向范围分区表新增分区 使用ALTER TABLE ADD PARTITION可以将分区添加到现有分区表的最后面,新增分区的上界值必须大于当前最后一个分区的上界值。 例如,对范围分区表range_sales新增一个分区。 ALTER TABLE range_sales ADD PARTITION date_202005 VALUES LESS THAN ('2020-06-01') TABLESPACE tb1; 当范围分区表有MAXVALUE分区时,无法新增分区。可以使用ALTER TABLE SPLIT PARTITION命令分割分区。分割分区同样适用于需要在现有分区表的前面/中间添加分区的情形,请参见对范围分区表分割分区。 父主题: 新增分区
  • RCR UBTree增删改查 Insert操作:UBTree的插入逻辑基本不变,只需增加索引插入时直接获取事务信息填写xmin字段。 Delete操作:UBTree额外增加了索引删除流程,索引删除主要步骤与插入相似,获取事务信息填写xmax字段(BTree索引不维护版本信息,不需要删除操作),同时更新页面上的active_tuple_count,若active_tuple_count被减为0,则尝试页面回收。 Update操作:对于Ustore而言,数据更新对UBTree索引列的操作也与Astore有所不同,数据更新包含两种情况:索引列和非索引列更新,下图给出了UBTree在数据发生更新时的处理。 上图展示UBTree在索引列和非索引列更新的差异: 在非索引列更新的情况下,索引不发生任何变化。index tuple仍指向第一次插入的data tuple,Uheap不会插入新的data tuple,而是修改当下data tuple并将历史数据存入Undo中。 在索引列更新的情况下,UBTree也会插入新的index tuple,但是会指向同一个data linepointer和同一个data tuple,扫描旧版本的数据则需要从Undo中读取。 Scan操作:用户在读取数据时,可通过使用索引扫描加速。UBTree支持索引数据的多版本管理及可见性检查,索引层的可见性检查使得索引扫描(Index Scan)及仅索引扫描(IndexOnly Scan)性能有所提升。 对于索引扫描: 若索引列包含所有扫描列(IndexOnly Scan),则通过扫描条件在索引上进行二分查找,找到符合条件元组即可返回数据。 若索引列不包含所有扫描列(Index Scan),则通过扫描条件在索引上进行二分查找,找到符合条件元组的TID,再通过TID到数据表上查找对应的数据元组。如下图所示。 父主题: RCR UBTree
  • Enhanced Toast运维管理 通过gs_parse_page_bypath解析主表中的ToastPointer信息。 gaussdb=# SELECT ctid,next_chunk,chunk_seq FROM pg_toast.pg_toast_part_17559; ctid | next_chunk | chunk_seq -------+------------+----------- (0,1) | (0,0) | 1 (0,2) | (0,1) | 0 (0,3) | (0,0) | 1 (0,4) | (0,3) | 0 (4 rows) gaussdb=# SELECT gs_parse_page_bypath((SELECT * FROM pg_relation_filepath('test_toast')),0,'uheap',false); gs_parse_page_bypath ------------------------------------------------------------------ ${data_dir}/gs_log/dump/1663_13113_17603_0.page (1 row) 解析文件1663_13113_17603_0.page中存储了ToastPointer的相关信息,具体如下: Toast_Pointer: column_index: 1 toast_relation_oid: 17608 --线外存储表OID信息 ctid: (4, 1) --线外存储数据链,头指针 bucket id: -1 --bucket id信息 column_index: 2 toast_relation_oid: 17608 ctid: (2, 1) bucket id: -1 Enhanced Toast数据查询,通过直接查询到的Enhanced Toast表数据可以判断其链式结构的完整性。 gaussdb=# SELECT ctid,next_chunk,chunk_seq FROM pg_toast.pg_toast_part_17559; ctid | next_chunk | chunk_seq -------+------------+----------- (0,1) | (0,0) | 1 (0,2) | (0,1) | 0 (0,3) | (0,0) | 1 (0,4) | (0,3) | 0 (4 rows) 父主题: Enhanced Toast
共100000条