华为云用户手册

  • 使用示例 gaussdb=# DROP TABLE IF EXISTS "public".flashtest; NOTICE: table "flashtest" does not exist, skipping DROP TABLE --创建表 gaussdb=# CREATE TABLE "public".flashtest (col1 INT,col2 TEXT) with(storage_type=ustore); NOTICE: The 'DISTRIBUTE BY' clause is not specified. Using 'col1' as the distribution column by default. HINT: Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column. CREATE TABLE --查询csn gaussdb=# SELECT int8in(xidout(next_csn)) FROM gs_get_next_xid_csn(); int8in ---------- 79352065 79352065 79352065 79352065 79352065 79352065 (6 rows) --查询当前的时间戳 gaussdb=# SELECT now(); now ------------------------------- 2023-09-13 19:46:34.102863+08 (1 row) --查看表flashtest gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+------ (0 rows) --插入数据 gaussdb=# INSERT INTO flashtest VALUES(1,'INSERT1'),(2,'INSERT2'),(3,'INSERT3'),(4,'INSERT4'),(5,'INSERT5'),(6,'INSERT6'); INSERT 0 6 gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 3 | INSERT3 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 6 | INSERT6 (6 rows) --闪回表至特定csn gaussdb=# TIMECAPSULE TABLE flashtest TO CS N 79352065; TimeCapsule Table gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+------ (0 rows) gaussdb=# SELECT now(); now ------------------------------- 2023-09-13 19:52:21.551028+08 (1 row) --插入数据 gaussdb=# INSERT INTO flashtest VALUES(1,'INSERT1'),(2,'INSERT2'),(3,'INSERT3'),(4,'INSERT4'),(5,'INSERT5'),(6,'INSERT6'); INSERT 0 6 gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 3 | INSERT3 6 | INSERT6 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 (6 rows) --闪回表至此刻之前的特定时间戳 gaussdb=# TIMECAPSULE TABLE flashtest TO TIMESTAMP to_timestamp ('2023-09-13 19:52:21.551028', 'YYYY-MM-DD HH24:MI:SS.FF'); TimeCapsule Table gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+------ (0 rows) gaussdb=# select now(); now ------------------------------- 2023-09-13 19:54:00.641506+08 (1 row) --插入数据 gaussdb=# INSERT INTO flashtest VALUES(1,'INSERT1'),(2,'INSERT2'),(3,'INSERT3'),(4,'INSERT4'),(5,'INSERT5'),(6,'INSERT6'); INSERT 0 6 gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 3 | INSERT3 6 | INSERT6 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 (6 rows) --闪回表至此刻之后的特定时间戳 gaussdb=# TIMECAPSULE TABLE flashtest TO TIMESTAMP '2023-09-13 20:54:00.641506'; ERROR: The specified timestamp is invalid. gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+------ 3 | INSERT3 6 | INSERT6 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 (6 rows) gaussdb=# DROP TABLE IF EXISTS "public".flashtest; DROP TABLE
  • 语法 {[ ONLY ] table_name [ * ] [ partition_clause ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] [ TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] ] [TIMECAPSULE { TIMESTAMP | CSN } expression ] |( select ) [ AS ] alias [ ( column_alias [, ...] ) ] |with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ] |function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [, ...] | column_definition [, ...] ) ] |function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] ) |from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]}
  • 使用示例 示例(需将undo_retention_time参数设置为大于0的值): gaussdb=# DROP TABLE IF EXISTS "public".flashtest; NOTICE: table "flashtest" does not exist, skipping DROP TABLE --创建表flashtest。 gaussdb=# CREATE TABLE "public".flashtest (col1 INT,col2 TEXT) with(storage_type=ustore); NOTICE: The 'DISTRIBUTE BY' clause is not specified. Using 'col1' as the distribution column by default. HINT: Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column. CREATE TABLE --查询csn。 gaussdb=# SELECT int8in(xidout(next_csn)) FROM gs_get_next_xid_csn(); int8in ---------- 79351682 79351682 79351682 79351682 79351682 79351682 (6 rows) --查询当前时间戳。 gaussdb=# SELECT now(); now ------------------------------- 2023-09-13 19:35:26.011986+08 (1 row) --插入数据。 gaussdb=# INSERT INTO flashtest VALUES(1,'INSERT1'),(2,'INSERT2'),(3,'INSERT3'),(4,'INSERT4'),(5,'INSERT5'),(6,'INSERT6'); INSERT 0 6 gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 3 | INSERT3 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 6 | INSERT6 (6 rows) --闪回查询某个csn处的表。 gaussdb=# SELECT * FROM flashtest TIMECAPSULE CSN 79351682; col1 | col2 ------+------ (0 rows) gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 3 | INSERT3 6 | INSERT6 (6 rows) --闪回查询某个时间戳处的表。 gaussdb=# SELECT * FROM flashtest TIMECAPSULE TIMESTAMP '2023-09-13 19:35:26.011986'; col1 | col2 ------+------ (0 rows) gaussdb=# SELECT * FROM flashtest; col1 | col2 ------+--------- 1 | INSERT1 2 | INSERT2 4 | INSERT4 5 | INSERT5 3 | INSERT3 6 | INSERT6 (6 rows) --闪回查询某个时间戳处的表。 gaussdb=# SELECT * FROM flashtest TIMECAPSULE TIMESTAMP to_timestamp ('2023-09-13 19:35:26.011986', 'YYYY-MM-DD HH24:MI:SS.FF'); col1 | col2 ------+------ (0 rows) --闪回查询某个csn处的表,并对表进行重命名。 gaussdb=# SELECT * FROM flashtest AS ft TIMECAPSULE CSN 79351682; col1 | col2 ------+------ (0 rows) gaussdb=# DROP TABLE IF EXISTS "public".flashtest; DROP TABLE
  • 闪回恢复 闪回恢复功能是数据库恢复技术的一环,可以有选择性地撤销一个已提交事务的影响,将数据从人为不正确的操作中进行恢复。在采用闪回技术之前,只能通过备份恢复、PITR等手段找回已提交的数据库修改,恢复时长需要数分钟甚至数小时。采用闪回技术后,通过闪回Drop和闪回Truncate恢复已提交的数据库Drop/Truncate的数据,只需要秒级,而且恢复时间和数据库大小无关。 ASTORE引擎只支持闪回DROP/TRUNCATE功能。 备机不支持闪回操作。 用户可以根据需要开启闪回功能,开启后会带来一定的性能劣化。 闪回查询 闪回表 闪回DROP/TRUNCATE 父主题: Ustore存储引擎
  • 事务回滚 回滚是在事务运行的过程中发生了故障等异常情形下,事务不能继续执行,系统需要将事务中已完成的修改操作进行撤销。Astore、Ubtree没有回滚段,自然没有这个专门的回滚动作。Ustore为了性能考虑,它的回滚流程结合了同步、异步和页面级回滚等3种形式。 同步回滚 有三种情况会触发事务的同步回滚: 事务块中的ROLLBACK关键字会触发同步回滚。 事务运行过程中如果发生ERROR级别报错,此时的COMMIT关键字与ROLLBACK功能相同,也会触发同步回滚。 事务运行过程中如果发生FATAL/PANIC级别报错,在线程退出前会尝试将该线程绑定的事务进行一次同步回滚。 异步回滚 同步回滚失败或者在系统宕机后再次重启时,会由Undo回收线程为未回滚完成的事务发起异步回滚任务,立即对外提供服务。由异步回滚任务发起线程undo launch负责拉起异步回滚工作线程undo worker,再由异步回滚工作线程实际执行回滚任务。undo launch线程最多可以同时拉起5个undo worker线程。 页面级回滚 当事务需要回滚但还未回滚到本页面时,如果其他事务需要复用该事务所占用的TD,就会在复用前对该事务在本页面的所有修改执行页面级回滚。页面级回滚只负责回滚事务在本页面的修改,不涉及其他页面。 Ustore子事务的回滚由ROLLBACK TO SAVEPOINT语句控制,子事务回滚后父事务可以继续运行,子事务的回滚不影响父事务的事务状态。如果一个事务在回滚时还存在未释放的子事务,该事务回滚前会先执行子事务的回滚,所有子事务回滚完毕后才会进行父事务的回滚。 父主题: Ustore事务模型
  • 事务提交 针对隐式事务和显式事务,其提交策略如下所示: 隐式事务。单条DML/DDL语句自动触发隐式事务,这种事务没有显式的事务块控制语句(START TRANSACTION/BEGIN/COMMIT/END),DML语句结束后自动提交。 显式事务。显式事务由显式的START TRANSACTION/BEGIN语句控制事务的开始,由COMMIT/END语句控制事务的提交。 子事务必须存在于显式事务或存储过程中,由SAVEPOINT语句控制子事务开始,由RELEASE SAVEPOINT语句控制子事务结束。如果一个事务在提交时还存在未释放的子事务,该事务提交前会先执行子事务的提交,所有子事务提交完毕后才会进行父事务的提交。 Ustore支持读已提交隔离级别。语句在执行开始时,获取当前系统的CSN作为当前语句的查询CSN。整个语句的可见结果由语句开始那一刻决定,不受后续其他事务修改影响。Ustore中read committed默认是保持一致性读的。Ustore也支持标准的2PC事务。 父主题: Ustore事务模型
  • Ustore事务模型 GaussDB 事务基础: 事务启动时不会自动分配XID,该事务中的第一条DML/DDL语句运行时才会真正为该事务分配XID。 事务结束时,会产生代表事务提交状态的C LOG (Commit Log),CLOG共有四种状态:事务运行中、事务提交、事务同步回滚、子事务提交。每个事务的 CLOG状态位为2 bits,CLOG页面上每个字节可以表示四个事务的提交状态。 事务结束时,还会产生代表事务提交顺序的CSN(Commit sequence number)。CSN为实例级变量,每个XID都有自己对应的唯一CSN。CSN可以标记事务的以下状态:事务提交中、事务提交、事务回滚、事务已冻结等。 事务提交 事务回滚 父主题: Ustore存储引擎
  • Enhanced Toast运维管理 通过gs_parse_page_bypath解析主表中的ToastPointer信息。 gaussdb=# SELECT ctid,next_chunk,chunk_seq FROM pg_toast.pg_toast_part_17559; ctid | next_chunk | chunk_seq -------+------------+----------- (0,1) | (0,0) | 1 (0,2) | (0,1) | 0 (0,3) | (0,0) | 1 (0,4) | (0,3) | 0 (4 rows) gaussdb=# SELECT gs_parse_page_bypath((SELECT * FROM pg_relation_filepath('test_toast')),0,'uheap',false); gs_parse_page_bypath ------------------------------------------------------------------ ${data_dir}/gs_log/dump/1663_13113_17603_0.page (1 row) 解析文件1663_13113_17603_0.page中存储了ToastPointer的相关信息,具体如下: Toast_Pointer: column_index: 1 toast_relation_oid: 17608 --线外存储表OID信息 ctid: (4, 1) --线外存储数据链,头指针 bucket id: -1 --bucket id信息 column_index: 2 toast_relation_oid: 17608 ctid: (2, 1) bucket id: -1 Enhanced Toast数据查询,通过直接查询到的Enhanced Toast表数据可以判断其链式结构的完整性。 gaussdb=# SELECT ctid,next_chunk,chunk_seq FROM pg_toast.pg_toast_part_17559; ctid | next_chunk | chunk_seq -------+------------+----------- (0,1) | (0,0) | 1 (0,2) | (0,1) | 0 (0,3) | (0,0) | 1 (0,4) | (0,3) | 0 (4 rows) 父主题: Enhanced Toast
  • Enhanced Toast相关DDL操作 Enhanced Toast表的创建 建表时指定Toast表的存储类型为Enhanced Toast或者Toast: gaussdb=# CREATE TABLE test_toast (id int, content text) with(toast.toast_storage_type=toast); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no 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=toast gaussdb=# DROP TABLE test_toast; DROP TABLE gaussdb=# CREATE TABLE test_toast (id int, content text) with(toast.toast_storage_type=enhanced_toast); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no 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 gaussdb=# DROP TABLE test_toast; DROP TABLE 建表时不指定线外存储表的类型,则创建线外存储表类型依赖于GUC参数enable_enhance_toast_table: -- 根据“Enhanced Toast使用”章节打开GUC gaussdb=# show enable_enhance_toast_table; enable_enhance_toast_table ---------------------------- on (1 row) gaussdb=# CREATE TABLE test_toast (id int, content text); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no 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 gaussdb=# DROP TABLE test_toast; DROP TABLE gaussdb=# SET enable_enhance_toast_table = off; SET gaussdb=# show enable_enhance_toast_table; enable_enhance_toast_table ---------------------------- off (1 row) gaussdb=# CREATE TABLE test_toast (id int, content text); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no 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=toast gaussdb=# DROP TABLE test_toast; DROP TABLE 线外存储表结构的升级 当GUC参数“enable_enhance_toast_table=on”时,线外存储表支持通过Vacuum Full操作将Toast升级为Enhanced Toast结构。 gaussdb=# CREATE TABLE test_toast (id int, content text); CREATE TABLE gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no 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=toast gaussdb=# VACUUM FULL test_toast; VACUUM gaussdb=# \d+ test_toast Table "public.test_toast" Column | Type | Modifiers | Storage | Stats target | Description ---------+---------+-----------+----------+--------------+------------- id | integer | | plain | | content | text | | extended | | Has OIDs: no 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 gaussdb=# DROP TABLE test_toast; DROP TABLE 分区表merge操作 支持将分区表的分区间不同的线外存储表类型进行合并操作。 对于相同类型的线外存储分区,合并与原有逻辑保持一致,进行物理合并。 对于不同类型的线外存储分区,合并后的分区线外存储表为Enhanced Toast表,需要进行逻辑合并,性能劣于物理合并。 gaussdb=# CREATE TABLE test_partition_table(a int, b text)PARTITION BY range(a)(partition p1 values less than (2000),partition p2 values less than (3000)); gaussdb=# SELECT relfilenode FROM pg_partition WHERE relname='p1'; relfilenode ------------- 17529 (1 row) gaussdb=# \d+ pg_toast.pg_toast_part_17529 TOAST table "pg_toast.pg_toast_part_17529" Column | Type | Storage ------------+---------+--------- chunk_id | oid | plain chunk_seq | integer | plain chunk_data | bytea | plain Options: storage_type=ustore, toast_storage_type=toast gaussdb=# SELECT relfilenode FROM pg_partition WHERE relname='p2'; relfilenode ------------- 17528 (1 row) gaussdb=# \d+ pg_toast.pg_toast_part_17528 TOAST table "pg_toast.pg_toast_part_17528" Column | Type | Storage ------------+---------+--------- chunk_seq | integer | plain next_chunk | tid | plain chunk_data | bytea | plain Options: storage_type=ustore, toast_storage_type=enhanced_toast gaussdb=# ALTER TABLE test_partition_table MERGE PARTITIONS p1,p2 INTO partition p1_p2; ALTER TABLE gaussdb=# SELECT reltoastrelid::regclass FROM pg_partition where relname='p1_p2'; reltoastrelid ------------------------------ pg_toast.pg_toast_part_17559 (1 row) gaussdb=# \d+ pg_toast.pg_toast_part_17559 TOAST table "pg_toast.pg_toast_part_17559" Column | Type | Storage ------------+---------+--------- chunk_seq | integer | plain next_chunk | tid | plain chunk_data | bytea | plain Options: storage_type=ustore, toast_storage_type=enhanced_toast gaussdb=# DROP TABLE test_partition_table; DROP TABLE 父主题: Enhanced Toast
  • Enhanced Toast增删改查 Insert操作:触发Enhanced Toast的写入条件保持与原有Toast一致,除了数据写入时增加了数据间的链接信息之外,插入基本逻辑保持不变。 Delete操作:Enhanced Toast的数据删除流程不再依赖Toast数据索引,仅依靠数据间的链接信息将对应的数据进行遍历删除。 Update操作:Enhanced Toast的更新流程与原有Toast保持一致。 父主题: Enhanced Toast
  • Enhanced Toast使用 新增的GUC参数enable_enhance_toast_table用于控制线外存储结构。 “enable_enhance_toast_table=on”表示使用Enhanced Toast线外存储表。 “enable_enhance_toast_table=off”表示使用Toast线外存储表。 gs_guc reload -Z coordinator -Z datanode -N all -I all -c "enable_enhance_toast_table=on" 父主题: Enhanced Toast
  • 概述 Enhanced Toast是一种用于处理超大字段的技术。首先,减少了Toast Pointer中的冗余信息,存储支持单表超长字段列数超过500列。其次,优化了主表与线外存储表之间的映射关系,无需通过pg_toast_index来存储主表数据与线外存储表数据的关系,降低了用户存储空间。最后,Enhanced Toast技术通过让分割数据自链接,消除了Oid分配的依赖,极大地加快了写入效率。 Astore存储引擎不支持Enhanced Toast。 不支持对Enhanced Toast类型的线外存储表单独进行Vacuum Full操作。 父主题: Enhanced Toast
  • 空间管理 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
  • 文件组织结构 如需查询当前回滚段使用的存储方式是页式或段页式,可以查询系统表。当前仅支持页式。 示例: gaussdb=# SELECT * FROM gs_global_config where name like '%undostoragetype%'; name | value -----------------+--------- undostoragetype | page (1 row) 当回滚段使用的存储方式为页式: txn page所在文件组织结构: $node_dir/undo/{permanent|unlogged|temp}/$undo_zone_id.meta.$segno undo row所在文件组织结构: $node_dir/undo/{permanent|unlogged|temp}/$undo_zone_id.$segno 父主题: Undo
  • PCR UBTree增删改查 Insert操作:操作与RCR UBTree基本一致,区别是:插入前需要先申请TD和写入Undo。 Delete操作:操作与RCR UBTree基本一致,区别是:删除前需要先申请TD和写入Undo。 Update操作:操作与RCR UBTree无区别,均转换为一条Delete操作和和一条Insert操作。 Scan操作:操作与RCR UBTree基本一致,区别是:查询操作需要将页面复制一个CR页面出来,将CR页面回滚到扫描快照可见的状态,从而整个页面的元组对于快照都是可见版本。
  • RCR UBTree增删改查 Insert操作:Ubtree的插入逻辑基本不变,只需增加索引插入时直接获取事务信息填写xmin字段。 Delete操作:Ubtree额外增加了索引删除流程。索引删除主要步骤与插入相似,获取事务信息填写xmax字段(B-tree索引不维护版本信息,不需要删除操作),同时更新页面上的active_tuple_count。若active_tuple_count被减为0,则尝试页面回收。 Update操作:对于Ustore而言,数据更新对Ubtree索引列的操作也与Astore有所不同。数据更新包含两种情况:索引列和非索引列更新,Ubtree在数据发生更新时的处理如图3所示。 图3 UBTree在数据发生更新时的处理 图3展示Ubtree在索引列和非索引列更新的差异: 在非索引列更新的情况下,索引不发生任何变化。index tuple仍指向第一次插入的data tuple,Uheap不会插入新的data tuple,而是修改当下data tuple并将历史数据存入Undo中。 在索引列更新的情况下,Ubtree也会插入新的index tuple,但是会指向同一个data linepointer和同一个data tuple。扫描旧版本的数据则需要从Undo中读取。 Scan操作:用户在读取数据时,可通过使用索引扫描加速,Ubtree支持索引数据的多版本管理及可见性检查,索引层的可见性检查使得索引扫描(Index Scan)及仅索引扫描(IndexOnly Scan)性能有所提升。 对于索引扫描: 若索引列包含所有扫描列(IndexOnly Scan),则通过扫描条件在索引上进行二分查找,找到符合条件元组即可返回数据。 若索引列不包含所有扫描列(Index Scan),则通过扫描条件在索引上进行二分查找,找到符合条件元组的TID,再通过TID到数据表上查找对应的数据元组。如图4 对应的数据元组所示。 图4 对应的数据元组
  • RCR UBTree空间管理 当前Astore的索引依赖AutoVacuum和Free Space Map(FSM)进行空间管理,存在回收不及时的问题,而Ustore的索引使用其特有的URQ(UBTree Recycle Queue,一种基于循环队列的数据结构,即双循环队列)对索引空闲空间进行管理。双循环队列是指有两个循环队列,一个潜在空页队列,另一个可用空页队列。在DML过程中完成索引的空间管理,能有效地缓解DML过程中造成的空间急剧膨胀问题。 索引回收队列单独储存在B-tree索引对应的FSM文件中。 图5 索引页面在双循环队列间流动 如图5所示,索引页面在双循环队列间流动如下: 索引空页流动到潜在队列 索引页尾字段中记录了页面上活跃元组个数(activeTupleCount)。在DML过程中,删空一个页面的所有元组,即activeTupleCount为零时会将索引页放入潜在队列中。 潜在队列流动到可用队列 潜在队列到可用队列的转化主要是达到一个潜在队列收支平衡以及可用队列在拿页时有页可拿的目的。即当从可用队列拿出一个索引空页用完后,建议从潜在队列转化至少一个索引页面到可用队列中,以及每当潜在队列新加入一个索引页面时,能从潜在队列中移除至少一个索引页插入可用队列中,达到潜在队列的收支平衡,以及可用队列有页可用的目的。 可用队列流动到索引空页 索引在分裂等获取一个索引空页面时,会先从可用队列中进行查找是否有可以复用的索引页,如果找到则直接进行复用,没有可复用页面则进行物理扩页。
  • RCR UBTree多版本管理 RCR(Row Consistency Read) btree 的多版本管理是基于数据行的行级多版本管理。将XID记录在了数据行上,会增加Key的大小,索引会有5-20%左右的膨胀。最新版本和历史版本均在btree上,索引没有记录Undo信息。插入或者删除key时按照key + TID的顺序排列,索引列相同的元组按照对应元组的TID作为第二关键字进行排序,会将xmin、xmax追加到key的后面。索引分裂时,多版本信息随着key的迁移而迁移,如图1所示。 图1 RCR UBTree多版本管理
  • RCR Uheap空闲空间管理 Ustore使用Free Space Map(FSM)文件记录了每个数据页的潜在空闲空间,并且以树的结构组织起来。每当用户想要对某个表执行插入操作或者是非原位更新操作时,就会从该表对应的FSM中进行快速查找,查看当前FSM上记录的最大空闲空间是否可以满足插入所需的空间要求;如果满足则返回对应的blocknum用于执行插入操作,否则执行拓展页面逻辑。 每一个表或者分区对应的FSM结构存放在一个独立的FSM文件中,该FSM文件与表数据放在相同的目录下。例如,假设表t1对应的数据文件为32181,则其对应的FSM文件为32181_fsm。FSM内部同样是以数据块的格式存储,这里称为FSM block,FSM block之间的逻辑结构组成了一棵有三层节点的树,树的节点在逻辑上是大顶堆关系。每次在FSM上查找时从根节点进行,一直查找到叶子节点,然后在叶子节点内搜索到一个可用的页面并返回给业务用于执行后续操作。 该结构不保证和数据页实际可用空间保持实时一致,会在DML的执行过程中进行维护。Ustore会在Auto Vacuum的过程中概率性对该FSM进行修复重建。当用户执行插入类型的DML语句,类似Insert/Non-Inplace Update(新页面)/Multi Insert时,会查询FSM结构,寻找到一个可以插入当前记录的空间。用户执行完DML操作后会根据当前页面的潜在空闲空间与实际空闲空间的差值来决定是否将该页面的空闲空间刷新到FSM上。该差值越大,即潜在空间大于实际空间越多,则该页面被更新至FSM的几率越大。FSM上会记录数据页的潜在空闲空间 ,在用户执行插入操作找到一个页面时,如果该页面上的空闲空间较大则直接插入,否则如果潜在空间较大则对页面执行清理后插入。最后如果空间不够则重新搜索FSM结构或者拓展总页面数量。更新FSM结构主要有以下几个位置,DML、页面清理、vacuum、拓展页面、分区合并、页面扫描等。 父主题: RCR Uheap
  • RCR Uheap多版本管理 Ustore对其使用的heap做了如下重要的增强,简称Uheap。 Ustore RCR(Row Consistency Read)的多版本管理是基于数据行的行级多版本管理。不过Ustore将XID记录在了页面的TD(Transaction Directory)区域区别于常见的将XID存储在数据行上,节省了页面空间。事务修改记录时,会将历史数据记录到Undo Row中,在Tuple中的td_id指向的TD槽上记录产生的Undo Row地址(zone_id, block no, page offset),并将新的数据覆盖写入页面。访问元组时,沿着版本链还原该元组,直到找到自己对应的版本。 父主题: RCR Uheap
  • 在线校验功能 在线校验是Ustore特有的,在运行过程中可以有效预防页面因编码逻辑错误导致的逻辑损坏,默认开启UPAGE:UBTREE:UNDO三个模块校验。业务现网请保持开启,性能场景除外。 关闭: gs_guc reload -Z coordinator -Z datanode -N all -I all -c "ustore_attr=''" 打开: gs_guc reload -Z coordinator -Z datanode -N all -I all -c "ustore_attr='ustore_verify_level=fast;ustore_verify_module=upage:ubtree:undo'" 父主题: Ustore的最佳实践
  • 使用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。 父主题: 存储引擎体系架构概述
共100000条
提示

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