华为云用户手册

  • 分区表行迁移 用户可以使用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'; 父主题: 分区表运维管理
  • 分区表对导入操作的性能影响 在GaussDB Kernel内核实现中,分区表数据的插入的处理过程相比非分区表增加分区路由部分的开销,因从整体上分区表场景的数据插入开销主要看成:(1)heap-insert基表插入、(2)partition-routing分区路由两个部分,如图1所示,其中heap基表插入解决tuple入库对应heap表的问题并且该部分普通表和分区表共用,而分区路由部分解决分区路由即tuple元组插入到对应partRel的问题,并且分区路由算法本身作为一级、二级分区共用,不同之处在于二级分区相比一级分区多一层路由操作,对路由算法为两次调用。 图1 普通表&分区表数据插入 因此对数据插入优化的侧重点如下: 分区表基表Heap表插入: 算子底噪优化 heap数据插入 索引插入build优化(带索引) 分区表分区路由: 路由查找算法逻辑优化 路由底噪优化,包括分区表partRel句柄开启、新增的函数调用逻辑开销 分区路由的性能主要通过大数据量的单条INSERT语句体现,UPDATE场景内部包含了查找对应要更新的元组进行DELETE操作然后再进行INSERT,因此不如单条INSERT语句场景直接。 不同分区类型的路由算法逻辑如表1所示: 表1 路由算法逻辑 分区方式 路由算法复杂度 实现概述说明 范围分区(Range Partition) O(logN) 基于二分binary-search实现 间隔分区(Interval Partition) O(logN) 基于二分binary-search实现 哈希分区(Hash-Partition) O(1) 基于key-partOid哈希表实现 列表分区(List-Partition) O(1) 基于key-partOid哈希表实现 二级分区(List/List) O(1) + O(1) 哈希+哈希 二级分区(List/Range) O(1) + O(1) = O(1) 哈希+二分查找 二级分区(List/Hash) O(1) + O(1) = O(1) 哈希+哈希 二级分区(Range/List) O(1) + O(1) = O(1) 二分查找+哈希 二级分区(Range/Range) O(1) + O(1) = O(1) 二分查找+二分查找 二级分区(Range/Hash) O(1) + O(1) = O(1) 二分查找+哈希 二级分区(Hash/List) O(1) + O(1) = O(1) 哈希+哈希 二级分区(Hash/Range) O(1) + O(1) = O(1) 哈希+二分查找 二级分区(Hash/Hash) O(1) + O(1) = O(1) 哈希+哈希 分区路由的主要处理逻辑根据导入数据元组的分区键计算其所在分区的过程,相比非分区表这部分为额外增加的开销,这部分开销在最终数据导入上的具体性能损失和服务器CPU处理能力、表宽度、磁盘/内存的实际容量相关,通常可以粗略认为: x86服务器场景下一级分区表相比普通表的导入性能会略低10%以内,二级分区表比普通表略低20%以内。 ARM服务器场景下为20%、30%,造成x86和ARM指向性能略微差异的主要原因是分区路由为in-memory计算强化场景,主流x86体系CPU在单核指令处理能力上略优于arm。 父主题: 分区策略
  • RCR(Row Consistency Read) UB-tree多版本管理 UB-tree的多版本管理采用基于Key的多版本管理,最新版本和历史版本均在UB-tree上。 为了节省空间,xmin/xmax采用xid-base + delta的方式表示,64位的xid-base储存在页面上,元组上储存32位的delta。页面上xid-base也需要通过额外的逻辑进行维护。 UB-tree插入或者删除key时按照key + TID的顺序排列,索引列相同的元组按照对应元组的TID作为第二关键字进行排序。会将xmin、xmax追加到key的后面。 索引分裂时,多版本信息随着key的迁移而迁移。 父主题: Index
  • 新增分区 用户可以在已建立的分区表中新增分区,来维护新业务的进行。当前各种分区表支持的分区上限为1048575,如果达到了上限则不能继续添加分区。同时需要考虑分区占用内存的开销,分区表使用内存大致为(分区数 * 3 / 1024)MB,分区占用内存不允许大于local_syscache_threshold的值,同时还需要预留部分空间以供其他功能使用。 新增分区不能作用于HASH分区上。 向范围分区表新增分区 向间隔分区表新增分区 向列表分区表新增分区 向二级分区表新增一级分区 向二级分区表新增二级分区 父主题: 分区表运维管理
  • 对二级分区表移动二级分区 使用ALTER TABLE MOVE SUBPARTITION可以对二级分区表移动二级分区。 例如,通过指定分区名将二级分区表range_list_sales的分区date_202001_channel1移动到表空间tb1中。 ALTER TABLE range_list_sales MOVE SUBPARTITION date_202001_channel1 TABLESPACE tb1; 或者,通过指定分区值将二级分区表range_list_sales中('2020-01-08', '0')所对应的分区移动到表空间tb1中。 ALTER TABLE range_list_sales MOVE SUBPARTITION FOR ('2020-01-08', '0') TABLESPACE tb1; 父主题: 移动分区
  • 对一级分区表重命名分区 使用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; 父主题: 重命名分区
  • 静态编译架构 从整个数据库服务的组成构架来看,存储引擎向上对接SQL引擎,为SQL引擎提供或接收标准化的数据格式(元组或向量数组);存储引擎向下对接存储介质,按照特定的数据组织方式,以页面、压缩单元(Compress Unit)或其他形式为单位,通过存储介质提供的特定接口,对存储介质中的数据完成读、写操作。GaussDB Kernel通过静态编译使数据库专业人员可以为特定的应用程序需求选择专用的存储引擎。为了减少对执行引擎的干扰,提供行存访问接口层TableAM,用来屏蔽底层行存引擎带来的差异,使得不同行存引擎可以分别独立演进。如下图所示。 在此基础之上,存储引擎通过日志系统提供数据的持久化和可靠性能力。通过并发控制(事务)系统保证同时执行的、多个读写操作之间的原子性、一致性和隔离性,通过索引系统提供对特定数据的加速寻址和查询能力,通过主备复制系统提供整个数据库服务的高可用能力。 行存引擎主要面向OLTP(OnLine Transaction Processing)类业务应用场景,适合高并发、小数据量的单点或小范围数据读写操作。行存引擎向上为SQL引擎提供元组形式的读写接口,向下以页面为单位通过可扩展的介质管理器对存储介质进行读写操作,并通过页面粒度的共享缓冲区来优化读写操作的效率。对于读写并发操作,采用多版本并发控制(MVCC,Multi-Version Concurrency Control);对于写写并发操作,采用基于两阶段锁协议(2PL,Two-Phase Locking)的悲观并发控制(PCC,Pessimistic Concurrency Control)。当前,行存引擎默认的介质管理器采用磁盘文件系统接口,后续可扩展支持块设备等其他类型的存储介质。GaussDB Kernel行存引擎可以选择基于Append update 的Astore或基于In-place update的Ustore。 父主题: 存储引擎体系架构概述
  • 功能描述 GaussDB对数据复制能力的支持情况为: 支持通过数据迁移工具定期向异构数据库(如Oracle等)进行数据同步,不具备实时数据复制能力。不足以支撑与异构数据库间并网运行实时数据同步的诉求。 GaussDB提供了逻辑解码功能,通过反解xlog的方式生成逻辑日志。目标数据库解析逻辑日志以实时进行数据复制。具体如图1所示。逻辑复制降低了对目标数据库的形态限制,支持异构数据库、同构异形数据库对数据的同步,支持目标库进行数据同步期间的数据可读写,数据同步时延低。 图1 逻辑复制 逻辑复制由两部分组成:逻辑解码和数据复制。逻辑解码会输出以事务为单位组织的逻辑日志。业务或数据库中间件将会对逻辑日志进行解析并最终实现数据复制。GaussDB当前只提供逻辑解码功能,因此本章节只涉及逻辑解码的说明。 逻辑解码为逻辑复制提供事务解码的基础能力,GaussDB使用SQL函数接口进行逻辑解码。此方法调用方便,不需使用工具,对接外部工具接口也比较清晰,不需要额外适配。
  • 性能 在Benchmarksql-5.0的100warehouse场景下,采用pg_logical_slot_get_changes时: 单次解码数据量4K行(对应约5MB~10MB日志),解码性能0.3MB/s~0.5 MB/s。 单次解码数据量32K行(对应约40MB~80MB日志),解码性能3MB/s~5MB/s。 单次解码数据量256K行(对应约320MB~640MB日志),解码性能3MB/s~5MB/s。 单次解码数据量再增大,解码性能无明显提升。 如果采用pg_logical_slot_peek_changes + pg_replication_slot_advance方式,解码性能相比采用pg_logical_slot_get_changes时要下降30%~50%。
  • 向二级分区表新增二级分区 使用ALTER TABLE MODIFY PARTITION ADD SUBPARTITION可以在二级分区表中新增一个二级分区,这个行为可以作用在二级分区策略为RANGE或者LIST的情况。 例如,对二级分区表range_list_sales的date_202004新增一个二级分区。 ALTER TABLE range_list_sales MODIFY PARTITION date_202004 ADD SUBPARTITION date_202004_channel5 VALUES ('X') TABLESPACE tb2; 当二级分区表的二级分区策略为HASH时,不支持通过ALTER TABLE MODIFY PARTITION ADD SUBPARTITION命令新增二级分区。 父主题: 新增分区
  • UB-tree增删改查 Insert操作:UB-tree的插入逻辑基本不变,只需增加索引插入时直接获取事务信息填写xmin字段。 Delete操作:UB-tree额外增加了索引删除流程,索引删除主要步骤与插入相似,获取事务信息填写xmax字段(B-tree索引不维护版本信息,不需要删除操作),同时更新页面上的active_tuple_count,若active_tuple_count被减为0,则尝试页面回收。 Update操作:对于Ustore而言,数据更新对UB-tree索引列的操作也与Astore有所不同,数据更新包含两种情况:索引列和非索引列更新,下图给出了UB-tree在数据发生更新时的处理。 上图展示UB-tree在索引列和非索引列更新的差异: 在非索引列更新的情况下,索引不发生任何变化,index tuple仍指向第一次插入的data tuple,Uheap不会插入新的data tuple,而是修改当下data tuple并将历史数据存入Undo中。 在索引列更新的情况下,UB-tree也会插入新的index tuple,但是会指向同一个data linepointer和同一个data tuple,扫描旧版本的数据则需要从Undo中读取。 Scan操作:用户在读取数据时,可通过使用索引扫描加速,UB-tree支持索引数据的多版本管理及可见性检查,索引层的可见性检查使得索引扫描(Index Scan)及仅索引扫描(IndexOnly Scan)性能有所提升。 对于索引扫描: 若索引列包含所有扫描列(IndexOnly Scan),则通过扫描条件在索引上进行二分查找,找到符合条件元组即可返回数据。 若索引列不包含所有扫描列(Index Scan),则通过扫描条件在索引上进行二分查找,找到符合条件元组的TID,再通过TID到数据表上查找对应的数据元组。如下图所示。 父主题: Index
  • 对一级分区表移动分区 使用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; 父主题: 移动分区
  • 使用Ustore的优势 最新版本和历史版本分离存储,相比Astore扫描范围小。去除Astore的HOT chain,非索引列/索引列更新,Heap均可原位更新,ROWID可保持不变。历史版本可批量回收,对最新版本空间膨胀友好。 大并发更新同一行的场景,Ustore的原位更新机制保证了元组ROWID稳定,先到先得,更新时延相对稳定。 不依赖Vacuum进行旧版本清理。Index与Heap解耦,可独立清理,IO平稳度较好。 支持闪回功能。 不过,Ustore DML除修改数据页面,同时也需要修改Undo,更新操作开销会稍大一些。此外单条Tuple扫描开销由于需要复制(Astore返回指针)也会大一些。
  • Undo空间管理 Undo子系统依赖后台回收线程进行空闲空间回收,负责主机上Undo模块的空间回收,备机通过回放Xlog进行回收。回收线程遍历使用中的undo zone,对该zone中的txn page扫描,依据xid从小到大的顺序进行遍历。回收已提交或者已回滚完成的事务,且该事务的提交时间应早于$(current_time-undo_retention_time)。对于遍历过程中需要回滚的事务,后台回收线程会为该事务添加异步回滚任务。 当数据库中存在运行时间长、修改数据量大的事务,或者开启闪回时间较长的时候,可能出现undo空间持续膨胀的情况。当undo占用空间接近undo_space_limit_size时,就会触发强制回收。只要事务已提交或者已回滚完成,即使事务提交时间晚于$(current_time-undo_retention_time),在这种情况下也可能被回收掉。 父主题: Undo
  • 对一级分区表合并分区 使用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; 对间隔分区表的间隔分区完成合并分区操作之后,源分区之前的间隔分区会变成范围分区。 父主题: 合并分区
  • 问题分析 在开启并行回放或串行回放的情况下(查询GUC参数recovery_parse_workers和recovery_max_workers均是1为串行回放;recovery_parse_workers是1,recovery_max_workers大于1为并行回放),备机的查询线程在做索引扫描时,会先对索引页面加读锁,每当扫到一个元组时会去判可见性,如果该元组对应的事务处于committing状态,需要等待该事务提交后再判断。而备机上的事务提交是依赖日志回放线程推进的,这个过程中会对索引页面进行修改,因此需要加锁。查询线程在等待过程中会释放索引页面的锁,否则会出现查询线程等待回放线程进行事务提交,而回放线程在等待查询线程释放锁。 该报错仅出现在查询与回放都需要访问同一个索引页面的场景下,查询线程在释放锁并等待事务结束过程中,访问的页面出现被修改的情况。 备机查询在扫到committing状态的元组时,需要等待事务提交是因为事务提交的顺序与产生日志的顺序可能是乱序的,例如主机上tx_1的事务比tx_2先提交,而备机上tx_1的commit日志在tx_2的commit日志之后回放,按照事务提交顺序来看tx_1对tx_2应当是可见的,所以需要等待事务提交。 备机查询在扫描索引页面时,发现页面元组数量(包含死元组)发生变化后不可重试,是因为在扫描时可能为正向或反向扫描,而举例来说页面发生分裂后一部分元组移动到右页面,在反向扫描的情况下即使重试只能向左扫描读取,无法再保证结果的正确性,并且由于无法分辨发生分裂或者插入,所以不可重试。 图1 问题分析
  • 列表分区 列表分区(List Partition)能够通过在每个分区的描述中为分区键指定离散值列表来显式控制行如何映射到分区。列表分区的优势在于可以以枚举分区值方式对数据进行分区,可以对无序和不相关的数据集进行分组和组织。对于未定义在列表中的分区键值,可以使用默认分区(DEFAULT)来进行数据的保存,这样所有未映射到任何其他分区的行都不会生成错误。示例如下: gaussdb=# CREATE TABLE bmsql_order_line ( ol_w_id INTEGER NOT NULL, ol_d_id INTEGER NOT NULL, ol_o_id INTEGER NOT NULL, ol_number INTEGER NOT NULL, ol_i_id INTEGER NOT NULL, ol_delivery_d TIMESTAMP, ol_amount DECIMAL(6,2), ol_supply_w_id INTEGER, ol_quantity INTEGER, ol_dist_info CHAR(24) ) PARTITION BY LIST(ol_d_id) ( PARTITION p0 VALUES (1,4,7), PARTITION p1 VALUES (2,5,8), PARTITION p2 VALUES (3,6,9), PARTITION p3 VALUES (DEFAULT) ); --清理示例 gaussdb=# DROP TABLE bmsql_order_line; 上述例子和之前给出的哈希分区的例子类似,同样通过ol_d_id列进行分区,但是在List分区中直接通过对ol_d_id的可能取值范围进行限定,不在列表中的数据会进入p3分区(DEFAULT)。相比哈希分区,List列表分区对分区键的可控性更好,往往能够精准的将目标数据保存在预想的分区中,但是如果列表值较多时在分区定义时变得麻烦,该情况下推荐使用Hash哈希分区。List、Hash分区往往都是处理无序、不相关的数据集进行分组和组织。 列表分区的分区键最多支持16列。如果分区键定义为1列,子分区定义时List列表中的枚举值不允许为NULL值;如果分区键定义为多列,子分区定义时List列表中的枚举值允许有NULL值。 父主题: 分区策略
  • 场景描述 当对分区表进行全局排序时,通常SQL引擎的实现方式是先通过Partition Iterator + PartitionScan对分区表做全量扫描然后进行Sort排序操作,这样难以利用数据分区分治的算法思想进行全局排序,假如ORDER BY排序列包含本身就有序的索引,本身局部有序的前提条件则无法利用。针对这类问题,目前分区表支持了分区归并排序执行策略,利用MergeAppend的执行机制改进分区表的排序机制。
  • 注意事项及约束条件 当分区扫描路径为Index/Index Only时,才支持MergeAppend执行机制。 分区剪枝结果大于1时,才支持MergeAppend执行机制。 当分区索引全部有效且为btree索引时,才支持MergeAppend执行机制。 当SQL含有Limit子句时,才支持MergeAppend执行机制。 当分区扫描时如果存在Filter,不支持MergeAppend执行机制。 当GUC参数sql_beta_feature = 'disable_merge_append_partition'时,不再生成MergeAppend路径。
  • 通用数据库服务层 从技术角度来看,存储引擎需要一些基础架构组件,主要包括: 并发:不同存储引擎选择正确的锁可以减少开销,从而提高整体性能。此外提供多版本并发控制或“快照”读取等功能。 事务:均需满足ACID的要求,提供事务状态查询等功能。 内存缓存:不同存储引擎在访问索引和数据时一般会对其进行缓存。缓存池允许直接从内存中处理经常使用的数据,从而加快了处理速度。。 检查点:不同存储引擎一般都支持增量checkpoint/double write或全量checkpoint/full page write模式。应用可以根据不同条件进行选择增量或者全量,这个对存储引擎是透明的。 日志:GaussDB Kernel采用的是物理日志,其写入/传输/回放对存储引擎透明。 父主题: 存储引擎体系架构概述
  • 工具函数示例 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) + 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; (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)
  • 对二级分区表删除二级分区 使用ALTER TABLE DROP SUBPARTITION可以删除二级分区表的一个二级分区,这个行为可以作用在二级分区策略为RANGE或者LIST的情况。 例如,通过指定分区名删除二级分区表range_list_sales的二级分区date_202005_channel1,并更新Global索引。 ALTER TABLE range_list_sales DROP SUBPARTITION date_202005_channel1 UPDATE GLOBAL INDEX; 或者,通过指定分区值来删除二级分区表range_list_sales中('2020-05-08', '0')所对应的二级分区。由于不带UPDATE GLOBAL INDEX子句,执行该命令后Global索引会失效。 ALTER TABLE range_list_sales DROP SUBPARTITION FOR ('2020-05-08', '0'); 当二级分区表所删除的目标分区只有一个二级分区时,不支持通过ALTER TABLE DROP SUBPARTITION命令删除二级分区。 当二级分区表的二级分区策略为HASH时,不支持通过ALTER TABLE DROP SUBPARTITION命令删除二级分区。 父主题: 删除分区
  • 对二级分区表合并二级分区 使用ALTER TABLE MERGE SUBPARTITIONS可以将多个二级分区合并为一个分区。 例如,将二级分区表hash_list_sales的分区product1_channel1、product1_channel2、product1_channel3合并为一个新的分区,并更新Global索引。 ALTER TABLE hash_list_sales MERGE SUBPARTITIONS product1_channel1, product1_channel2, product1_channel3 INTO SUBPARTITION product1_channel1 UPDATE GLOBAL INDEX; 父主题: 合并分区
  • 哈希分区 哈希分区(Hash Partition)基于对分区键使用哈希算法将数据映射到分区。使用的哈希算法为GaussDB Kernel内置哈希算法,在分区键取值范围不倾斜(no data skew)场景下,哈希算法在分区之间均匀分布行,使分区大小大致相同。因此哈希分区是实现分区间均匀分布数据的理想方法。哈希分区也是范围分区的一种易于使用的替代方法,尤其是当要分区的数据不是历史数据或没有明显的分区键时,示例如下: CREATE TABLE bmsql_order_line ( ol_w_id INTEGER NOT NULL, ol_d_id INTEGER NOT NULL, ol_o_id INTEGER NOT NULL, ol_number INTEGER NOT NULL, ol_i_id INTEGER NOT NULL, ol_delivery_d TIMESTAMP, ol_amount DECIMAL(6,2), ol_supply_w_id INTEGER, ol_quantity INTEGER, ol_dist_info CHAR(24) ) --预先定义100个分区 PARTITION BY HASH(ol_d_id) ( PARTITION p0, PARTITION p1, PARTITION p2, … PARTITION p99 ); 上述例子中,bmsql_order_line表的ol_d_id进行了分区,ol_d_id列是一个identifier性质的属性列,本身并不带有时间或者某一个特定维度上的区分。使用哈希分区策略来对其进行分表处理则是一个较为理想的选择,相比其他分区类型,除了预先确保分区键没有过多数据倾斜(某一、某几个值重复度高),只需要指定分区键和分区数即可创建分区,同时还能够确保每个分区的数据均匀,提升了分区表的易用性。 父主题: 分区策略
  • 范围分区 范围分区(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) ); gaussdb=# INSERT INTO range_sales VALUES(9,5,'a'); gaussdb=# INSERT INTO range_sales VALUES(9,20,'a'); gaussdb=# INSERT INTO range_sales VALUES(9,21,'a'); gaussdb=# INSERT INTO range_sales VALUES(10,5,'a'); gaussdb=# INSERT INTO range_sales VALUES(10,15,'a'); gaussdb=# INSERT INTO range_sales VALUES(10,20,'a'); gaussdb=# INSERT INTO range_sales VALUES(10,21,'a'); gaussdb=# INSERT INTO range_sales VALUES(11,5,'a'); gaussdb=# INSERT INTO range_sales VALUES(11,20,'a'); gaussdb=# 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'; gaussdb=# CREATE TABLESPACE startend_tbs2 LOCATION '/home/omm/startend_tbs2'; gaussdb=# CREATE TABLESPACE startend_tbs3 LOCATION '/home/omm/startend_tbs3'; gaussdb=# CREATE TABLESPACE startend_tbs4 LOCATION '/home/omm/startend_tbs4'; -- 创建临时schema gaussdb=# CREATE SCHEMA tpcds; gaussdb=# SET CURRENT_SCHEMA TO tpcds; -- 创建分区表,分区键是integer类型 gaussdb=# 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; 父主题: 分区策略
  • 对一级分区表交换分区 使用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; 父主题: 交换分区
  • 注意事项及约束条件 GUC参数partition_iterator_elimination开启后,且优化器剪枝结果只有一个分区时,目标场景优化才能生效。 消除Partition Iterator算子不支持二级分区表。 支持cplan,支持部分gplan场景,如分区键a = $1(即优化器阶段可以剪枝到一个分区的场景)。 支持SeqScan、Indexscan、Indexonlyscan、Bitmapscan、RowToVec、Tidscan算子。 支持行存,astore/ustore存储引擎,支持SQLBypass。 Partition Iterator算子下层算子是支持的Scan算子时,才支持消除。
  • 场景描述 在当前分区表架构中,执行器通过Partition Iterator算子去迭代访问每一个分区。当分区剪枝结果只有一个分区时,Partition Iterator算子已经失去了迭代器的作用,在此情况下消除Partition Iterator算子,可以避免执行时一些不必要的开销。由于执行器的PIPELINE架构,Partition Iterator算子会重复执行,在数据量较大的场景下消除Partition Iterator算子的收益十分可观。
  • 外部密钥服务的身份验证 当数据库驱动访问华为云密钥管理服务时,为避免攻击者伪装为密钥服务,在数据库驱动与密钥服务建立https连接的过程中,可通过CA证书验证密钥服务器的合法性。为此,需提前配置CA证书,如果未配置,将不会验证密钥服务的身份。配置方法如下: 华为云场景下,需在环境变量中增加如下参数: export HUAWEI_KMS_INFO='其他参数, iamCaCert=路径/IAM的CA证书文件, kmsCaCert=路径/KMS的CA证书文件' 大部分浏览器均会自动下载网站对应的CA证书,并提供证书导出功能。虽然,诸如https://www.ssleye.com/ssltool/certs_down.html等很多网站也提供自动下载CA证书的功能,但可能因本地环境中存在代理或网关,导致CA证书无法正常使用。所以,建议借助浏览器下载CA证书。下载方式如下: 由于我们使用restful接口访问密钥服务,当我们在浏览器输入接口对应的url时,可忽略下述2中的失败页面,因为即使在失败的情况下,浏览器早已提前自动下载CA证书。 输入域名:打开浏览器,在华为云场景中,分别输入IAM服务的域名:iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens与KMS的域名:kms.cn-north-4.myhuaweicloud.com/v1.0。 查找证书:在每次输入域名后,找到SSL连接相关信息,单击后会发现证书,继续单击可查看证书内容。 导出证书:在证书查看页面,可能会看到证书分为很多级,我们仅需要域名的上一级证书即可,选择该证书并单击导出,便可直接生成证书文件,即我们需要的证书文件。 上传证书:将导出的证书上传至应用端,并配置到上述参数中即可。
共100000条