必须一提的是,测试之后,我了解到停止应用程序运行绝对不是没有理由的。但下一个问题是,生产应用程序需要脱机多久?在计划每个周末进行的正常两小时的维护时间之内能够对列进行加密吗?或者,需要更长的停机时间?为了弄清这个问题,我只需测量在沙箱环境中对列进行加密所花费的时间,因为沙箱环境与生产环境具有相同的服务器硬件和数据集。我发现,列加密要花费一个小时多一点的时间才能完成。坦白地说,由于我使用类似数据在笔记本电脑上模拟测试加密运行才花费了不到 5 分钟的时间,因此对于它花费这么长时间,我感到非常震惊。但是当我们在生产数据库系统中对列进行加密时,最要紧的是要使用陈旧服务器硬件所发生的情况。
了解到在正常维护时间内执行其他任务需要更多时间,我决定必须找到减少加密列花费时间的方法。我的第一个直觉就是删除包含目标列的两个索引。这样,Oracle 只需加密表本身中的列数据,之后我可以有效地重建索引,而没有日志记录开销。经过一些新的测试之后,我将加密列以及相关索引所需的时间从 70 分钟(在加密期间存在索引)减少到仅 20 分钟(加密列后重建索引)。列表 2 是我用来得出结论的测试示例(从我们在列表 1 中停止的位置继续)。此外,请注意,列表中的时间来自用来编写本文的测试系统,而不是来自我的客户端使用的实际系统。
SQL> -- Remove existing synthetic data
SQL> TRUNCATE TABLE app_001.transactions;
Table truncated.
SQL> -- Disable encryption of the credit card column
SQL> ALTER TABLE app_001.transactions
2 MODIFY (credit_card DECRYPT);
Table altered.
SQL> -- Load new synthetic data
SQL> BEGIN
2 -- AMEX
3 FOR i IN 1 .. 100000 LOOP
4 INSERT INTO app_001.transactions(trans_id, credit_card)
5 VALUES (
6 i,
7 '34' || TRUNC(DBMS_RANDOM.VALUE(low=>0, high=>99999999999999))
8 );
9 END LOOP;
10 COMMIT;
11 -- VISA
12 FOR i IN 100001 .. 400000 LOOP
13 INSERT INTO app_001.transactions(trans_id, credit_card)
14 VALUES (
15 i,
16 '4' || TRUNC(DBMS_RANDOM.VALUE(low=>0, high=>999999999999999))
17 );
18 END LOOP;
19 COMMIT;
20 -- MASTERCARD
21 FOR i IN 400001 .. 500000 LOOP
22 INSERT INTO app_001.transactions(trans_id, credit_card)
23 VALUES (
24 i,
25 '54' || TRUNC(DBMS_RANDOM.VALUE(low=>0, high=>99999999999999))
26 );
27 END LOOP;
28 COMMIT;
29 END;
30 /
PL/SQL procedure successfully completed.
SQL> -- Time how long it takes to encrypt credit card data
SQL> -- with corresponding indexes in place
SQL> SET TIMING ON;
SQL> ALTER TABLE app_001.transactions
2 MODIFY (credit_card ENCRYPT NO SALT);
Table altered.
Elapsed: 00:02:27.18
SQL> SET TIMING OFF;
SQL> -- Remove existing synthetic data
SQL> TRUNCATE TABLE app_001.transactions;
Table truncated.
SQL> -- Drop all indexes that correspond to the credit card column
SQL> DROP INDEX app_001.transactions_ndx1;
Index dropped.
SQL> -- Disable encryption of the credit card column
SQL> ALTER TABLE app_001.transactions
2 MODIFY (credit_card DECRYPT);
Table altered.
SQL> -- Load new synthetic data
SQL> BEGIN
2 -- AMEX
3 FOR i IN 1 .. 100000 LOOP
4 INSERT INTO app_001.transactions(trans_id, credit_card)
5 VALUES (
6 i,
7 '34' || TRUNC(DBMS_RANDOM.VALUE(low=>0, high=>99999999999999))
8 );
9 END LOOP;
10 COMMIT;
11 -- VISA
12 FOR i IN 100001 .. 400000 LOOP
13 INSERT INTO app_001.transactions(trans_id, credit_card)
14 VALUES (
15 i,
16 '4' || TRUNC(DBMS_RANDOM.VALUE(low=>0, high=>999999999999999))
17 );
18 END LOOP;
19 COMMIT;
20 -- MASTERCARD
21 FOR i IN 400001 .. 500000 LOOP
22 INSERT INTO app_001.transactions(trans_id, credit_card)
23 VALUES (
24 i,
25 '54' || TRUNC(DBMS_RANDOM.VALUE(low=>0, high=>99999999999999))
26 );
27 END LOOP;
28 COMMIT;
29 END;
30 /
PL/SQL procedure successfully completed.
SQL> -- Time how long it takes to:
SQL> -- 1. Encrypt credit card data without corresponding indexes in place
SQL> -- 2. Recreate corresponding indexes
SQL> SET TIMING ON;
SQL> ALTER TABLE app_001.transactions
2 MODIFY (credit_card ENCRYPT NO SALT);
Table altered.
Elapsed: 00:01:15.48
SQL> CREATE INDEX app_001.transactions_ndx1
2 ON app_001.transactions(credit_card)
3 TABLESPACE indx_001
4 PARALLEL 2
5 NOLOGGING;
Index created.
Elapsed: 00:00:02.98
SQL> SET TIMING OFF;
列表 2要快速执行对现有数据进行加密的过程,只需在对其进行加密之前删除列的底层索引,然后再重建索引。
注:本文的模拟环境中使用了 CREATE INDEX 语句。在实际的设置中,可考虑使用 Oracle 数据库的 DBMS_METADATA 实用程序包来生成 CREATE INDEX 语句,您可以使用这些语句在完成数据加密之后重新创建索引。
总之,在列加密之后重建索引的新策略可留出更多时间来处理整个过程中最具挑战性的问题,这将在下一部分中进行说明。