华为云用户手册

  • 使用Ustore进行测试 创建Ustore表 使用CREATE TABLE语句创建Ustore表。 gaussdb=# CREATE TABLE ustore_table(a INT PRIMARY KEY, b CHAR (20)) WITH (STORAGE_TYPE=USTORE); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ustore_table_pkey" for table "ustore_table" CREATE TABLE gaussdb=# \d+ ustore_table Table "public.ustore_table" Column | Type | Modifiers | Storage | Stats target | Description --------+---------------+-----------+----------+--------------+------------- a | integer | not null | plain | | b | character(20) | | extended | | Indexes: "ustore_table_pkey" PRIMARY KEY, ubtree (a) WITH (storage_type=USTORE) TABLESPACE pg_default Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, storage_type=ustore, compression=no, segment=off 删除Ustore表 gaussdb=# DROP TABLE ustore_table; DROP TABLE 为Ustore表创建索引 Ustore当前仅支持BTree类型的多版本索引。在一些场景中,为了区别于Astore的BTree索引,也会将Ustore表的多版本BTree索引称为UBTree(Ustore BTree,UBTree介绍详见UBTree章节)。用户可以参照以下方式使用CREATE INDEX语句为Ustore表的“a”属性创建一个UBTree索引。 Ustore表不指定创建索引类型,默认创建的是UBTree索引。 UBTree索引分为RCR版本和PCR版本,默认创建RCR版本的UBTree。若在创建索引时with选项指定(index_txntype=pcr)或者指定GUC的index_txntype=pcr,则创建的是PCR版本的UBTree。 gaussdb=# CREATE TABLE test(a int); CREATE TABLE gaussdb=# CREATE INDEX UB_tree_index ON test(a); CREATE INDEX gaussdb=# \d+ test Table "public.test" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+---------+--------------+------------- a | integer | | plain | | Indexes: "ub_tree_index" ubtree (a) WITH (storage_type=USTORE) TABLESPACE pg_default Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, compression=no, storage_type=USTORE, segment=off --删除Ustore表索引。 gaussdb=# DROP TABLE test; DROP TABLE 父主题: Ustore简介
  • 存储规格 数据表最大列数不能超过1600列。 init_td(TD(Transaction Directory,事务目录)是Ustore表独有的用于存储页面事务信息的结构,TD的数量决定该页面支持的最大并发数。在创建表或索引时可以指定初始的TD大小init_td)取值范围[2, 128],默认值4。单页面支持的最大并发不超过128个。 Ustore表(不含toast情况)最大Tuple长度不能超过(8192 - MAXALIGN(56 + init_td * 26 + 4)), 其中MAXALIGN表示8字节对齐。当插入数据长度超过阈值时,用户会收到元组长度过长无法插入的报错。其中init_td对于Tuple长度的影响如下: 表init_td数量为最小值2时,Tuple长度不能超过8192 - MAXALIGN(56+2*26+4) = 8080B。 表init_td数量为默认值4时,Tuple长度不能超过8192 - MAXALIGN(56+4*26+4) = 8024B。 表init_td数量为最大值128时,Tuple长度不能超过8192 - MAXALIGN(56+128*26+4) = 4800B。 索引最大列数不能超过32列。全局分区索引最大列数不能超过31列。 索引元组长度不能超过(8192 - MAXALIGN(28 + 3 * 4 + 3 * 10) - MAXALIGN(42))/3, 其中MAXALIGN表示8字节对齐。当插入数据长度超过阈值时,用户会收到索引元组长度过长无法插入的报错,其中索引页头为28B,行指针为4B,元组CTID+INFO标记位为10B,页尾为42B。 回滚段容量最大支持16TB。 父主题: Ustore特性与规格
  • 使用Ustore的优势 最新版本和历史版本分离存储,相比Astore扫描范围小。去除Astore的HOT chain,非索引列/索引列更新,Heap均可原位更新,ROWID可保持不变。历史版本可批量回收,空间膨胀可控。 B-tree索引增加了事务信息,能够独立进行MVCC,增加了IndexOnlyScan的比例,大大减少回表次数。 不依赖Vacuum进行旧版本清理。独立的空间回收能力,索引与堆表解耦,可独立清理,IO平稳度更优。 大并发更新同一行的场景,相对于Astore的ROWID会偏移,Ustore的原位更新机制保证了元组ROWID稳定,先到先得,更新时延相对稳定。 支持闪回功能。 Ustore DML在修改数据页面时,也需要同步生成Undo,因此更新操作开销会稍大一些。此外单条Tuple扫描开销由于需要复制(Astore返回指针)也会大一些。
  • 使用Astore的优势 Astore没有回滚段,而Ustore有回滚段。对于Ustore来说,回滚段是非常重要的,回滚段损坏会导致数据丢失甚至数据库无法启动的严重问题,且Ustore恢复时同步需要Redo和Undo。由于Astore没有回滚段,旧数据都是记录在原先的文件中;所以,当数据库异常crash后恢复时,不会像Ustore数据库那样进行复杂的恢复。 由于旧的数据是直接记录在数据文件中,而不是回滚段中,所以不会经常报Snapshot Too Old错误。 回滚可以很快完成,因为回滚并不删除数据。 回滚时很复杂,在事务回滚时必须清理该事务所进行的修改,插入的记录要删除,更新的记录要更新回来,同时回滚的过程也会再次产生大量的Redo日志。 WAL日志要简单一些,仅需要记录数据文件的变化,不需要记录回滚段的变化。 支持回收站(闪回DROP、闪回Truncate)功能。
  • GaussDB 内核R2版本 - Ustore增加新的基于原位更新的行存储引擎Ustore,首次实现新旧版本的记录的分离存储。 - Ustore增加回滚段模块。 - Ustore增加回滚过程,支持同步/异步/页内模式。 - Ustore增加支持事务的增强版本B-tree。 - Astore增加闪回功能,支持闪回表/闪回查询/闪回Drop/闪回Truncate。 - Ustore不支持的特性包括:分布式/并行查询/Table Sampling/Global Temp Table/在线创建/重建索引/极致RTO/Vacuum Full/列约束DEFERRABLE以及INITIALLY DEFERRED。 父主题: 存储引擎更新说明
  • GaussDB内核503版本 - Ustore适配分布式/并行查询/Global Temp Table/Vacuum full/列约束DEFERRABLE以及INITIALLY DEFERRED。 - Ustore增加在线重建索引。 - Ustore增加增强版本B-tree空页面估算,提升优化器代价估算准确度。 - Ustore增加存储引擎可靠性验证框架,Diagnose Page/Page Verify。 - Ustore增强存储引擎相关的解析/检测/修复视图。 - Ustore增强基于WAL日志的定位能力,新增gs_redo_upage系统视图,支持对单页面的不断重放,获取并打印该页面的任何一个历史版本,加速页面损坏类问题的定位。 - Ustore扩展事务槽TD物理格式,为事务内空间复用做好铺垫。 - Ustore增加在线创建索引。 - Ustore适配闪回功能(for Ustore)/极致RTO。 父主题: 存储引擎更新说明
  • 设置存储引擎 存储引擎会对数据库整体效率和性能具有巨大影响,请根据实际需求选择适当的存储引擎。用户可使用WITH ( [ORIENTATION | STORAGE_TYPE] [= value] [, ... ] )为表或索引指定一个可选的存储参数。参数的详细描述如下所示: ORIENTATION STORAGE_TYPE ROW(缺省值):表的数据将以行式存储。 [USTORE(缺省值)|ASTORE|空] 如果ORIENTATION指定为ROW,且STORAGE_TYPE为空的情况下创建出的表类型取决于GUC参数enable_default_ustore_table(取值为on/off,默认情况为on):如果参数设置为on,创建出的表为Ustore类型;如果为off,创建出的表为Astore类型。 具体示例如下: gaussdb=# CREATE TABLE TEST(a int); gaussdb=# \d+ test Table "public.test" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+---------+--------------+------------- a | integer | | plain | | Has OIDs: no Options: orientation=row, compression=no, storage_type=USTORE, segment=off gaussdb=# CREATE TABLE TEST1(a int) with(orientation=row, storage_type=ustore); gaussdb=# \d+ test1 Table "public.test1" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+---------+--------------+------------- a | integer | | plain | | Has OIDs: no Options: orientation=row, storage_type=ustore, compression=no, segment=off gaussdb=# CREATE TABLE TEST2(a int) with(orientation=row, storage_type=astore); gaussdb=# \d+ test2 Table "public.test2" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+---------+--------------+------------- a | integer | | plain | | Has OIDs: no Options: orientation=row, storage_type=astore, compression=no gaussdb=# CREATE TABLE test4(a int) with(orientation=row); gaussdb=# \d+ List of relations Schema | Name | Type | Owner | Size | Storage | Description --------+-------+-------+-----------+---------+------------------------------------------------------------------+------------- public | test | table | z7ee88f3a | 0 bytes | {orientation=row,compression=no,storage_type=USTORE,segment=off} | public | test1 | table | z7ee88f3a | 0 bytes | {orientation=row,storage_type=ustore,compression=no,segment=off} | public | test2 | table | z7ee88f3a | 0 bytes | {orientation=row,storage_type=astore,compression=no} | public | test3 | table | z7ee88f3a | 16 kB | {orientation=column,storage_type=astore,compression=low} | public | test4 | table | z7ee88f3a | 0 bytes | {orientation=row,compression=no,storage_type=USTORE,segment=off} | (5 rows) gaussdb=# show enable_default_ustore_table; enable_default_ustore_table ----------------------------- on (1 row) gaussdb=# DROP TABLE test; gaussdb=# DROP TABLE test1; gaussdb=# DROP TABLE test2; gaussdb=# DROP TABLE test4; 父主题: 存储引擎体系架构
  • 通用数据库服务层 从技术角度来看,存储引擎需要一些基础架构组件,主要包括: 并发:不同存储引擎选择正确的锁可以减少开销,从而提高整体性能。此外提供多版本并发控制或“快照”读取等功能。 事务:均需满足ACID的要求,提供事务状态查询等功能。 内存缓存:不同存储引擎在访问索引和数据时一般会对其进行缓存。缓存池允许直接从内存中处理经常使用的数据,从而加快了处理速度。 检查点:不同存储引擎一般都支持增量checkpoint/double write或全量checkpoint/full page write模式。应用可以根据不同条件进行选择增量或者全量,这个对存储引擎是透明的。 日志:GaussDB采用的是物理日志,其写入/传输/回放对存储引擎透明。 父主题: 存储引擎体系架构概述
  • 静态编译架构 从整个数据库服务的组成构架来看,存储引擎向上对接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。 父主题: 存储引擎体系架构概述
  • 工具函数示例 pg_get_tabledef获取分区表的定义,入参可以为表的OID或者表名。 SELECT pg_get_tabledef('test_range_pt'); pg_get_tabledef ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SET search_path = public; + CREATE TABLE test_range_pt ( + a integer, + b integer, + c integer + ) + WITH (orientation=row, compression=no, storage_type=USTORE, segment=off) + 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 + ) + ENABLE ROW MOVEMENT; + CREATE INDEX idx_range_a ON test_range_pt USING ubtree (a) LOCAL(PARTITION p1_a_idx, PARTITION p2_a_idx, PARTITION p3_a_idx, PARTITION p4_a_idx, PARTITION p5_a_idx) WITH (storage_type=USTORE) TABLESPACE pg_default; (1 row) pg_stat_get_partition_tuples_hot_updated返回给定分区id的分区热更新元组数的统计。 在分区p1中插入10条数据并更新,统计分区p1的热更新元组数。 INSERT INTO test_range_pt VALUES(generate_series(1,10),1,1); INSERT 0 10 SELECT pg_stat_get_partition_tuples_hot_updated(49294); pg_stat_get_partition_tuples_hot_updated ------------------------------------------ 0 (1 row) UPDATE test_range_pt SET b = 2; UPDATE 10 SELECT pg_stat_get_partition_tuples_hot_updated(49294); pg_stat_get_partition_tuples_hot_updated ------------------------------------------ 10 (1 row)
  • 前置建表相关信息 前置建表: CREATE TABLE test_range_pt (a INT, b INT, c INT) PARTITION BY RANGE (a) ( PARTITION p1 VALUES LESS THAN (2000), PARTITION p2 VALUES LESS THAN (3000), partition p3 VALUES LESS THAN (4000), partition p4 VALUES LESS THAN (5000), partition p5 VALUES LESS THAN (MAXVALUE) )ENABLE ROW MOVEMENT; 查看分区表OID: SELECT oid FROM pg_class WHERE relname = 'test_range_pt'; oid ------- 49290 (1 row) 查看分区信息: SELECT oid,relname,parttype,parentid,boundaries FROM pg_partition WHERE parentid = 49290; oid | relname | parttype | parentid | boundaries -------+---------------+----------+----------+------------ 49293 | test_range_pt | r | 49290 | 49294 | p1 | p | 49290 | {2000} 49295 | p2 | p | 49290 | {3000} 49296 | p3 | p | 49290 | {4000} 49297 | p4 | p | 49290 | {5000} 49298 | p5 | p | 49290 | {NULL} (6 rows) 创建索引: CREATE INDEX idx_range_a ON test_range_pt(a) LOCAL; CREATE INDEX --查看分区索引oid SELECT oid FROM pg_class WHERE relname = 'idx_range_a'; oid ------- 90250 (1 row) 查看索引分区信息: SELECT oid,relname,parttype,parentid,boundaries,indextblid FROM pg_partition WHERE parentid = 90250; oid | relname | parttype | parentid | boundaries | indextblid -------+----------+----------+----------+------------+------------ 90255 | p5_a_idx | x | 90250 | | 49298 90254 | p4_a_idx | x | 90250 | | 49297 90253 | p3_a_idx | x | 90250 | | 49296 90252 | p2_a_idx | x | 90250 | | 49295 90251 | p1_a_idx | x | 90250 | | 49294 (5 rows)
  • Local索引分区重建/不可用 使用ALTER INDEX PARTITION可以设置Local索引分区是否可用。 使用ALTER TABLE MODIFY PARTITION可以设置分区表上指定分区的所有索引分区是否可用。 例如,假设分区表range_sales上存在两张Local索引range_sales_idx1和range_sales_idx2,假设其在分区date_202001上对应的索引分区名分别为range_sales_idx1_part1和range_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; 父主题: 分区表索引重建/不可用
  • 索引重建/不可用 使用ALTER INDEX可以设置索引是否可用。 例如,假设分区表range_ sales上存在索引range_sales_idx,可以通过如下命令设置其不可用。 ALTER INDEX range_sales_idx UNUSABLE; 可以通过如下命令重建索引range_sales_idx。 ALTER INDEX range_sales_idx REBUILD; 父主题: 分区表索引重建/不可用
  • 分区表索引重建/不可用 用户可以通过命令使得一个分区表索引或者一个索引分区不可用,此时该索引/索引分区不再维护。使用重建索引命令可以重建分区表索引,恢复索引的正常功能。 此外,部分分区级DDL操作也会使得Global索引失效,包括删除drop、交换exchange、清空truncate、分割split、合并merge。如果在DDL操作中带UPDATE GLOBAL INDEX子句,则会同步更新Global索引,否则需要用户自行重建索引。 索引重建/不可用 Local索引分区重建/不可用 父主题: 分区表运维管理
  • 分区表行迁移 用户可以使用ALTER TABLE ENABLE/DISABLE ROW MOVEMENT来开启/关闭分区表行迁移。 开启行迁移时,允许通过更新操作将一个分区中的数据迁移到另一个分区中;关闭行迁移时,如果出现这种更新行为,则业务报错。 如果业务明确不允许对分区键所在列进行更新操作,建议关闭分区表行迁移。 例如,创建列表分区表,并开启分区表行迁移,此时可以跨分区更新分区键所在列;关闭分区表行迁移后,对分区键所在列进行跨分区更新会业务报错。 CREATE TABLE list_sales ( product_id INT4 NOT NULL, customer_id INT4 PRIMARY KEY, time_id DATE, channel_id CHAR(1), type_id INT4, quantity_sold NUMERIC(3), amount_sold NUMERIC(10,2) ) PARTITION BY LIST (channel_id) ( PARTITION channel1 VALUES ('0', '1', '2'), PARTITION channel2 VALUES ('3', '4', '5'), PARTITION channel3 VALUES ('6', '7'), PARTITION channel4 VALUES ('8', '9') ) ENABLE ROW MOVEMENT; INSERT INTO list_sales VALUES (153241,65143129,'2021-05-07','0',864134,89,34); --跨分区更新成功,数据从分区channel1迁移到分区channel2 UPDATE list_sales SET channel_id = '3' WHERE channel_id = '0'; --关闭分区表行迁移 ALTER TABLE list_sales DISABLE ROW MOVEMENT; --跨分区更新失败,报错fail to update partitioned table "list_sales" UPDATE list_sales SET channel_id = '0' WHERE channel_id = '3'; --分区内更新依然成功 UPDATE list_sales SET channel_id = '4' WHERE channel_id = '3'; 父主题: 分区表运维管理
  • 对分区表重命名分区 使用ALTER TABLE RENAME PARTITION可以对分区表重命名分区。 例如,通过指定分区名将范围分区表range_sales的分区date_202001重命名。 ALTER TABLE range_sales RENAME PARTITION date_202001 TO date_202001_new; 或者,通过指定分区值将列表分区表list_sales中'0'所对应的分区重命名。 ALTER TABLE list_sales RENAME PARTITION FOR ('0') TO channel_new; 父主题: 重命名分区
  • 移动分区 用户可以使用移动分区的命令来将一个分区移动到新的表空间中。移动分区可以通过指定分区名或者分区值来进行。 使用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; 父主题: 分区表运维管理
  • 合并分区 用户可以使用合并分区的命令来将多个分区合并为一个分区。合并分区只能通过指定分区名来进行,不支持指定分区值的写法。 合并分区不能作用于哈希分区上。 执行合并分区命令会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,或者用户自行重建Global索引。 合并前的分区如果包含分类索引则不支持合并。 合并后的新分区,对于范围分区,可以与最后一个源分区名字相同,比如将p1,p2合并为p2;对于列表分区,可以与任一源分区名字相同,比如将p1,p2合并为p1。 如果新分区与源分区名字相同,数据库会将新分区视为对源分区的继承。 使用ALTER TABLE MERGE PARTITIONS可以将多个分区合并为一个分区。 例如,将范围分区表range_sales的分区date_202001和date_202002合并为一个新的分区,并更新Global索引。 ALTER TABLE range_sales MERGE PARTITIONS date_202001, date_202002 INTO PARTITION date_2020_old UPDATE GLOBAL INDEX; 父主题: 分区表运维管理
  • 对列表分区表分割分区 使用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分区范围。 父主题: 分割分区
  • 对范围分区表分割分区 使用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分区范围。 父主题: 分割分区
  • 分割分区 用户可以使用分割分区的命令来将一个分区分割为两个或多个新分区。当分区数据太大,或者需要对有MAXVALUE的范围分区/DEFAULT的列表分区新增分区时,可以考虑执行该操作。分割分区可以指定分割点将一个分区分割为两个新分区,也可以不指定分割点将一个分区分割为多个新分区。分割分区可以通过指定分区名或者分区值来进行。 分割分区不能作用于哈希分区上。 执行分割分区命令会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,或者用户自行重建Global索引。 分割的目标分区如果包含分类索引时,该分区不支持分割。 分割后的新分区,可以与源分区名字相同,比如将分区p1分割为p1,p2。但数据库不会将分割前后相同名的分区视为同一个分区。 对范围分区表分割分区 对列表分区表分割分区 父主题: 分区表运维管理
  • 清空分区 用户可以使用清空分区的命令来快速清空分区的数据。与删除分区功能类似,区别在于清空分区只会删除分区中的数据,分区的定义和物理文件都会保留。清空分区可以通过指定分区名或者分区值来进行。 执行清空分区命令会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,或者用户自行重建Global索引。 使用ALTER TABLE TRUNCATE PARTITION可以清空指定分区表的任何一个分区。 例如,通过指定分区名清空范围分区表range_sales的分区date_202005,并更新Global索引。 ALTER TABLE range_sales TRUNCATE PARTITION date_202005 UPDATE GLOBAL INDEX; 或者,通过指定分区值来清空范围分区表range_sales中'2020-05-08'所对应的分区。由于不带UPDATE GLOBAL INDEX子句,执行该命令后Global索引会失效。 ALTER TABLE range_sales TRUNCATE PARTITION FOR ('2020-05-08'); 父主题: 分区表运维管理
  • 交换分区 用户可以使用交换分区的命令来将分区与普通表的数据进行交换。交换分区可以快速将数据导入/导出分区表,实现数据高效加载的目的。在业务迁移的场景,使用交换分区比常规导入会快很多。交换分区可以通过指定分区名或者分区值来进行。 执行交换分区命令会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,或者用户自行重建Global索引。 执行交换分区时,可以声明WITH/WITHOUT VALIDATION,表明是否校验普通表数据满足目标分区的分区键约束规则(默认校验)。数据校验活动开销较大,如果能确保交换的数据属于目标分区,可以声明WITHOUT VALIDATION来提高交换性能。 可以声明WITH VALIDATION VERBOSE,此时数据库会校验普通表的每一行,将不满足目标分区的分区键约束规则的数据,插入到分区表的其他分区中,最后再进行普通表与目标分区的交换。 例如,给出如下分区定义和普通表exchange_sales的数据分布,并将分区DATE_202001和普通表exchange_sales做交换,则根据声明子句的不同,存在以下三种行为: 声明WITHOUT VALIDATION,数据全部交换到分区DATE_202001中,由于'2020-02-03', '2020-04-08'不满足分区DATE_202001的范围约束,后续业务可能会出现异常。 声明WITH VALIDATION,由于'2020-02-03', '2020-04-08'不满足分区DATE_202001的范围约束,数据库给出相应的报错。 声明WITH VALIDATION VERBOSE,数据库会将'2020-02-03'插入分区DATE_202002,将'2020-04-08'插入分区DATE_202004,再将剩下的数据交换到分区DATE_202001中。 --分区定义 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') -- exchange_sales的数据分布 ('2020-01-15', '2020-01-17', '2020-01-23', '2020-02-03', '2020-04-08') 如果交换的数据不完全属于目标分区,请不要声明WITHOUT VALIDATION交换分区,否则会破坏分区约束规则,导致分区表后续DML业务结果异常。 进行交换的普通表和分区必须满足如下条件: 普通表和分区的列数目相同,对应列的信息严格一致。 普通表和分区的表压缩信息严格一致。 普通表索引和分区Local索引个数相同,且对应索引的信息严格一致。 普通表和分区的表约束个数相同,且对应表约束的信息严格一致。 普通表不可以是临时表。 普通表和分区表上不可以有动态数据脱敏,行访问控制约束。 使用ALTER TABLE EXCHANGE PARTITION可以对分区表交换分区。 例如,通过指定分区名将范围分区表range_sales的分区date_202001和普通表exchange_sales进行交换,不进行分区键校验,并更新Global索引。 ALTER TABLE range_sales EXCHANGE PARTITION (date_202001) WITH TABLE exchange_sales WITHOUT VALIDATION UPDATE GLOBAL INDEX; 或者,通过指定分区值将范围分区表range_sales中'2020-01-08'所对应的分区和普通表exchange_sales进行交换,进行分区校验并将不满足目标分区约束的数据插入到分区表的其他分区中。由于不带UPDATE GLOBAL INDEX子句,执行该命令后Global索引会失效。 ALTER TABLE range_sales EXCHANGE PARTITION FOR ('2020-01-08') WITH TABLE exchange_sales WITH VALIDATION VERBOSE; 父主题: 分区表运维管理
  • 删除分区 用户可以使用删除分区的命令来移除不需要的分区。删除分区可以通过指定分区名或者分区值来进行。 删除分区不能作用于HASH分区上。 执行删除分区命令会使得Global索引失效,可以通过UPDATE GLOBAL INDEX子句来同步更新Global索引,或者用户自行重建Global索引。 删除分区时,如果该分区上带有仅属于当前分区的分类索引时,则会级联删除分类索引。 使用ALTER TABLE DROP PARTITION可以删除指定分区表的任何一个分区,这个行为可以作用在范围分区表、列表分区表上。 例如,通过指定分区名删除范围分区表range_sales的分区date_202005,并更新Global索引。 ALTER TABLE range_sales DROP PARTITION date_202005 UPDATE GLOBAL INDEX; 或者,通过指定分区值来删除范围分区表range_sales中'2020-05-08'所对应的分区。由于不带UPDATE GLOBAL INDEX子句,执行该命令后Global索引会失效。 ALTER TABLE range_sales DROP PARTITION FOR ('2020-05-08'); 当分区表只有一个分区时,不支持通过ALTER TABLE DROP PARTITION命令删除分区。 当分区表为哈希分区表时,不支持通过ALTER TABLE DROP PARTITION命令删除分区。 父主题: 分区表运维管理
  • 向列表分区表新增分区 使用ALTER TABLE ADD PARTITION可以在列表分区表中新增分区,新增分区的枚举值不能与已有的任一个分区的枚举值重复。 例如,对列表分区表list_sales新增一个分区。 ALTER TABLE list_sales ADD PARTITION channel5 VALUES ('X') TABLESPACE tb1; 当列表分区表有DEFAULT分区时,无法新增分区。可以使用ALTER TABLE SPLIT PARTITION命令分割分区。 父主题: 新增分区
  • 向范围分区表新增分区 使用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命令分割分区。分割分区同样适用于需要在现有分区表的前面/中间添加分区的情形,请参见对范围分区表分割分区。 父主题: 新增分区
  • 新增分区 用户可以在已建立的分区表中新增分区,来维护新业务的进行。当前各种分区表支持的分区上限为1048575个,一旦达到该上限,就无法再继续添加新分区。 同时需要考虑分区占用内存的开销,分区表使用内存大致为(分区数 * 3 / 1024)MB,分区占用内存不允许大于local_syscache_threshold的值,并且还需预留一定的内存空间,以确保其他功能能够正常运行。 新增分区不能作用于HASH分区上。 新增分区不继承表上的分类索引属性。 向范围分区表新增分区 向列表分区表新增分区 父主题: 分区表运维管理
  • 分区表运维管理 分区表运维管理包括分区管理、分区表管理、分区索引管理和分区表业务并发支持等。 分区管理:也称分区级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和partition for指定分区两种写法,前者需要指定分区名,后者需要指定分区定义范围内的任一分区值。比如假设分区part1的范围定义为[100, 200),那么partition part1和partition for(150)这两种写法是等价的。 不同分区DDL的执行代价各不相同,由于在执行分区DDL过程中目标分区会被锁住,用户需要评估其代价以及对业务的影响。一般而言,分割(Split)、合并(Merge)的执行代价远大于其他分区DDL,与源分区的大小正相关;交换(Exchange)的代价主要源于Global索引的重建和validation校验;移动(Move)的代价限制于磁盘I/O;其余分区DDL的执行代价都很低。 分区表管理:除了继承普通表的功能外,还支持开启/关闭分区表行迁移的功能。 分区索引管理:支持用户设置索引/索引分区不可用,或者重建不可用的索引/索引分区,比如由于分区管理操作导致的Global索引失效场景。 分区表业务并发支持:分布式分区表的DDL操作会锁全表,不支持跨分区DDL-DQL/DML并发。 新增分区 删除分区 交换分区 清空分区 分割分区 合并分区 移动分区 重命名分区 分区表行迁移 分区表索引重建/不可用 父主题: 分区表
  • 指定单分区统计信息收集 当前分区表支持指定单分区统计信息收集,已收集统计信息的分区会在再次收集时自动更新维护。该功能适用于列表分区、哈希分区和范围分区。 gaussdb=# CREATE TABLE only_fisrt_part(id int,name varchar)PARTITION BY RANGE (id) (PARTITION id11 VALUES LESS THAN (1000000), PARTITION id22 VALUES LESS THAN (2000000), PARTITION max_id1 VALUES LESS THAN (MAXVALUE)); gaussdb=# INSERT INTO only_fisrt_part SELECT generate_series(1,5000),'test'; gaussdb=# ANALYZE only_fisrt_part PARTITION (id11); gaussdb=# ANALYZE only_fisrt_part PARTITION (id22); gaussdb=# ANALYZE only_fisrt_part PARTITION (max_id1); gaussdb=# SELECT relname, relpages, reltuples FROM pg_partition WHERE relname IN ('id11', 'id22', 'max_id1'); relname | relpages | reltuples ---------+----------+----------- id11 | 3400 | 5000 id22 | 0 | 0 max_id1 | 0 | 0 (3 rows) gaussdb=# \x gaussdb=# SELECT * FROM pg_stats WHERE tablename ='only_fisrt_part' AND partitionname ='id11'; -[ RECORD 1 ]----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- schemaname | public tablename | only_fisrt_part attname | name inherited | f null_frac | 0 avg_width | 5 n_distinct | 1 n_dndistinct | 0 most_common_vals | {test} most_common_freqs | {1} histogram_bounds | correlation | 1 most_common_elems | most_common_elem_freqs | elem_count_histogram | partitionname | id11 subpartitionname | -[ RECORD 2 ]----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- schemaname | public tablename | only_fisrt_part attname | id inherited | f null_frac | 0 avg_width | 4 n_distinct | -1 n_dndistinct | 0 most_common_vals | most_common_freqs | histogram_bounds | {1,50,100,150,200,250,300,350,400,450,500,550,600,650,700,750,800,850,900,950,1000,1050,1100,1150,1200,1250,1300,1350,1400,1450,1500,1550,1600,1650,1700,1750,1800,1850,1900,1950,2000,2050,2100,2150,2200,2250,2300,2350,2400,2450,2500,2550,2600,2650,2700,2750,2800,2850,2900,2950,3000,3050,3100,3150,3200,3250,3300,3350,3400,3450,3500,3550,3600,3650,3700,3750,3800,3850,3900,3950,4000,4050,4100,4150,4200,4250,4300,4350,4400,4450,4500,4550,4600,4650,4700,4750,4800,4850,4900,4950,5000} correlation | 1 most_common_elems | most_common_elem_freqs | elem_count_histogram | partitionname | id11 subpartitionname | gaussdb=# q \x -- 删除分区表 gaussdb=# DROP TABLE only_fisrt_part;
  • 示例 创建分区表并插入数据 gaussdb=# CREATE TABLE t1_range_int ( c1 INT, c2 INT, c3 INT, c4 INT ) PARTITION BY RANGE(c1) ( PARTITION range_p00 VALUES LESS THAN(10), PARTITION range_p01 VALUES LESS THAN(20), PARTITION range_p02 VALUES LESS THAN(30), PARTITION range_p03 VALUES LESS THAN(40), PARTITION range_p04 VALUES LESS THAN(50) ); gaussdb=# INSERT INTO t1_range_int SELECT v,v,v,v FROM generate_series(0, 49) AS v; 级联收集统计信息 gaussdb=# ANALYZE t1_range_int WITH ALL; 查看分区级统计信息 gaussdb=# SELECT relname, parttype, relpages, reltuples FROM pg_partition WHERE parentid=(SELECT oid FROM pg_class WHERE relname='t1_range_int') ORDER BY relname; relname | parttype | relpages | reltuples --------------+----------+----------+----------- range_p00 | p | 4 | 9 range_p01 | p | 7 | 17 range_p02 | p | 6 | 13 range_p03 | p | 2 | 5 range_p04 | p | 4 | 9 t1_range_int | r | 0 | 0 (6 rows) gaussdb=# SELECT schemaname,tablename,partitionname,subpartitionname,attname,inherited,null_frac,avg_width,n_distinct,n_dndistinct,most_common_vals,most_common_freqs,histogram_bounds FROM pg_stats WHERE tablename='t1_range_int' ORDER BY tablename, partitionname, attname; schemaname | tablename | partitionname | subpartitionname | attname | inherited | null_frac | avg_width | n_distinct | n_dndistinct | most_common_vals | most_common_freqs | histogram_bounds ------------+--------------+---------------+------------------+---------+-----------+-----------+-----------+------------+--------------+------------------+-------------------+---------------------------------------------------------------------------------------------- ------------------------------------------------- public | t1_range_int | range_p00 | | c1 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9} public | t1_range_int | range_p00 | | c2 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9} public | t1_range_int | range_p00 | | c3 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9} public | t1_range_int | range_p00 | | c4 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9} public | t1_range_int | range_p01 | | c1 | f | 0 | 4 | -1 | -1 | | | {10,11,12,13,14,15,16,17,18,19} public | t1_range_int | range_p01 | | c2 | f | 0 | 4 | -1 | -1 | | | {10,11,12,13,14,15,16,17,18,19} public | t1_range_int | range_p01 | | c3 | f | 0 | 4 | -1 | -1 | | | {10,11,12,13,14,15,16,17,18,19} public | t1_range_int | range_p01 | | c4 | f | 0 | 4 | -1 | -1 | | | {10,11,12,13,14,15,16,17,18,19} public | t1_range_int | range_p02 | | c1 | f | 0 | 4 | -1 | -1 | | | {20,21,22,23,24,25,26,27,28,29} public | t1_range_int | range_p02 | | c2 | f | 0 | 4 | -1 | -1 | | | {20,21,22,23,24,25,26,27,28,29} public | t1_range_int | range_p02 | | c3 | f | 0 | 4 | -1 | -1 | | | {20,21,22,23,24,25,26,27,28,29} public | t1_range_int | range_p02 | | c4 | f | 0 | 4 | -1 | -1 | | | {20,21,22,23,24,25,26,27,28,29} public | t1_range_int | range_p03 | | c1 | f | 0 | 4 | -1 | -1 | | | {30,31,32,33,34,35,36,37,38,39} public | t1_range_int | range_p03 | | c2 | f | 0 | 4 | -1 | -1 | | | {30,31,32,33,34,35,36,37,38,39} public | t1_range_int | range_p03 | | c3 | f | 0 | 4 | -1 | -1 | | | {30,31,32,33,34,35,36,37,38,39} public | t1_range_int | range_p03 | | c4 | f | 0 | 4 | -1 | -1 | | | {30,31,32,33,34,35,36,37,38,39} public | t1_range_int | range_p04 | | c1 | f | 0 | 4 | -1 | -1 | | | {40,41,42,43,44,45,46,47,48,49} public | t1_range_int | range_p04 | | c2 | f | 0 | 4 | -1 | -1 | | | {40,41,42,43,44,45,46,47,48,49} public | t1_range_int | range_p04 | | c3 | f | 0 | 4 | -1 | -1 | | | {40,41,42,43,44,45,46,47,48,49} public | t1_range_int | range_p04 | | c4 | f | 0 | 4 | -1 | -1 | | | {40,41,42,43,44,45,46,47,48,49} public | t1_range_int | | | c1 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33, 34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49} public | t1_range_int | | | c2 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33, 34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49} public | t1_range_int | | | c3 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33, 34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49} public | t1_range_int | | | c4 | f | 0 | 4 | -1 | -1 | | | {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33, 34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49} (24 rows) 生成多列数据的分区级统计信息 gaussdb=# ALTER TABLE t1_range_int ADD STATIS TICS ((c2, c3)); gaussdb=# ANALYZE t1_range_int WITH ALL; 查看多列数据的分区级统计信息 gaussdb=# SELECT schemaname,tablename,partitionname,subpartitionname,attname,inherited,null_frac,avg_width,n_distinct,n_dndistinct,most_common_vals,most_common_freqs,histogram_bounds FROM pg_ext_stats WHERE tablename='t1_range_int' ORDER BY tablename,partitionname,attname; schemaname | tablename | partitionname | subpartitionname | attname | inherited | null_frac | avg_width | n_distinct | n_dndistinct | most_common_vals | most_common_freqs | histogram_bounds ------------+--------------+---------------+------------------+---------+-----------+-----------+-----------+------------+--------------+------------------+-------------------+------------------ public | t1_range_int | range_p00 | | 2 3 | f | 0 | 8 | -1 | -1 | | | public | t1_range_int | range_p01 | | 2 3 | f | 0 | 8 | -1 | -1 | | | public | t1_range_int | range_p02 | | 2 3 | f | 0 | 8 | -1 | -1 | | | public | t1_range_int | range_p03 | | 2 3 | f | 0 | 8 | -1 | -1 | | | public | t1_range_int | range_p04 | | 2 3 | f | 0 | 8 | -1 | -1 | | | public | t1_range_int | | | 2 3 | f | 0 | 8 | -1 | -1 | | | (6 rows) 创建表达式索引并生成对应的分区级统计信息 gaussdb=# CREATE INDEX t1_range_int_index ON t1_range_int(text(c1)) LOCAL; gaussdb=# ANALYZE t1_range_int WITH ALL; 查看表达式索引的分区级统计信息 gaussdb=# SELECT schemaname,tablename,partitionname,subpartitionname,attname,inherited,null_frac,avg_width,n_distinct,n_dndistinct,most_common_vals,most_common_freqs,histogram_bounds FROM pg_stats WHERE tablename='t1_range_int_index' ORDER BY tablename,partitionname,attname; schemaname | tablename | partitionname | subpartitionname | attname | inherited | null_frac | avg_width | n_distinct | n_dndistinct | most_common_vals | most_common_freqs | histogram_bounds ------------+--------------------+--------------------+------------------+---------+-----------+-----------+-----------+------------+--------------+------------------+-------------------+----------------------------------------------------------------------------------- ------------------------------------------------------------ public | t1_range_int_index | range_p00_text_idx | | text | f | 0 | 5 | -1 | 0 | | | {0,1,2,3,4,5,6,7,8,9} public | t1_range_int_index | range_p01_text_idx | | text | f | 0 | 6 | -1 | 0 | | | {10,11,12,13,14,15,16,17,18,19} public | t1_range_int_index | range_p02_text_idx | | text | f | 0 | 6 | -1 | 0 | | | {20,21,22,23,24,25,26,27,28,29} public | t1_range_int_index | range_p03_text_idx | | text | f | 0 | 6 | -1 | 0 | | | {30,31,32,33,34,35,36,37,38,39} public | t1_range_int_index | range_p04_text_idx | | text | f | 0 | 6 | -1 | 0 | | | {40,41,42,43,44,45,46,47,48,49} public | t1_range_int_index | | | text | f | 0 | 5 | -1 | 0 | | | {0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,3 4,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,6,7,8,9} (6 rows) 删除分区表 gaussdb=# DROP TABLE t1_range_int;
共100000条
提示

您即将访问非华为云网站,请注意账号财产安全