摘要:账本数据库融合了区块链思想,将用户操作记录至两种历史表中:用户历史表和全局区块表。
本文分享自华为云社区《openGauss账本数据库,你不知道的那些事儿》,作者:Gauss松鼠会。
账本数据库融合了区块链思想,将用户操作记录至两种历史表中:用户历史表和全局区块表。当用户创建防篡改用户表时,系统将自动为该表添加一个hash列来保存每行数据的hash摘要信息,同时在blockchain模式下会创建一张用户历史表来记录对应用户表中每条数据的变更行为;而用户对防篡改用户表的一次修改行为将记录至全局区块表中。由于历史表具有只可追加不可修改的特点,因此历史表记录串联起来便形成了用户对防篡改用户表的修改历史。
操作步骤
1.创建防篡改模式。
首先在这个SQL中我们可以看到WITH BLOCKCHAIN ,这里说明创建出来的SCHEMA与普通的SCHEMA不同,但就行不同在哪里我们后面会提到。
- 从语法解析看,增加了对BLOCKCHAIN的处理,标记了是否为账本模式。
- CreateSchemaStmt 结构中增加了bool类型字段hasBlockChain
你不知道的限制
账本数据库对于ALTER SCHEMA的几个限制
1)dbe_perf和snapshot两个模式不能ALTER为blockchain模式。
2)系统模式不能 ALTER 为blockchain模式。
3)包含了表的SCHEMA不能ALTER为blockchain模式。
查看模式
2.在防篡改模式下创建防篡改用户表。
你不知道的限制
- 创建账本表的同时会自动创建一个“历史表”和“历史表的索引”。
在建表时CreateCommand会调用AlterCreateChainTables,如果是账本表再去调用create_hist_relation来创建历史表
CreateCommand -> AlterCreateChainTables -> create_hist_relation
- 历史表命名规则,参见函数get_hist_name
- 表名最大长度 #define NAMEDATALEN 64
- 如果没有超过长度限制:schema_table_hist
- 如果超过长度限制:schema(oid)_talbe(oid)_hist,因为oid是unsigned int 类型最大值为4294967295为10位,所以这种命名规则的最大长度为10+1+10+1+4+\0=27,因此永远不会超过最大长度64。
- 历史表索引命名规则,参见函数get_hist_name
- 命名规则:gs_hist_$(账本表oid)_index。
3、修改防篡改用户表数据
对防篡改用户表执行INSERT/UPDATE/DELETE。
查看账本历史操作记录
官方文档
前提条件
- 系统中需要有审计管理员或者具有审计管理员权限的角色。
- 数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。
基本操作
1、查询全局区块表记录。
- 注册钩子,在对账本做修改操作的时候注册的钩子函数ledger_ExecutorEnd被回调。
- 生成globalhash规则
全局区块表记录主要是生成globalhash.
调用过程:
ledger_ExecutorEnd --> ledger_gchain_append --> set_gchain_comb_string
--> get_next_g_blocknum
--> gen_global_hash
- set_gchain_comb_string,是一组字符串拼接成的:rel_name + nsp_name + query_string + rel_hash
- get_next_g_blocknum,用全局变量g_blocknum保存
- gen_global_hash,是的set_gchain_comb_string拼出来的串+上一条的hash值拼串然后再去hash——区块链的基本原理
- 在src/gausskernel/runtime/executor/nodeModifyTable.cpp中更新_hist表的hash值。
- 通过set_user_tuple_hash得到账本表hash列的值。
校验账本数据一致性
官方文档
数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。
基本操作
1、校验防篡改用户表ledgernsp.usertable与其对应的历史表是否一致。
- 校验用户权限 Only super user or audit admin have access right to blockchain nsp
- 校验历史表hash值
is_hist_hash_identity --> get_usertable_hash_sum
--> get_histtable_hash_sum
2、查询防篡改用户表ledgernsp.usertable与其对应的历史表以及全局区块表中关于该表的记录是否一致。
- 校验是否为账本表ledger_usertable_check
- 校验用户权限has_ledger_consistent_privilege
- 校验历史表hash值is_hist_hash_identity
- 计算/校验全局表hash get_gchain_relhash_sum
归档账本数据库
官方文档
前提条件:
- 系统中需要有审计管理员或者具有审计管理员权限的角色。
- 数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。
- 数据库已经正确配置审计文件的存储路径audit_directory。
基本操作
1、对指定用户历史表进行归档操作。
主要步骤如下:
- Copy user history table.
- Do unify and truncate.
- sum all hash_ins and hash_del for unification.
- Do real truncate.heap_truncate_one_rel
- Do insertion for unified row.simple_heap_insert
- Flush history hash table cache.
2、执行全局区块表导出操作
gs_global_chain主要处理流程:
- Init and prepare bak dictionary.
- Using CopyStmt to copy global chain.
- Do unify and truncate.
- Using hash table to do unify, each hash_entry refers to one relid informations.
- Split gs_global_chain by relid, and accumulate rel_hash to a new record for each rel.
- Do rel truncate.
- Insert newest record to gchain order by relid.
- Flush global_hash cache.
修复账本数据库
官方文档
前提条件:
- 系统中需要有审计管理员或者具有审计管理员权限的角色。
- 数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。
基本操作
1、执行历史表修复操作
[drawio] (rHmeQ8HWKS_RFXgP-oTUZINZguxBYqh2IV64Y0j5TAA.svg)
2、执行全局区块表修复操作
首先判断用户权限,之后通过get_gchain_relhash_sum函数计算relhash字段
主要是计算并修复gs_global_chain中的relhash字段。
总结
账本数据库其实并不像我们想象的那么复制,实际上就是利用了区块链的最基本的原理,即当前记录的特征值 + 上一条记录特征值的hash值,再进行hash。下一条与上一条记录具有数据关联性,形成“链”的结构,如果篡改了其中的数据,则会导致“链”断开,导致不能与后面数据记录形成hash关联。_hist表记录了用户表每一步数据变化的过程,gs_global_chain表记录了所有防篡改模式下对用户表的操作记录。用户表结合_hist和global表就能完整记录和校验。