华为云用户手册

  • 数据分区查找优化 分区表对数据查找方面的帮助主要体现在对分区键进行谓词查询场景,例如一张以月份Month作为分区键的表,如图1所示。 如果以普通表的方式设计表结构则需要访问表全量的数据(Full Table Scan),如果以日期为分区键重新设计该表,那么原有的全表扫描会被优化成为分区扫描。当表内的数据量很大同时具有很长的历史周期时,由于扫描数据缩减所带来的性能提升会有明显的效果,如图2所示。 图1 分区表示例图 图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 对数据复制能力的支持情况为: 支持通过数据迁移工具定期向异构数据库(如Oracle数据库等)进行数据同步,不具备实时数据复制能力,因此不足以支撑与异构数据库间并网运行实时数据同步的诉求。 基于上述情况,GaussDB提供了逻辑解码功能,通过反解xlog的方式生成逻辑日志。目标数据库解析逻辑日志以实时进行数据复制。具体如图1所示。逻辑复制降低了对目标数据库的形态限制,支持异构数据库、同构异形数据库对数据的同步,支持目标库进行数据同步期间的数据可读写,数据同步时延低。 图1 逻辑复制 逻辑复制由两部分组成:逻辑解码和数据复制。逻辑解码会输出以事务为单位组织的逻辑日志。业务或数据库中间件将会对逻辑日志进行解析并最终实现数据复制。GaussDB当前只提供逻辑解码功能,因此本章节只涉及逻辑解码的说明。 逻辑解码
  • 背景信息 当前的账本数据库机制为:全局区块表存储在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。
  • 恢复用户表和用户历史表名称 已通过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
  • 背景信息 账本数据库归档功能目前提供两种校验接口,分别为: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证书文件");
  • 执行加密表的预编译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();
  • 执行密态等值密文解密 数据库连接接口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())); }
  • 加密模型 全密态数据库使用多级加密模型,加密模型中涉及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等语法。数据库驱动会自动根据上一阶段的加密定义自动对加密列中的数据进行加解密。 四、清理阶段:依次删除加密表、列密钥和主密钥。
  • 语法格式 创建增量物化视图 CREATE INCREMENTAL MATERIALIZED VIEW view_name AS query; 全量刷新物化视图 REFRESH MATERIALIZED VIEW view_name; 增量刷新物化视图 REFRESH INCREMENTAL MATERIALIZED VIEW view_name; 删除物化视图 DROP MATERIALIZED VIEW view_name; 查询物化视图 SELECT * FROM view_name;
  • 示例 -- 修改表的默认类型 gaussdb=# SET enable_default_ustore_table=off; -- 准备数据 CREATE TABLE t1(c1 int, c2 int); INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(2, 2); -- 创建增量物化视图 gaussdb=# CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM t1; CREATE MATERIALIZED VIEW -- 插入数据 gaussdb=# INSERT INTO t1 VALUES(3, 3); INSERT 0 1 -- 增量刷新物化视图 gaussdb=# REFRESH INCREMENTAL MATERIALIZED VIEW mv; REFRESH MATERIALIZED VIEW -- 查询物化视图结果 gaussdb=# SELECT * FROM mv; c1 | c2 ----+---- 1 | 1 2 | 2 3 | 3 (3 rows) -- 插入数据 gaussdb=# INSERT INTO t1 VALUES(4, 4); INSERT 0 1 -- 全量刷新物化视图 gaussdb=# REFRESH MATERIALIZED VIEW mv; REFRESH MATERIALIZED VIEW -- 查询物化视图结果 gaussdb=# select * from mv; c1 | c2 ----+---- 1 | 1 2 | 2 3 | 3 4 | 4 (4 rows) -- 删除物化视图,删除表 gaussdb=# DROP MATERIALIZED VIEW mv; DROP MATERIALIZED VIEW gaussdb=# DROP TABLE t1; DROP TABLE
  • 不支持场景 物化视图中不支持带Stream计划,多表join连接计划以及subquery计划。 不支持WITH子句、GROUP BY子句、ORDER BY子句、LIMIT子句、WINDOW子句、DISTINCT算子、AGG算子,不支持除UNION ALL外的子查询。 除少部分ALTER操作外,不支持对物化视图中基表做绝大多数DDL操作。 创建物化视图不可指定物化视图分布列。 不可对物化视图做增删改操作,只支持查询语句。 不支持用临时表/hashbucket/unlog/分区表创建物化视图,只支持hash分布表。 不支持物化视图嵌套创建(物化视图上创建物化视图)。 不支持UN LOG GED类型的物化视图,不支持WITH语法。 Ustore引擎不支持增量物化视图的创建和使用。
  • 示例 -- 修改表的默认类型 gaussdb=# set enable_default_ustore_table=off; -- 准备数据 CREATE TABLE t1(c1 int, c2 int); INSERT INTO t1 VALUES(1, 1); 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); -- 对全量物化视图做全量刷新 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
  • 物化视图 物化视图是一种特殊的物理表,物化视图是相对普通视图而言的。普通视图是虚拟表,应用的局限性较大,任何对视图的查询实际上都是转换为对SQL语句的查询,性能并没有实际提高。而物化视图实际上就是存储SQL所执行语句的结果,起到缓存的效果。物化视图常用的操作包括创建、查询、删除和刷新。 根据创建规则,物化视图分为全量物化视图和增量物化视图。全量物化视图只支持全量刷新;增量物化视图支持全量刷新和增量刷新两种方式。全量刷新会将基表中的数据全部重新刷入物化视图中,而增量刷新只会将两次刷新间隔期间的基表产生的增量数据刷入物化视图中。 目前Ustore引擎不支持创建、使用物化视图。 全量物化视图 增量物化视图
共100000条
提示

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