介绍
账本数据库顾名思义,可以作为记账用的数据库,既然是记账,那至少应该具备历史操作可追溯,历史操作不可修改2个特点。
以下是比较官网的说明。
账本数据库融合了区块链思想,将用户操作记录至两种历史表中:用户历史表和全局区块表。当用户创建防篡改用户表时,系统将自动为该表添加一个hash列来保存每行数据的hash摘要信息,同时在blockchain模式下会创建一张用户历史表来记录对应用户表中每条数据的变更行为;而用户对防篡改用户表的一次修改行为将记录至全局区块表中。由于历史表具有只可追加不可修改的特点,因此历史表记录串联起来便形成了用户对防篡改用户表的修改历史。
实践过程
账本数据库配置
首先,账本数据库是以模式(schema)为单位设置,每创建一张防篡改表,在模式blockchain中自动创建一张对应的历史表。
注:用户历史表的表名一般为blockchain.<schemaname>_<tablename>hist形式。当防篡改用户表模式名或者表名过长导致前述方式生成的表名超出表名长度限制,则会采用blockchain.<schema_oid><table_oid>_hist的方式命名。
创建防篡改模式
bicewow@192.168.20.253 [db1]=> CREATE SCHEMA scm_for_book WITH BLOCKCHAIN;
CREATE SCHEMA
bicewow@192.168.20.253 [db1]=>\dn
List of schemas
Name | Owner
-----------------+---------
blockchain | omm
....
public | omm
scm_for_book | bicewow
snapshot | omm
sqladvisor | omm
(13 rows)
创建防篡改用户表
bicewow@192.168.20.253 [db1]=>CREATE TABLE scm_for_book.tab_01(id int, name text);
CREATE TABLE
bicewow@192.168.20.253 [db1]=>\d+ scm_for_book.tab_01
Table "scm_for_book.tab_01"
Column | Type | Modifiers | Storage | Stats target | Description
--------+---------+-----------+----------+--------------+-------------
id | integer | | plain | |
name | text | | extended | |
hash | hash16 | | plain | |
Has OIDs: no
Options: orientation=row, compression=no
History table name: scm_for_book_tab_01_hist <=========== 在对应表后面可以看到历史表名称
bicewow@192.168.20.253 [db1]=>\d+ blockchain.*
Index "blockchain.gs_hist_16413_index"
Column | Type | Definition | Storage
---------+--------+------------+---------
rec_num | bigint | rec_num | plain
primary key, btree, for table "blockchain.scm_for_book_tab_01_hist"
Table "blockchain.scm_for_book_tab_01_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_16413_index" PRIMARY KEY, btree (rec_num int4_ops) TABLESPACE pg_default
Has OIDs: no
Options: internal_mask=263
注:
1、 dbe_perf和snapshot两个模式不能ALTER为blockchain属性,如:ALTER SCHEMA dbe_perf WITH BLOCKCHAIN;。
2、 系统模式不能 ALTER 为blockchain属性,如:ALTER SCHEMA pg_catalog WITH BLOCKCHAIN;。
3、 包含表的SCHEMA不能通过ALTER SCHEMA语句修改属性为blockchain。
修改防篡改用户表数据
对表tab_01进行增删查改操作。
bicewow@192.168.20.253 [db1]=> INSERT INTO scm_for_book.tab_01 VALUES(1, 'zhangliang'), (2, 'xiaohe'), (3, 'liubang');
INSERT 0 3
bicewow@192.168.20.253 [db1]=>SELECT *, hash FROM scm_for_book.tab_01 ORDER BY id;
id | name | hash
----+------------+------------------
1 | zhangliang | 8f9fcdb1e3fc91d0
2 | xiaohe | aa47a297e70bcf06
3 | liubang | 3388adf014ef9cac
(3 rows)
bicewow@192.168.20.253 [db1]=>UPDATE scm_for_book.tab_01 SET name = 'hanxin' WHERE id = 2;
UPDATE 1
bicewow@192.168.20.253 [db1]=>SELECT *, hash FROM scm_for_book.tab_01 ORDER BY id;
id | name | hash
----+------------+------------------
1 | zhangliang | 8f9fcdb1e3fc91d0
2 | hanxin | 78eb5ae85955cc84
3 | liubang | 3388adf014ef9cac
(3 rows)
bicewow@192.168.20.253 [db1]=>DELETE FROM scm_for_book.tab_01 WHERE id = 3;
DELETE 1
bicewow@192.168.20.253 [db1]=>SELECT *, hash FROM scm_for_book.tab_01 ORDER BY id;
id | name | hash
----+------------+------------------
1 | zhangliang | 8f9fcdb1e3fc91d0
2 | hanxin | 78eb5ae85955cc84
(2 rows)
bicewow@192.168.20.253 [db1]=>
账本历史操作记录查看
账本历史操作存在2张表中,用户历史表和全局区块表,
用户历史表blockchain.<schemaname>_<tablename>_hist所包含的字段
| 字段名 | 类型 | 描述 |
|---|---|---|
| rec_num | bigint | 行级修改操作在历史表中的执行序号。 |
| hash_ins | hash16 | INSERT或UPDATE操作插入的数据行的hash值。 |
| hash_del | hash16 | DELETE或UPDATE操作删除的数据行的hash值。 |
| pre_hash | hash32 | 当前用户历史表的数据整体摘要。 |
全局区块表GS_GLOBAL_CHAIN字段
| 字段名 | 类型 | 描述 |
|---|---|---|
| blocknum | bigint | 区块号,当前用户操作在账本中记录的序号。 |
| dbname | name | 数据库名称。被修改的防篡改用户表所属的database。 |
| username | name | 用户名,执行用户表修改操作的用户名。 |
| starttime | timestamp with time zone | 用户操作执行的最新时间戳。 |
| relid | oid | 用户表Oid,被修改的防篡改用户表Oid。 |
| relnsp | name | 模式Oid,被修改的防篡改用户表所属的namesapce oid。 |
| relname | name | 用户表名,被修改的防篡改用户表名。 |
| relhash | hash16 | 当前操作产生的表级别hash变化量。 |
| globalhash | hash32 | 全局摘要,由当前行信息与前一行globalhash计算而来,将整个表串联起来,用于验证GS_GLOBAL_CHAIN数据完整性。 |
| txcommand | text | 被记录操作的SQL语句。 |
全局区块表记录
通过全局区块表,可以清晰的看到操作的命令,以及操作用户,数据库,schema等信息。
bicewow@192.168.20.253 [db1]=>\x
Expanded display is on.
bicewow@192.168.20.253 [db1]=>SELECT * FROM gs_global_chain;
-[ RECORD 1 ]---------------------------------------------------------------------------------------
blocknum | 1
dbname | db1
username | bicewow
starttime | 2024-11-04 15:26:07.446804+08
relid | 16413
relnsp | scm_for_book
relname | tab_01
relhash | 6d701e39dff7fd82
globalhash | bc57c39b0791ab44631580a81eddeb78
txcommand | INSERT INTO scm_for_book.tab_01 VALUES(1, 'zhangliang'), (2, 'xiaohe'), (3, 'liubang');
-[ RECORD 2 ]---------------------------------------------------------------------------------------
blocknum | 2
dbname | db1
username | bicewow
starttime | 2024-11-04 15:26:57.577345+08
relid | 16413
relnsp | scm_for_book
relname | tab_01
relhash | cea3b8507249fd7e
globalhash | 7ebe924a30fe07a879ebe1cff4aa2206
txcommand | UPDATE scm_for_book.tab_01 SET name = 'hanxin' WHERE id = 2;
-[ RECORD 3 ]---------------------------------------------------------------------------------------
blocknum | 3
dbname | db1
username | bicewow
starttime | 2024-11-04 15:27:32.590788+08
relid | 16413
relnsp | scm_for_book
relname | tab_01
relhash | cc77520feb106354
globalhash | 4184309834c0c49167a2f7b687121176
txcommand | DELETE FROM scm_for_book.tab_01 WHERE id = 3;
历史表记录
与全局区块表不同的是, 历史表记录每条数据的变更信息,而全局区块表则记录的是操作命令级别。
bicewow@192.168.20.253 [db1]=>select * from blockchain.scm_for_book_tab_01_hist;
rec_num | hash_ins | hash_del | pre_hash
---------+------------------+------------------+----------------------------------
0 | 8f9fcdb1e3fc91d0 | | e2758e5db54028e79a27596862f873c2
1 | aa47a297e70bcf06 | | 97455647ee682cfdfd24d67f5df28ee1
2 | 3388adf014ef9cac | | 34177d2391bd9fe29c57f8cf5322eedf
3 | 78eb5ae85955cc84 | aa47a297e70bcf06 | 99d019e1c3a121aa683e31ae8522a045
4 | | 3388adf014ef9cac | 931afb526a7891429ea32bbed53c4fac
(5 rows)
bicewow@192.168.20.253 [db1]=>delete from blockchain.scm_for_book_tab_01_hist where rec_num=0;
ERROR: Un-support feature
DETAIL: internal relation doesn't allow DELETE
bicewow@192.168.20.253 [db1]=>
校验账本数据一致性
账本数据库校验功能目前提供两种校验接口,分别为:ledger_hist_check(text, text)和ledger_gchain_check(text, text)。普通用户调用校验接口,仅能校验自己有权限访问的表。
历史表校验
校验防篡改用户表scm_for_book.tab_01与其对应的历史表是否一致。
bicewow@192.168.20.253 [db1]=>SELECT pg_catalog.ledger_hist_check('scm_for_book', 'tab_01');
ledger_hist_check
-------------------
t
(1 row)
该结果表明防篡改用户表和用户历史表中记录的结果能够一一对应,保持一致。
全局区块表校验
查询防篡改用户表ledgernsp.usertable与其对应的历史表以及全局区块表中关于该表的记录是否一致。
bicewow@192.168.20.253 [db1]=>SELECT pg_catalog.ledger_gchain_check('scm_for_book', 'tab_01');
ledger_gchain_check
---------------------
t
(1 row)
查询结果显示,上述三表中关于scm_for_book.tab_01的记录保持一致,未发生篡改行为。
归档账本数据库
账本数据库归档功能目前提供两种校验接口,分别为:ledger_hist_archive(text…和ledger_gchain_archive(vo…。账本数据库接口仅审计管理员可以调用。
注:
归档账本数据库,首先数据库需要正确配置审计文件的存储路径audit_directory
审计路径查看
bicewow@192.168.20.253 [db1]=>show audit_directory;
audit_directory
--------------------------------------
/opt/huawei/log/omm/pg_audit/dn_6001
(1 row)
历史表进行归档
## 归档前 历史表数据
bicewow@192.168.20.253 [db1]=>select * from blockchain.scm_for_book_tab_01_hist;
rec_num | hash_ins | hash_del | pre_hash
---------+------------------+------------------+----------------------------------
0 | 8f9fcdb1e3fc91d0 | | e2758e5db54028e79a27596862f873c2
1 | aa47a297e70bcf06 | | 97455647ee682cfdfd24d67f5df28ee1
2 | 3388adf014ef9cac | | 34177d2391bd9fe29c57f8cf5322eedf
3 | 78eb5ae85955cc84 | aa47a297e70bcf06 | 99d019e1c3a121aa683e31ae8522a045
4 | | 3388adf014ef9cac | 931afb526a7891429ea32bbed53c4fac
(5 rows)
## 归档
bicewow@192.168.20.253 [db1]=>SELECT pg_catalog.ledger_hist_archive('scm_for_book', 'tab_01');
ledger_hist_archive
---------------------
t
(1 row)
## 归档后 历史表数据
bicewow@192.168.20.253 [db1]=>select * from blockchain.scm_for_book_tab_01_hist;
rec_num | hash_ins | hash_del | pre_hash
---------+------------------+------------------+----------------------------------
4 | e65b7922394dca06 | ddd05087fbfb6bb2 | 931afb526a7891429ea32bbed53c4fac
(1 row)
归档之后,在本地目录生成hist_bak目录,里面详细记录了历史表的归档信息。
[root@dmdem ~]# cd /opt/huawei/log/omm/pg_audit/dn_6001
[root@dmdem dn_6001]# ll
total 26092
-rw------- 1 omm dbgrp 426438 Oct 30 23:59 0_adt
-rw------- 1 omm dbgrp 1691986 Oct 31 23:59 1_adt
-rw------- 1 omm dbgrp 2073663 Nov 1 23:59 2_adt
-rw------- 1 omm dbgrp 2073653 Nov 2 23:59 3_adt
-rw------- 1 omm dbgrp 2072253 Nov 3 23:59 4_adt
-rw------- 1 omm dbgrp 1483205 Nov 4 17:03 5_adt
drwx------ 2 omm dbgrp 6 Oct 30 16:47 done
drwx------ 2 omm dbgrp 64 Nov 4 17:02 hist_bak
-rw------- 1 omm dbgrp 16777456 Nov 4 00:00 index_table_new
[root@dmdem dn_6001]# cd hist_bak
[root@dmdem hist_bak]# ll
total 4
-rw------- 1 omm dbgrp 289 Nov 4 17:02 scm_for_book_tab_01_hist_16413_20241104170211.hist
[root@dmdem hist_bak]# more scm_for_book_tab_01_hist_16413_20241104170211.hist
0 8f9fcdb1e3fc91d0 \N b1c611527989582f7355b2c829613e65
1 aa47a297e70bcf06 \N f55f72741dfde6f5447a070df811c6a2
2 3388adf014ef9cac \N 3ffcf0d105d8a16e227fe3a70963a844
3 78eb5ae85955cc84 aa47a297e70bcf06 f82e49ee28179195108899785e5fd025
4 \N 3388adf014ef9cac 16e4b1fd3de2856113bb07b80c439a29
全局区块表归档
bicewow@192.168.20.253 [db1]=>SELECT pg_catalog.ledger_gchain_archive();
ledger_gchain_archive
-----------------------
t
(1 row)
bicewow@192.168.20.253 [db1]=>\x
Expanded display is on.
bicewow@192.168.20.253 [db1]=>SELECT * FROM gs_global_chain;
-[ RECORD 1 ]--------------------------------
blocknum | 2
dbname | db1
username | bicewow
starttime | 2024-11-04 15:26:07.446804+08
relid | 16413
relnsp | scm_for_book
relname | tab_01
relhash | 088b289a3d525e54
globalhash | 0897b36eccc3d21dbe03849d2e5e2d1e
txcommand | Archived. <===========
归档之后,在本地目录生成hist_bak目录,里面详细记录了全局区块表的归档信息。
[root@dmdem hist_bak]# cd ..
[root@dmdem dn_6001]# ll
total 26092
-rw------- 1 omm dbgrp 426438 Oct 30 23:59 0_adt
-rw------- 1 omm dbgrp 1691986 Oct 31 23:59 1_adt
-rw------- 1 omm dbgrp 2073663 Nov 1 23:59 2_adt
-rw------- 1 omm dbgrp 2073653 Nov 2 23:59 3_adt
-rw------- 1 omm dbgrp 2072253 Nov 3 23:59 4_adt
-rw------- 1 omm dbgrp 1487543 Nov 4 17:06 5_adt
drwx------ 2 omm dbgrp 6 Oct 30 16:47 done
drwx------ 2 omm dbgrp 106 Nov 4 17:05 hist_bak
-rw------- 1 omm dbgrp 16777456 Nov 4 00:00 index_table_new
[root@dmdem dn_6001]# cd hist_bak/
[root@dmdem hist_bak]# ll
total 8
-rw------- 1 omm dbgrp 555 Nov 4 17:05 gs_global_chain_20241104170551.bak
-rw------- 1 omm dbgrp 289 Nov 4 17:02 scm_for_book_tab_01_hist_16413_20241104170211.hist
[root@dmdem hist_bak]# more gs_global_chain_20241104170551.bak
0 db1 bicewow 2024-11-04 15:26:07.446804+08 16413 scm_for_book tab_01 6d701e39dff7fd82 bc57c39b0791ab44631580a81eddeb78 INSERT INTO scm_for_book.tab_01 VALUES(1, 'zhan
gliang'), (2, 'xiaohe'), (3, 'liubang');
1 db1 bicewow 2024-11-04 15:26:57.577345+08 16413 scm_for_book tab_01 cea3b8507249fd7e 19c88f9ab09b33126be1dfd08c3e68b7 UPDATE scm_for_book.tab_01 SET name = 'hanxin'
WHERE id = 2;
2 db1 bicewow 2024-11-04 15:27:32.590788+08 16413 scm_for_book tab_01 cc77520feb106354 d4af6e190b7d3c4ff2a590f238e258d9 DELETE FROM scm_for_book.tab_01 WHERE id = 3;
修复账本数据库
当在异常情况或表被损坏时需要使用账本数据库中的ledger_gchain_repair(text, text)或ledger_hist_repair(text, text)接口对全局区块表或用户历史表进行修复,修复后调用全局区块表或用户历史表校验接口结果为true。
关于历史表和全局区块表的修复,操作较少,后续可以在研究下具体的实现。
历史表修复操作
bicewow@192.168.20.253 [db1]=>SELECT pg_catalog.ledger_hist_repair('scm_for_book', 'tab_01');
ledger_hist_repair
--------------------
0000000000000000
(1 row)
该结果表明当前节点用户历史表修复成功。
全局区块表修复操作
bicewow@192.168.20.253 [db1]=>SELECT pg_catalog.ledger_gchain_repair('scm_for_book', 'tab_01');
ledger_gchain_repair
----------------------
088b289a3d525e54
(1 row)
该结果表明,全局区块表修复成功。
总结
账本数据库采用区块表作为历史表,防止历史操作被修改的可能,对于类似财务这种业务,利用账本数据库的这种功能,可在数据库级别多一层历史操作回溯的手段(不涉及查询操作),而且配置相对简单,对业务透明,相信在未来的业务场景中,将会有更多实用的地方。




