华为云用户手册

  • 分区策略 分区策略描述了在分区表中数据和分区路由映射规则,在建表时通过DDL语句中的PARTITION BY语法指定。 常见的分区类型有基于条件的范围分区、基于哈希散列函数的哈希分区、基于数据枚举的列表分区: CREATE TABLE table_name (…) PARTITION BY partition_strategy (partition_key) (…) 范围分区 哈希分区 列表分区 分区表对导入操作的性能影响 父主题: 分区表介绍
  • 分区(分区子表、子分区) 子分区是分区表中实际保存数据的表,对应的记录通常保存在pg_partition中,各子分区通过parentid字段作为外键,与pg_class表中分区母表对应的OID(对象标识符)列建立关联关系。示例:t1_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 ); --查询t1_hash分区类型。 gaussdb=# SELECT oid, relname, parttype FROM pg_class WHERE relname = 't1_hash'; oid | relname | parttype -------+---------+---------- 16685 | t1_hash | p (1 row) --查询t1_hash的分区信息。 gaussdb=# SELECT oid, relname, parttype, parentid FROM pg_partition WHERE parentid = 16685; oid | relname | parttype | parentid -------+---------+----------+---------- 16688 | t1_hash | r | 16685 16689 | p0 | p | 16685 16690 | p1 | p | 16685 16691 | p2 | p | 16685 16692 | p3 | p | 16685 16693 | p4 | p | 16685 16694 | p5 | p | 16685 16695 | p6 | p | 16685 16696 | p7 | p | 16685 16697 | p8 | p | 16685 16698 | p9 | p | 16685 (11 rows) --删除t1_hash gaussdb=# DROP TABLE t1_hash; 父主题: 基本概念
  • 分区表(母表) 分区表作为用户实际操作的表对象,支持常规DML(数据操作语言)的增、删、查、改操作。通常通过在建表DDL(数据定义语言)语句中显式使用PARTITION BY子句进行定义。创建成功以后在pg_class表中新增一条记录,其parttype字段值为'p'(标识一级分区)或's'(标识二级分区),以此标记该记录对应的表为分区母表。 需要注意的是,分区母表本质上是逻辑概念,其对应的物理表文件并不实际存储数据。 示例: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.) Distribute By: HASH(c1) Location Nodes: ALL DATANODES Has OIDs: no Options: orientation=row, compression=no --查询t1_hash分区类型。 gaussdb=# SELECT relname, parttype FROM pg_class WHERE relname = 't1_hash'; relname | parttype ---------+---------- t1_hash | p (1 row) --删除t1_hash。 gaussdb=# DROP TABLE t1_hash; 父主题: 基本概念
  • 分区表介绍 分区表(Partitioned Table)是指在单节点内,依据分区键及相应分区策略,对表数据进行逻辑层面的切分,本质上属于水平分区(Horizontal partition)策略。分区表增强了数据库应用程序的性能、可管理性和可用性,并有助于降低存储大量数据的总体拥有成本。通过分区,表、索引及索引组织表可被拆分为更小单元,实现数据库对象的精细化管理与访问。 GaussDB 提供了丰富的分区策略和扩展能力,以满足不同业务场景的需求。由于分区策略的实现完全由数据库内部实现,用户无感知,因此它几乎可以在实施分区表优化策略以后做平滑迁移,无需耗费人力物力的应用程序更改。本章围绕GaussDB分区表的基本概念从以下几个方面展开介绍: 分区表基本概念:从分区表的基本概念出发,介绍分区表的Catalog存储方式以及内部对应原理。 分区策略:从分区表所支持的基本类型出发,介绍各种分区模式下对应的特性以及能够达到的优化特点和效果。 基本概念 分区策略 分区基本使用 父主题: 分区表
  • 数据分区运维管理 数据生命周期管理(Data Life Cycle Management,DLM) 是一套贯穿数据全生命周期的管理流程与策略体系,其核心任务之一在于为不同阶段的数据匹配最适宜且经济高效的存储介质:高频访问的新数据存放于读写速度快、可用性高的存储层,而低频访问的旧数据则迁移至成本较低、性能稍弱的存储层。鉴于旧数据更新频率低,将其压缩并转为只读存储模式更具合理性。 分区表为DLM方案落地创造了理想条件,通过不同分区使用不同表空间,最大限度在确保易用性的同时,实现了有效的数据生命周期的成本优化。此类底层配置由数据库运维人员在服务端完成,终端用户无感知,仍可按常规逻辑对表执行查询操作。此外,各分区支持独立开展备份、恢复、索引重建等运维工作,可针对数据集的不同子集实施分治管理,满足用户业务场景的差异化需求。 父主题: 表分区介绍
  • 数据分区查找优化 分区表在数据查找方面的优势主要体现在对分区键进行谓词查询的场景。例如,考虑一张以月份(Month)作为分区键的表,如图1所示。 图1 分区表示例图 如果使用普通表设计,查询时需要进行全表扫描(Full Table Scan)。而如果以月份为分区键重新设计该表,全表扫描会被优化为分区扫描。当表内数据量很大且历史周期较长时,通过减少扫描的数据量,性能提升将非常显著,如图2所示。 图2 分区表剪枝示例图 父主题: 表分区介绍
  • 表分区技术 表分区技术(Table-Partitioning)通过将非常大的表或者索引从逻辑上切分为更小、更易管理的逻辑单元(分区),能够让用户对表查询、变更等语句操作具备更小的影响范围,能够让用户通过分区键(Partition Key)快速定位到数据所在的分区,从而避免在数据库中对大表的全量扫描,能够在不同的分区上并发进行DDL、DML操作。从用户使用的角度来看,表分区技术主要有以下能力: 提升大容量数据场景查询效率:由于表内数据按照分区键进行逻辑分区,查询时只需访问相关分区的子集,而非整个表。这种分区剪枝技术能够显著提升查询性能,提供数量级的性能增益。 降低运维与查询的并发操作影响:分区表可以显著减少DML语句和DDL语句在并发场景下的相互影响。在大数据量且按时间维度进行分区的场景下,这种优势尤为明显。例如,新数据分区的入库和实时点查操作,以及老数据分区的数据清洗和分区合并等运维操作,可以独立进行,互不干扰。 提供大容量场景下灵活的数据运维管理方式:分区表通过物理上对不同分区的数据进行隔离,每个分区可以独立设置物理属性,如启用或禁用压缩、物理存储设置和表空间。此外,分区表支持分区级别的数据管理操作,如数据加载、索引创建和重建,以及备份和恢复,无需对整个表进行操作,从而大大减少了操作时间。 父主题: 表分区介绍
  • 大容量数据库背景介绍 随着处理数据量的日益增长和使用场景的多样化,数据库越来越多地面对容量大、数据多样化的场景。在过去数据库业界发展的20多年时间里,数据量从最初的MB、GB级数据量逐渐发展到现在的TB级数据量,在如此数据大规模、数据多样化的客观背景下,数据库管理系统(DBMS)在数据查询、数据管理方面提出了更高的要求,客观上要求数据库能够支持多种优化查找策略和管理运维方式。 在计算机科学经典的算法中,人们通常使用分治法(Divide and Conquer)解决场景和规模较大的问题。其基本思想就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题直到最后子问题可以简单的直接求解,原问题的解可看成子问题的解的合并。对于大容量数据场景,数据库提供对数据进行“分治处理”的方式即分区,将逻辑数据库或其组成元素划分为不同的独立部分,每一个分区维护逻辑上存在相类似属性的数据,这样就把庞大的数据整体进行了切分,有利于数据的管理、查找和维护。 父主题: 表分区介绍
  • 分布式强一致解码 logical-receiver-num: 仅流式解码设置有效,分布式解码启动的logical_receiver数量,系统函数调用场景下此选项无效,仅校验取值范围。 取值范围:1~20的int型,默认值为1。当该值被设置为比当前集群分片数更大时,将被修改为分片数。 slice-id: 仅连接DN解码时设置,指定当前DN所在的分片号,用于复制表解码。 取值范围:0~8192的int型,默认值为-1,即不指定分片号,但在解码到复制表时会报错。 该配置选项在尝试连接DN使用 CS N序逻辑复制槽(confirmed_csn为非0值的复制槽)进行解码时使用,用来表示自己的分片号(即第几个分片,第一个分片则输入0),如果不设置该参数(即使用默认值-1)在解码到复制表时将会报错。连接CN解码时,不支持指定该参数,程序内部会得出DN分片号,CN只会收集该DN分片的复制表解码结果。 start-position: 仅连接DN设置,主要功能为过滤掉小于指定CSN对应的事务,以及针对指定的CSN对应的事务,过滤掉小于指定LSN的日志,且指定CSN对应事务的BEGIN日志一定被过滤掉。 取值范围:字符串类型,可以解析为以'/'分隔,左右两侧分别为代表CSN和LSN的两个uint64类型。 连接CN解码时,不支持指定该参数,程序内部会使用该选项,用于CN建立与DN的连接后发送解码请求时过滤可能已经被接收过的日志。
  • 串行解码 force-binary: 是否以二进制格式输出解码结果,针对不同场景呈现不同行为。 针对系统函数pg_logical_slot_get_binary_changes和pg_logical_slot_peek_binary_changes: 取值范围:boolean型,默认值为false。此值无实际意义,均以二进制格式输出解码结果。 针对系统函数pg_logical_slot_get_changes、pg_logical_slot_peek_changes和pg_logical_get_area_changes: 取值范围:仅取false值的boolean型。以文本格式输出解码结果。 针对流式解码(仅连接DN时支持): 取值范围:boolean型,默认值为false。此值无实际意义,均以文本格式输出解码结果。
  • 并行解码 以下配置选项仅限流式解码设置。 decode-style: 当enable-ddl-json-format参数值为true时,DDL的格式由enable-ddl-json-format控制,decode-style仅指定DML语句的解码格式;当enable-ddl-json-format参数值为false时,decode-style指定DML和DDL语句的解码格式。 取值范围:char型的字符'j'、't'或'b',分别代表JSON格式、TEXT格式及二进制格式。 默认值: 没有指定decode-style: 针对复制槽插件类型为mppdb_decoding、sql_decoding,decode-style默认值为'b'即二进制格式解码。针对复制槽插件类型为parallel_binary_decoding、parallel_json_decoding、parallel_text_decoding,decode-style默认值分别为'b'、'j'、't',解码格式分别为二进制格式、JSON格式、TEXT格式。 指定decode-style: 按照指定的decode-style进行解码。 对于JSON格式和TEXT格式解码,开启批量发送选项时的解码结果中,每条解码语句的前4字节组成的uint32代表该条语句总字节数(不包含该uint32类型占用的4字节,0代表本批次解码结束),8字节uint64代表相应lsn(begin对应first_lsn,commit对应end_lsn,其他场景对应该条语句的lsn)。 例如:以mppdb_decoding插件为例,当decode-style为b类型时,以二进制格式解码,结果如下: current_lsn: 0/CFE5C80 BEGIN CSN: 2357 first_lsn: 0/CFE5C80 current_lsn: 0/CFE5D40 INSERT INTO public.test1 new_tuple: {a[typid = 23]: "1", b[typid = 23]: "2"} current_lsn: 0/CFE5E68 COMMIT xid: 78108 当decode-style为j类型时,以JSON格式解码,结果如下: BEGIN CSN: 2358 first_lsn: 0/CFE6220 {"table_name":"public.test1","op_type":"INSERT","columns_name":["a","b"],"columns_type":["integer","integer"],"columns_val":["3","3"],"old_keys_name":[],"old_keys_type":[],"old_keys_val":[]} COMMIT XID: 78109 当decode-style为t类型时,以TEXT格式解码,结果如下: BEGIN CSN: 2359 first_lsn: 0/CFE64D0 table public test1 INSERT: a[integer]:3 b[integer]:4 COMMIT XID: 78110 二进制格式编码规则如下所示: 前4字节代表接下来到语句级别分隔符字母P(不含)或者该批次结束符F(不含)的解码结果的总字节数,该值如果为0代表本批次解码结束。 接下来8字节uint64代表相应lsn(begin对应first_lsn,commit对应end_lsn,其他场景对应该条语句的lsn)。 接下来1字节的字母有5种B/C/I/U/D,分别代表begin/commit/insert/update/delete。 第3步字母为B时: 接下来的8字节uint64代表CSN。 接下来的8字节uint64代表first_lsn。 【该部分为可选项】接下来的1字节字母如果为T,则代表后面4字节uint32表示该事务commit时间戳长度,再后面等同于该长度的字符为时间戳字符串。 【该部分为可选项】接下来的1字节字母如果为N,则代表后面4字节uint32表示该事务用户名的长度,再后面等同于该长度的字符为事务的用户名字。 因为之后仍可能有解码语句,接下来会有1字节字母P或F作为语句间的分隔符,P代表本批次仍有解码的语句,F代表本批次解码完成。 第3步字母为C时: 【该部分为可选项】接下来1字节字母如果为X,则代表后面的8字节uint64表示xid。 【该部分为可选项】接下来的1字节字母如果为T,则代表后面4字节uint32表示时间戳长度,再后面等同于该长度的字符为时间戳字符串。 因为批量发送日志时,一个COMMIT日志解码之后可能仍有其他事务的解码结果,接下来的1字节字母如果为P则表示该批次仍需解码,如果为F则表示该批次解码结束。 第3步字母为I/U/D时: 接下来的2字节uint16代表Schema名的长度。 按照上述长度读取Schema名。 接下来的2字节uint16代表table名的长度。 按照上述长度读取table名。 【该部分为可选项】接下来1字节字母如果为N代表为新元组,如果为O代表为旧元组,这里先发送新元组。 接下来的2字节uint16代表该元组需要解码的列数,记为attrnum。 以下流程重复attrnum次。 接下来2字节uint16代表列名的长度。 按照上述长度读取列名。 接下来4字节uint32代表当前列类型的OID。 接下来4字节uint32代表当前列值(以字符串格式存储)的长度,如果为0xFFFFFFFF则表示NULL,如果为0则表示长度为0的字符串。 按照上述长度读取列值。 因为之后仍可能有解码语句,接下来的1字节字母如果为P则表示该批次仍需解码,如果为F则表示该批次解码结束。 sending-batch: 指定是否批量发送。 取值范围:0或1的int型,默认值为0。 0:设为0时,表示逐条发送解码结果。 1:设为1时,表示解码结果累积到达1MB则批量发送解码结果。 开启批量发送的场景中,当解码格式为'j'或't'时,在原来的每条解码语句之前会附加一个uint32类型,表示本条解码结果长度(长度不包含当前的uint32类型),以及一个uint64类型,表示当前解码结果对应的lsn。 在CSN序解码(即output-order设置为1)场景下,批量发送仅限于单个事务内(即如果一个事务有多条较小的语句会采用批量发送),即不会使用批量发送功能在同一批次里发送多个事务,且BEGIN和COMMIT语句不会批量发送。 parallel-queue-size: 指定并行逻辑解码线程间进行交互的队列长度。 取值范围:2~1024的int型,且必须为2的整数幂,默认值为128。 队列长度和解码过程的内存使用量正相关。
  • SQL函数解码性能 在Benchmarksql-5.0的100warehouse场景下,采用pg_logical_slot_get_changes时: 单次解码数据量4K行(对应约5MB~10MB日志),解码性能0.3MB/s~0.5MB/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%。 在Benchmarksql-5.0的100warehouse场景下,采用pg_logical_get_area_changes时: 单次解码数据量4K行(对应约5MB~10MB日志),解码性能0.3MB/s~0.5MB/s。 单次解码数据量32K行(对应约40MB~80MB日志),解码性能3MB/s~5MB/s。 单次解码数据量256K行(对应约320MB~640MB日志),解码性能3MB/s~5MB/s。 单次解码数据量再增大,解码性能无明显提升。 在Benchmarksql-5.0的100warehouse场景下,采用pg_logical_slot_get_binary_changes时: 单次解码数据量4K行(对应约5MB~10MB日志),解码性能0.3MB/s~0.5MB/s。 单次解码数据量32K行(对应约40MB~80MB日志),解码性能2MB/s~3MB/s。 单次解码数据量256K行(对应约320MB~640MB日志),解码性能2MB/s~3MB/s。 单次解码数据量再增大,解码性能无明显提升。 如果采用pg_logical_slot_peek_binary_changes + pg_replication_slot_advance方式,解码性能相比采用pg_logical_slot_get_binary_changes时要下降30%~50%。
  • 逻辑复制 逻辑复制分为逻辑解码与数据复制两部分。逻辑解码提取事务级逻辑日志,由业务或数据库中间件解析后完成数据复制。 GaussDB数据库 支持通过数据迁移工具定期向异构数据库同步数据,不具备实时数据复制能力,不足以支撑与异构数据库间并网运行实时数据同步的诉求。因此,GaussDB数据库提供逻辑解码功能,通过解析Xlog生成逻辑日志,供目标数据库实时解析实现数据复制。降低对目标数据库的形态限制,支持异构数据库、同构异形数据库对数据的同步;支持目标数据库进行数据同步期间的数据可读写,实现数据同步低时延。实现逻辑如图1所示,本章节仅介绍逻辑解码。 图1 逻辑复制 逻辑解码
  • 恢复用户表和用户历史表名称 已通过enable_recyclebin参数和recyclebin_retention_time参数开启闪回DROP功能,恢复用户表和用户历史表名称。示例如下: DROP用户表,对用户表执行闪回DROP。使用ledger_hist_repair对用户表、用户历史表进行表名恢复。 -- 对用户表执行闪回drop,使用ledger_hist_repair对用户历史表进行表名恢复。 gaussdb=# CREATE TABLE ledgernsp.tab2(a int, b text); 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. NOTICE: The 'DISTRIBUTE BY' clause is not specified. Using 'rec_num' as the distribution column by default. HINT: Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column. CREATE TABLE gaussdb=# DROP TABLE ledgernsp.tab2; DROP TABLE gaussdb=# SELECT rcyrelid, rcyname, rcyoriginname FROM gs_recyclebin; rcyrelid | rcyname | rcyoriginname ----------+------------------------------+--------------------- 32838 | BIN$39B523388046$55C8400==$0 | tab2 32846 | BIN$39B52338804E$55C90E8==$0 | gs_hist_tab2_index 32843 | BIN$39B52338804B$55C96A0==$0 | ledgernsp_tab2_hist 32841 | BIN$39B523388049$55C9EE0==$0 | pg_toast_32838 (4 rows) -- 对用户表执行闪回drop。 gaussdb=# TIMECAPSULE TABLE ledgernsp.tab2 TO BEFORE DROP; TimeCapsule Table -- 使用ledger_hist_repair恢复用户历史表表名。 gaussdb=# SELECT ledger_hist_repair('ledgernsp', 'tab2'); ledger_hist_repair -------------------- 0000000000000000 (1 row) gaussdb=# \d+ ledgernsp.tab2; Table "ledgernsp.tab2" Column | Type | Modifiers | Storage | Stats target | Description -------------+---------+-----------+----------+--------------+------------- a | integer | | plain | | b | text | | extended | | hash_1d2d14 | hash16 | | plain | | Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=enhanced_toast History table name: ledgernsp_tab2_hist -- 对用户表执行闪回drop,使用ledger_hist_repair对用户表进行表名恢复。 gaussdb=# CREATE TABLE ledgernsp.tab3(a int, b text); 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. NOTICE: The 'DISTRIBUTE BY' clause is not specified. Using 'rec_num' as the distribution column by default. HINT: Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column. CREATE TABLE gaussdb=# DROP TABLE ledgernsp.tab3; DROP TABLE gaussdb=# SELECT rcyrelid, rcyname, rcyoriginname FROM gs_recyclebin; rcyrelid | rcyname | rcyoriginname ----------+------------------------------+--------------------- 32952 | BIN$80B6233880B8$FECFF98==$0 | tab3 32960 | BIN$80B6233880C0$FED0C98==$0 | gs_hist_tab3_index 32957 | BIN$80B6233880BD$FED1250==$0 | ledgernsp_tab3_hist 32955 | BIN$80B6233880BB$FED1A00==$0 | pg_toast_32952 (4 rows) -- 对用户历史表执行闪回drop。 gaussdb=# TIMECAPSULE TABLE blockchain.ledgernsp_tab3_hist TO BEFORE DROP; TimeCapsule Table -- 拿到回收站中用户表对应的rcyname,使用ledger_hist_repair恢复用户表表名。 gaussdb=# SELECT ledger_hist_repair('ledgernsp', 'BIN$80B6233880B8$FECFF98==$0'); ledger_hist_repair -------------------- 0000000000000000 (1 row) gaussdb=# \d+ ledgernsp.tab3; Table "ledgernsp.tab3" Column | Type | Modifiers | Storage | Stats target | Description -------------+---------+-----------+----------+--------------+------------- a | integer | | plain | | b | text | | extended | | hash_7a0c87 | hash16 | | plain | | Has OIDs: no Distribute By: HASH(a) Location Nodes: ALL DATANODES Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=enhanced_toast History table name: ledgernsp_tab3_hist -- 删除表。 gaussdb=# DROP TABLE ledgernsp.tab2 PURGE; DROP TABLE gaussdb=# DROP TABLE ledgernsp.tab3 PURGE; DROP TABLE
  • 背景信息 当前的账本数据库机制为:全局区块表存储在CN端,各个CN数据独立。用户历史表存储在DN端,历史表记录的数据为所在DN防篡改表的数据变化。因此,在触发数据重分布时,可能导致防篡改表和用户历史表数据不一致,此时需要使用ledger_hist_repair(text, text)接口对指定DN节点的用户历史表进行修复,修复后当前DN节点调用历史表校验接口结果为true。在CN剔除、修复的场景下,可能导致全局区块表数据丢失或者与用户历史表不一致,此时需要使用ledger_gchain_repair(text, text)接口对整个集群范围内的全局区块表进行修复,修复后调用全局区块表校验接口结果为true。 修复用户历史表的接口为pg_catalog.ledger_hist_repair,操作为: SELECT pg_catalog.ledger_hist_repair(schema_name text,table_name text); 如果修复成功,函数返回修复过程中用户历史表hash的增量。 注:对用户表执行闪回DROP时,可使用该函数恢复用户表和用户历史表名称,请参见恢复用户表和用户历史表名称。 修复全局区块表的接口为pg_catalog.ledger_gchain_repair,操作为: SELECT pg_catalog.ledger_gchain_repair(schema_name text,table_name text); 如果修复成功,函数返回修复过程中全局区块表中指定表的hash总和。
  • 恢复用户表数据和全局区块表数据 以omm用户为例进行操作,步骤如下。 以操作系统用户omm登录数据库主节点。 使用EXECUTE DIRECT对某个DN节点进行历史表修复操作。 1 gaussdb=# EXECUTE DIRECT ON (datanode1) 'select pg_catalog.ledger_hist_repair(''ledgernsp'', ''usertable'');'; 查询结果如下: ledger_hist_repair -------------------- 84e8bfc3b974e9cf (1 row) 该结果表明datanode1节点用户历史表修复成功,修复造成的用户历史表hash增量为84e8bfc3b974e9cf。 连接CN执行全局区块表修复操作。 1 gaussdb=# SELECT pg_catalog.ledger_gchain_repair('ledgernsp', 'usertable'); 查询结果如下: ledger_gchain_repair ---------------------- a41714001181a294 (1 row) 该结果表明,当前集群全局区块表修复成功,且向当前CN节点插入一条修复数据,其hash值为a41714001181a294。
  • 背景信息 账本数据库归档功能目前提供两种校验接口,分别为:ledger_hist_archive(text, text)和ledger_gchain_archive(text, text)。账本数据库接口仅审计管理员可以调用。 归档用户历史表的接口为pg_catalog.ledger_hist_archive,表示归档当前DN的用户历史表数据。执行操作为: SELECT pg_catalog.ledger_hist_archive(schema_name text,table_name text); 如果归档成功,函数返回t,反之则提示失败原因并返回f。 归档全局区块表的接口为pg_catalog.ledger_gchain_archive,表示归档当前CN的全局历史表数据。执行操作为: SELECT pg_catalog.ledger_gchain_archive(); 如果归档成功,函数返回t,反之则提示失败原因并返回f。
  • 操作步骤 使用EXECUTE DIRECT对某个DN节点进行归档操作。 1 gaussdb=# EXECUTE DIRECT ON (datanode1) 'select pg_catalog.ledger_hist_archive(''ledgernsp'', ''usertable'');'; 查询结果如下: ledger_hist_archive --------------------- t (1 row) 用户历史表将归档为一条数据: gaussdb=# EXECUTE DIRECT ON (datanode1) 'SELECT * FROM blockchain.ledgernsp_usertable_hist;'; rec_num | hash_ins | hash_del | pre_hash ---------+------------------+------------------+---------------------------------- 3 | e78e75b00d396899 | 8fcd74a8a6a4b484 | fd61cb772033da297d10c4e658e898d7 (1 row) 该结果表明datanode1节点用户历史表导出成功。 连接CN执行全局区块表导出操作。 1 gaussdb=# SELECT pg_catalog.ledger_gchain_archive(); 查询结果如下: ledger_gchain_archive ----------------------- t (1 row) 全局历史表将以用户表为单位归档为N(用户表数量)条数据: gaussdb=# SELECT * FROM gs_global_chain; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+----------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+----------- 1 | testdb | libc | 2021-05-10 19:59:38.619472+08 | 16388 | ledgernsp | usertable | 57c101076694b415 | be82f98ee68b2bc4e375f69209345406 | Archived. (1 row) 该结果表明,当前coordinator节点全局区块表导出成功。
  • 操作步骤 校验防篡改用户表ledgernsp.usertable与其对应的历史表是否一致。 1 gaussdb=# SELECT pg_catalog.ledger_hist_check('ledgernsp', 'usertable'); 查询结果如下: ledger_hist_check ------------------- t (1 row) 该结果表明防篡改用户表和用户历史表中记录的结果能够一一对应,保持一致。 查询防篡改用户表ledgernsp.usertable与其对应的历史表以及全局区块表中关于该表的记录是否一致。 1 gaussdb=# SELECT pg_catalog.ledger_gchain_check('ledgernsp', 'usertable'); 查询结果如下: ledger_gchain_check --------------------- t (1 row) 查询结果显示,上述三表中关于ledgernsp.usertable的记录保持一致,未发生篡改行为。
  • 背景信息 账本数据库校验功能目前提供两种校验接口,分别为:ledger_hist_check(text, text)和ledger_gchain_check(text, text)。普通用户调用校验接口,仅能校验自己有权限访问的表。 校验防篡改用户表和用户历史表的接口为pg_catalog.ledger_hist_check,操作为: SELECT pg_catalog.ledger_hist_check(schema_name text,table_name text); 如果校验通过,函数返回t,反之则提示失败原因并返回f。 校验防篡改用户表、用户历史表和全局区块表三者是否一致的接口为pg_catalog.ledger_gchain_check,操作为: SELECT pg_catalog.ledger_gchain_check(schema_name text, table_name text); 如果校验通过,函数返回t,反之则提示失败原因并返回f。
  • 操作步骤 查询全局区块表记录。 1 gaussdb=# SELECT * FROM gs_global_chain; 查询结果如下: blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+----------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+------------------ ------------------------------------------------------------ 0 | testdb | omm | 2021-04-14 07:00:46.32757+08 | 16393 | ledgernsp | usertable | a41714001181a294 | 6b5624e039e8aee36bff3e8295c75b40 | insert into ledge rnsp.usertable values(1, 'alex'), (2, 'bob'), (3, 'peter'); 1 | testdb | omm | 2021-04-14 07:01:19.767799+08 | 16393 | ledgernsp | usertable | b3a9ed0755131181 | 328b48c4370faed930937869783c23e0 | update ledgernsp. usertable set name = 'bob2' where id = 2; 2 | testdb | omm | 2021-04-14 07:01:29.896148+08 | 16393 | ledgernsp | usertable | 0ae4b4e4ed2fcab5 | aa8f0a236357cac4e5bc1648a739f2ef | delete from ledge rnsp.usertable where id = 3; 该结果表明,用户omm连续执行了三条DML命令,包括INSERT、UPDATE和DELETE操作。 查询历史表记录。 1 gaussdb=# SELECT * FROM blockchain.ledgernsp_usertable_hist; 查询结果如下: rec_num | hash_ins | hash_del | pre_hash ---------+------------------+------------------+---------------------------------- 0 | 1f2e543c580cb8c5 | | e1b664970d925d09caa295abd38d9b35 1 | 8fcd74a8a6a4b484 | | dad3ed8939a141bf3682043891776b67 2 | f51b4b1b12d0354b | | 53eb887fc7c4302402343c8914e43c69 3 | 437761affbb7c605 | 8fcd74a8a6a4b484 | c2868c5b49550801d0dbbbaa77a83a10 4 | | f51b4b1b12d0354b | 9c512619f6ffef38c098477933499fe3 (5 rows) 查询结果显示,用户omm对ledgernsp.usertable表插入了3条数据,更新了1条数据,随后删除了1行数据,最后剩余2行数据,hash值分别为1f2e543c580cb8c5和437761affbb7c605。 查询用户表数据及校验列。 1 gaussdb=# SELECT *, hash_69dd43 FROM ledgernsp.usertable; 查询结果如下: id | name | hash_69dd43 ----+------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob2 | 437761affbb7c605 (2 rows) 查询结果显示,用户表中剩余2条数据,与2中的记录一致。
  • 背景信息 账本数据库融合了 区块链 思想,将用户操作记录至两种历史表:用户历史表和全局区块表中。当用户创建防篡改用户表时,系统将自动为该表添加一个hash列来保存每行数据的hash摘要信息,同时在blockchain模式下会创建一张用户历史表来记录对应用户表中每条数据的变更行为;而用户对防篡改用户表的每一次修改行为将被记录到全局区块表中。由于历史表具有只可追加不可修改的特点,因此历史表记录串联起来便形成了用户对防篡改用户表的修改历史。
  • 操作步骤 创建防篡改模式。 例如,创建防篡改模式ledgernsp。 1 gaussdb=# CREATE SCHEMA ledgernsp WITH BLOCKCHAIN; 如果需要创建防篡改模式或更改普通模式为防篡改模式,则需设置enable_ledger参数为on。enable_ledger默认参数为off。 在防篡改模式下创建防篡改用户表。 例如,创建防篡改用户表ledgernsp.usertable。 gaussdb=# CREATE TABLE ledgernsp.usertable(id int, name text); 查看防篡改用户表结构及其对应的用户历史表结构。 gaussdb=# \d+ ledgernsp.usertable; gaussdb=# \d+ blockchain.ledgernsp_usertable_hist; 执行结果如下: gaussdb=# \d+ ledgernsp.usertable; Table "ledgernsp.usertable" Column | Type | Modifiers | Storage | Stats target | Description --------+---------+-----------+----------+--------------+------------- id | integer | | plain | | name | text | | extended | | hash_69dd43 | hash16 | | plain | | Has OIDs: no Distribute By: HASH(id) Location Nodes: ALL DATANODES Options: orientation=row, compression=no History table name: ledgernsp_usertable_hist gaussdb=# \d+ blockchain.ledgernsp_usertable_hist; Table "blockchain.ledgernsp_usertable_hist" Column | Type | Modifiers | Storage | Stats target | Description ----------+--------+-----------+---------+--------------+------------- rec_num | bigint | | plain | | hash_ins | hash16 | | plain | | hash_del | hash16 | | plain | | pre_hash | hash32 | | plain | | Indexes: "gs_hist_69dd43_index" PRIMARY KEY, btree (rec_num int4_ops) TABLESPACE pg_default Has OIDs: no Distribute By: HASH(rec_num) Location Nodes: ALL DATANODES Options: internal_mask=263 防篡改模式下仅行存表为防篡改表,临时表、外表、unlog表及非行存表均无防篡改属性。 防篡改表在创建时会自动增加一个用于校验的系统列,所以防篡改表单表最大列数为1599。 修改防篡改用户表数据。 例如,对防篡改用户表执行INSERT、UPDATE、DELETE操作。 gaussdb=# INSERT INTO ledgernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter'); INSERT 0 3 gaussdb=# SELECT *, hash_69dd43 FROM ledgernsp.usertable ORDER BY id; id | name | hash_69dd43 ----+-------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob | 8fcd74a8a6a4b484 3 | peter | f51b4b1b12d0354b (3 rows) gaussdb=# UPDATE ledgernsp.usertable SET name = 'bob2' WHERE id = 2; UPDATE 1 gaussdb=# SELECT *, hash_69dd43 FROM ledgernsp.usertable ORDER BY id; id | name | hash_69dd43 ----+-------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob2 | 437761affbb7c605 3 | peter | f51b4b1b12d0354b (3 rows) gaussdb=# DELETE FROM ledgernsp.usertable WHERE id = 3; DELETE 1 gaussdb=# SELECT *, hash_69dd43 FROM ledgernsp.usertable ORDER BY id; id | name | hash_69dd43 ----+------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob2 | 437761affbb7c605 (2 rows) 删除表和模式。 若要执行其他账本数据库章节的示例,请在执行完之后再执行当前步骤,否则请直接执行当前步骤。 gaussdb=# DROP TABLE ledgernsp.usertable; DROP TABLE gaussdb=# DROP SCHEMA ledgernsp; DROP SCHEMA
  • 前向兼容 在上文中,支持通过key_info设置访问外部密钥管理的参数: 使用gsql时,通过元命令\key_info xxx设置。 使用JDBC时,通过连接参数conn.setProperty(“key_info”, “xxx”)设置。 为保持前向兼容,还支持通过环境变量等方式设置访问主密钥的参数。 第一次配置使用密态数据库时,可忽略下述方法。如果以前使用下述方法配置密态数据库,建议改用’key_info’配置。 使用系统级环境变量配置的方式如下: export HUAWEI_KMS_INFO='iamUrl=https://iam.{项目}.myhuaweicloud.com/v3/auth/tokens,iamUser={ IAM 用户名},iamPassword={IAM用户密码},iamDomain={账号名},kmsProject={项目}' # 该方法中操作系统日志可能会记录环境变量中的敏感信息,使用过程中注意及时清理。 还可通过标准库接口设置进程级环境变量,不同语言设置方法如下: C/C++ setenv("HIS_KMS_INFO", "xxx"); GO os.Setenv("HIS_KMS_INFO", "xxx");
  • 外部密钥服务的身份验证 当数据库驱动访问华为云密钥管理服务时,为避免攻击者伪装为密钥服务,在数据库驱动与密钥服务建立https连接的过程中,可通过CA证书验证密钥服务器的合法性。为此,需提前配置CA证书,如果未配置,将不会验证密钥服务的身份。本节介绍如何下载与配置CA证书。 配置方法 在key_info参数的中,增加证书相关参数即可。 使用gsql时 gaussdb=# \key_info keyType=huawei_kms,iamUrl=https://iam.example.com/v3/auth/tokens,iamUser={IAM用户名},iamPassword={IAM用户密码},iamDomain={账号名},kmsProject={项目},iamCaCert=/路径/IAM的CA证书文件,kmsCaCert=/路径/KMS的CA证书文件 gaussdb=# \key_info keyType=huawei_kms,kmsProjectId={项目ID},ak={AK},sk={SK},kmsCaCert=/路径/KMS的CA证书文件 使用JDBC时 conn.setProperty("key_info", "keyType=huawei_kms," + "iamUrl=https://iam.{example.com/v3/auth/tokens," + "iamUser={IAM用户名}," + "iamPassword={IAM用户密码}," + "iamDomain={账号名}," + "kmsProject={项目}," + "iamCaCert=/路径/IAM的CA证书文件," + "kmsCaCert=/路径/KMS的CA证书文件"); conn.setProperty("key_info", "keyType=huawei_kms, kmsProjectId={项目ID}, ak={AK}, sk={SK}, kmsCaCert=/路径/KMS的CA证书文件");
  • 执行密态等值密文解密 数据库连接接口PgConnection类型新增解密接口,可以对全密态数据库的密态等值密文进行解密。解密后返回其明文值,通过schema.table.column找到解文对应的密文列并返回其原始数据类型。 表1 新增com.huawei.gaussdb.jdbc.jdbc.PgConnection函数接口 方法名 返回值类型 支持JDBC 4 decryptData(String ciphertext, Integer len, String schema, String table, String column) ClientLogicDecryptResult Yes 参数说明: ciphertext 需要解密的密文。 len 密文长度。当取值小于实际密文长度时,解密失败。 schema 加密列所属schema名称。 table 加密列所属table名称。 column 加密列所属column名称。 下列场景可以解密成功,但不推荐: 密文长度入参比实际密文长。 schema.table.column指向其他加密列,此时将返回被指向的加密列的原始数据类型。 表2 新增com.huawei.gaussdb.jdbc.jdbc.clientlogic.ClientLogicDecryptResult函数接口 方法名 返回值类型 描述 支持JDBC4 isFailed() Boolean 解密是否失败,若失败返回True,否则返回False。 Yes getErrMsg() String 获取错误信息。 Yes getPlaintext() String 获取解密后的明文。 Yes getPlaintextSize() Integer 获取解密后的明文长度。 Yes getOriginalType() String 获取加密列的原始数据类型。 Yes // 通过非密态连接、逻辑解码等其他方式获得密文后,可使用该接口对密文进行解密 import com.huawei.gaussdb.jdbc.jdbc.PgConnection; import com.huawei.gaussdb.jdbc.jdbc.clientlogic.ClientLogicDecryptResult; // conn为密态连接 // 调用密态PgConnection的decryptData方法对密文进行解密,通过列名称定位到该密文的所属加密列,并返回其原始数据类型 ClientLogicDecryptResult decrypt_res = null; decrypt_res = ((PgConnection)conn).decryptData(ciphertext, ciphertext.length(), schemaname_str, tablename_str, colname_str); // 检查返回结果类解密成功与否,失败可获取报错信息,成功可获得明文及长度和原始数据类型 if (decrypt_res.isFailed()) { System.out.println(String.format("%s\n", decrypt_res.getErrMsg())); } else { System.out.println(String.format("decrypted plaintext: %s size: %d type: %s\n", decrypt_res.getPlaintext(), decrypt_res.getPlaintextSize(), decrypt_res.getOriginalType())); }
  • 执行加密表的预编译SQL语句 // 调用Connection的prepareStatement方法创建预编译语句对象。 PreparedStatement pstmt = conn.prepareStatement("INSERT INTO creditcard_info VALUES (?, ?, ?);"); // 调用PreparedStatement的setShort设置参数。 pstmt.setInt(1, 2); pstmt.setString(2, "joy"); pstmt.setString(3, "6219985678349800033"); // 调用PreparedStatement的executeUpdate方法执行预编译SQL语句。 int rowcount = pstmt.executeUpdate(); // 调用PreparedStatement的close方法关闭预编译语句对象。 pstmt.close();
  • 加密模型 全密态数据库使用多级加密模型,加密模型中涉及3个对象:数据、列密钥和主密钥,以下是对3个对象的介绍: 数据: SQL语法中包含的数据,比如INSERT... VALUES ('data')语法中包含'data'。 从数据库服务端返回的查询结果,如执行SELECT语法返回的查询结果。 密态数据库会在驱动中,对SQL语法中属于加密列的数据进行加密,对数据库服务端返回的属于加密列的查询结果进行解密。 列密钥:数据由列密钥进行加密,列密钥由数据库驱动生成或由用户手动导入,列密钥密文存储在数据库服务端。 主密钥:列密钥由主密钥加密,主密钥由外部密钥管理者生成并存储。数据库驱动会自动访问外部密钥管理者,以实现对列密钥进行加解密。
  • 生成主密钥阶段 首次使用密态数据库时,需使用外部密钥管理服务生成至少一个主密钥,生成方式如下: 华为公有云场景 登录账号:进入华为云官网,注册并登录账号。 创建新用户:搜索并进入"身份认证服务",在"用户"中,通过"创建用户"按钮创建一个IAM用户,设置IAM密码,并为IAM用户关联一个"用户组",然后对用户组授权使用" 数据加密 服务"权限。 登录新用户:重新回到登录页面,选择"IAM用户"登录方式,使用新上一步创建的IAM用户进行登录。后续操作均由该IAM用户完成。 创建主密钥:选择"密钥管理"功能,并通过"创建密钥"按钮创建至少1个密钥,即主密钥。 记住主密钥ID:成功创建主密钥后,每个主密钥都有1个密钥ID。在后续使用密态数据过程中,需配置主密钥ID,数据库驱动会通过Restful接口访问该主密钥。 在生成主密钥后,需为数据驱动准备访问主密钥的参数,比如IAM用户名、项目ID等参数。华为云支持两种身份认证方式,两种方式需要的参数个数与参数类型不同,选择其中一种方式即可。下述步骤介绍如何获取这些参数: 方式一 aksk认证 AK、SK:首先登录华为云“控制台”,单击右上角用户名,进入“我的凭证”,选择“访问密钥”,通过“新增访问密钥”创建AK与SK,创建完成后下载密钥(即AK与SK)。 项目ID:在华为云控制台中,单击右上角用户名,并进入“我的凭证”,单击“API凭证”即可找到“项目ID”。 KMS服务器地址:https://kms.项目.myhuaweicloud.com/v1.0/项目ID/kms。 方式二 账号密码认证 IAM用户名、账号名、项目、项目ID:在华为云控制台中,单击右上角用户名,并进入“我的凭证”,可看到下图所示页面,该页面可获取4个参数:IAM用户名、账号名、项目、项目ID。 IAM服务器地址:https://iam.项目.myhuaweicloud.com/v3/auth/tokens。 IAM用户密码:IAM用户名对应的密码。 KMS服务器地址:https://kms.项目.myhuaweicloud.com/v1.0/项目ID/kms。
  • 整体流程 在使用全密态数据库的过程中,主要流程包括如下四个阶段,本节(2.1)介绍整体流程。使用gsql操作密态数据库、使用JDBC操作密态数据库章节介绍详细使用流程。 一、生成主密钥阶段:首先,用户需在华为云密钥服务中生成主密钥。生成主密钥后,需准备访问主密钥的参数,以供数据库使用。 二、执行DDL阶段:在本阶段,用户可使用密态数据库的密钥语法依次定义主密钥和列密钥,然后定义表并指定表中某列为加密列。定义主密钥和列密钥的过程中,需访问上一阶段生成的主密钥。 三、执行DML阶段:在创建加密表后,用户可直接执行包含但不限于INSERT、SELECT、UPDATE、DELETE等语法。数据库驱动会自动根据上一阶段的加密定义自动对加密列中的数据进行加解密。 四、清理阶段:依次删除加密表、列密钥和主密钥。
共100000条
提示

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