区块链表是一个仅插入的防篡改表,可以设置表级别或行级别的保留时间。行数据被组织到链中,每行数据包含一个hash算值,一个前一行数据的hash算值。
这个特性已经在19.10被后台引入,不过扔需要patch 32431413 ,并设定COMPATIBLE
参数到19.10.0或之后。从19.11开始,就不需要额外的补丁了。
在学习区块链表的时候,小心不要设置太长的保留时间,否则你会要等待很长的时间来删除测试表
- 创建区块链表
- 修改区块链表
- 阻塞dml和ddl操作
- DBMS_BLOCKCHAIN_TABLE包
- 一些思考
创建区块链表
在create table命令那里添加blockchain关键字,这里有3种区块链选项
no drop选项指定保护表不能被drop。如果表中没有数据,那仍然可以drop表,不像区块链表的初始版本(19.10和21.3),no drop选项同样防止了表被命令drop table ... cascade级联删除。
NO DROP [ UNTIL number DAYS IDLE ]
NO DROP
: 这个表不能被drop,在测试时请小心NO DROP UNTIL number DAYS IDLE
: 这个表不能被drop,直到指定时间内没有新的数据行插入。你可能在测试时喜欢用0或者1天。
NO DELETE
选项指定保留的时间。每行数据会被保护多长时间不被删除。
NO DELETE { [ LOCKED ] | (UNTIL number DAYS AFTER INSERT [ LOCKED ]) }
NO DELETE
:每行永久保留。没有LOCKED关键字的含有了这个表可以被alter table命令修改设置的意思。修改的保留时间只能更长。NO DELETE LOCKED
: 与NO DELETE相同。
NO DELETE UNTIL number DAYS AFTER INSERT
: 每行保护指定的保留时间避免删除,不过这个设置可以被alter table命令增加。最小值为16天。NO DELETE UNTIL number DAYS AFTER INSERT LOCKED
: 每行保护指定的保留时间避免删除,而且这个设置不能被alter table命令修改。最小值为16天。
--碎碎念:这里可以看19c的sql language reference的create table子节的blockchain_row_retention_clause部分
快链 hash 和数据格式选项在当前版本中固定。它看起来会在将来的版本中允许修改hash算法。
HASHING USING sha2_512 VERSION v1
把所有选项放一起的语句像是下面:
--drop table bct_t1 purge;
create blockchain table bct_t1 (
id number,
fruit varchar2(20),
quantity number,
created_date date,
constraint bct_t1_pk primary key (id)
)
no drop until 0 days idle
no delete until 16 days after insert
hashing using "SHA2_512" version "v1";
检查 USER_TAB_COLS
视图会显示若干不可见的列被加入到列表中。这些隐藏列描述如下。
set linesize 120 pagesize 50
column column_name format a30
column data_type format a27
column hidden_column format a13
select internal_column_id,
column_name,
data_type,
data_length,
hidden_column
FROM user_tab_cols
WHERE table_name = 'BCT_T1'
ORDER BY internal_column_id;
INTERNAL_COLUMN_ID COLUMN_NAME DATA_TYPE DATA_LENGTH HIDDEN_COLUMN
------------------ ------------------------------ --------------------------- ----------- -------------
1 ID NUMBER 22 NO
2 FRUIT VARCHAR2 25 NO
3 QUANTITY NUMBER 22 NO
4 CREATED_DATE DATE 7 NO
5 ORABCTAB_INST_ID$ NUMBER 22 YES
6 ORABCTAB_CHAIN_ID$ NUMBER 22 YES
7 ORABCTAB_SEQ_NUM$ NUMBER 22 YES
8 ORABCTAB_CREATION_TIME$ TIMESTAMP(6) WITH TIME ZONE 13 YES
9 ORABCTAB_USER_NUMBER$ NUMBER 22 YES
10 ORABCTAB_HASH$ RAW 2000 YES
11 ORABCTAB_SIGNATURE$ RAW 2000 YES
12 ORABCTAB_SIGNATURE_ALG$ NUMBER 22 YES
13 ORABCTAB_SIGNATURE_CERT$ RAW 16 YES
14 ORABCTAB_SPARE$ RAW 2000 YES
14 rows selected.
SQL>
{CDB|DBA|ALL|USER}_BLOCKCHAIN_TABLES
视图展示区块链表的信息。这些视图都基于基表SYS.BLOCKCHAIN_TABLE$。
column row_retention format a13
column row_retention_locked format a20
column table_inactivity_retention format a26
column hash_algorithm format a14
SELECT row_retention,
row_retention_locked,
table_inactivity_retention,
hash_algorithm
FROM user_blockchain_tables
WHERE table_name = 'BCT_T1';
ROW_RETENTION ROW_RETENTION_LOCKED TABLE_INACTIVITY_RETENTION HASH_ALGORITHM
------------- -------------------- -------------------------- --------------
16 NO 0 SHA2_512
SQL>
修改区块链表
文档中说 NO DROP
选项可以被 ALTER TABLE
命令修改只要保留的时间不减少。但是当表初始化创建时指定NO DROP UNTIL 0 DAYS IDLE
就不有效了,所有指定的值都会被返回错误。 我们现在有一张保留时间为0天的表,在下面的例子中我们尝试去修改它的保留时间到100天,然后它报错了。这个命令是格式上正确,所以我猜测是一个当前版本的bug。这个bug在19.12依然存在,不过在21.3被修复了。
alter table bct_t1 no drop until 100 days idle;
Error report -
ORA-05732: retention value cannot be lowered
SQL>
这个命令在 NO DROP UNTIL 1 DAYS IDLE
或更高的时候正常。.
无论当前的延迟删除设置,尝试修改NO DROP到最大都会导致ORA-00600的错误。这个bug在19.12依然存在,不过在21.3被修复了。
alter table bct_t1 no drop;
Error starting at line : 1 in command -
alter table bct_t1 no drop
Error report -
ORA-00600: internal error code, arguments: [atbbctable_1], [0], [], [], [], [], [], [], [], [], [], []
这个问题,我估计大部分人是想安全的使用,先设置一个初始值0天,一旦他们喜欢这个设置,再增加保留时间。试用时直接使用NO DROP
看起来是个十分冒险,因为唯一移除这张表的办法就是删除整个schema。
假设它没有被设定为locked。NO DELETE
选项的时间可以被用ALTER TABLE
命令增加。我们当前是一行保留16天。在下面的例子中,我们将时间设置到32天。随后我们在尝试缩小到16天,他会报错。
-- Increase to 32 days.
alter table bct_t1 no delete until 32 days after insert;
Table BCT_T1 altered.
SQL>
-- Decrease to 16 days (fail).
alter table bct_t1 no delete until 16 days after insert;
Error report -
ORA-05732: retention value cannot be lowered
SQL>
在当前的版本中,尝试设置行保留时间到 NO DELETE
即增加保留时间会导致ORA-00600
错误。我人为这是一个当前版本的bug。这个bug在19.12依然存在,不过在21.3被修复了。
alter table bct_t1 no delete;
Error report -
ORA-00600: internal error code, arguments: [atbbctable_1], [0], [], [], [], [], [], [], [], [], [], []
阻止dml和ddl操作
就跟我们想要的仅插入表一样,区块链表中所有DML和DDL操作,导致都行修正或删除的都会被阻止。
下面就是一个成功插入的例子,和一些不成功的DML语句。
-- INSERT
insert into bct_t1 (id, fruit, quantity, created_date ) values (1, 'apple', 20, sysdate);
1 row inserted.
SQL> commit;
Commit complete.
SQL>
-- UPDATE
update bct_t1 set quantity = 10 where id = 1;
Error report -
SQL Error: ORA-05715: operation not allowed on the blockchain table
SQL>
-- DELETE
delete from bct_t1 where id = 1;
Error report -
SQL Error: ORA-05715: operation not allowed on the blockchain table
SQL>
一些DDL语句可以修改数据内容的也会被阻止。这里有一个truncate语句的例子。
truncate table bct_t1;
Error report -
ORA-05715: operation not allowed on the blockchain table
SQL>
扩容已有列的长度是被允许的,不过增加新列或删除已有的列则不被允许。
-- Extend column.
alter table bct_t1 modify (fruit varchar2(25));
Table BCT_T1 altered.
SQL>
-- Add column
alter table bct_t1 add (additional_info varchar2(50));
Error report -
ORA-05715: operation not allowed on the blockchain table
SQL>
-- Drop column.
alter table bct_t1 drop column quantity;
Error report -
ORA-05715: operation not allowed on the blockchain table
SQL>
DBMS_BLOCKCHAIN_TABLE包
DBMS_BLOCKCHAIN_TABLE
包用于维护区块链表。
DELETE_EXPIRED_ROWS
过程删除超出保留时间的行。他们不能被delete语句删除。
set serveroutput on
declare
l_rows number;
begin
dbms_blockchain_table.delete_expired_rows(
schema_name => 'admin',
table_name => 'bct_t1',
before_timestamp => null,
number_of_rows_deleted => l_rows);
dbms_output.put_line('Rows Deleted=' || l_rows);
end;
/
Rows Deleted=0
PL/SQL procedure successfully completed.
SQL>
我们也可以限制删除的日期。数据行只会删除保留时间外的,符合时间标准的。
set serveroutput on
declare
l_rows number;
begin
dbms_blockchain_table.delete_expired_rows(
schema_name => 'testuser1',
table_name => 'it_t1',
before_timestamp => systimestamp - 60,
number_of_rows_deleted => l_rows);
dbms_output.put_line('Rows Deleted=' || l_rows);
end;
/
Rows Deleted=0
PL/SQL procedure successfully completed.
SQL>
VERIFY_ROWS
procedure过程检查行数据和它的hash是否一致,然后签字确认。
set serveroutput on
declare
l_rows number;
l_verified number;
begin
select count(*)
into l_rows
from admin.bct_t1;
dbms_blockchain_table.verify_rows(
schema_name => 'admin',
table_name => 'bct_t1',
number_of_rows_verified => l_verified);
dbms_output.put_line('Rows=' || l_rows || ' Verified Rows=' || l_verified);
end;
/
Rows=1 Verified Rows=1
PL/SQL procedure successfully completed.
SQL>
一些思考
当你使用区块链表,这里有很多的事情需要考虑。
- 在21.3版本前实现的版本的感觉有些。甚至在19.12的版本都在19c中有一些bug。 文档里说的有一些特性不能用,输出错误信息不准确,或捕获的不合适。这里还有一些特性完全不能用,我就没有在这篇文章里介绍。有一个例子,导致内存失败只能用重启实例修复。我会继续回看下面几个版本,看看他是否被修复,然后直接更新在本篇文章里。
- 区块链表比传统的表要慢,因为它有额外的工作要做。
- 区块链表可以用常规语法被索引和分区。
- 这里有一些数据库泵的限制,详情见 here.
- 这里有很多常规的区块链表限制,详情见 here.
- Oracle建议在数据库外保存每个表当前的hash值和对应的序列值。这样可以允许你对比你的记录作为额外保险。
- 在dataguard环境中,oracle建议在使用区块链表时使用最大保护模式或者最高可用模式。
- 用户认证可以被添加到数据中,用
DBMS_USER_CERTS包的ADD_CERTIFICATE
过程添加认证,然后再现有行里使用DBMS_BLOCKCHAIN_TABLE
包SIGN_ROW
过程 进行认证。不过这个特性当下看起来没有效果。
我以为关键的问题在于,为什么要用区块链表
- 如果你需要一个仅插入的防篡改表,这个会是一个解决方案。
- 如果你想以集中的方式利用与区块链相关的信任,而不是让多个客户端应用程序单独管理区块链。使用区块链表可以集中这种信任。
- 您可以将与区块链相关的信任添加到现有的应用程序中,而不必担心重新编码。
翻译员碎碎念:这个特性起名就很有蹭热度的意思,区块链的分权思想没有看到,更多是以保护区块链数据为目的的防篡改设计。