计算整个表的哈希的最快方法

时间:2015-11-20 10:39:58

标签: sql oracle plsql database-performance oracle12c

我们需要能够为外部环境计算表哈希,并将其与内部环境中的预先计算哈希进行比较。这样做的目的是确保外部环境中的数据不被“流氓”数据库管理员篡改。 用户坚持使用此功能

目前,我们通过计算每个列值的各个哈希值来执行此操作,对列哈希执行bit-xor以获取行哈希,然后对所有行哈希执行bit-xor以得出表哈希。下面的伪脚本:

cursor hash_cur is
select /*+ PARALLEL(4)*/ dbms_crypto.mac(column1_in_raw_type, HMAC_SH512, string_to_raw('COLUMN1_NAME')) as COLUMN1_NAME
       ...
from TABLE_NAME;

open hash_cur;
fetch hash_cur bulk collect into hashes;
close hash_cur;

for i in 1..hashes.count
loop
  rec := hashes(i);
  record_xor = rec.COLUMN1;
  record_xor = bit_xor(record_xor, rec.COLUMN2);
  ...
  record_xor = bit_xor(record_xor, rec.COLUMNN);

  table_xor = bit_xor(table_xor, record_xor);
end loop;

上面的伪脚本将使用dbms_job并行运行。

问题在于我们为某些表提供了数TB的数据,目前性能无法满足我们想要达到的性能。哈希必须“在运行中”完成,因为用户希望自己执行哈希检查。

  1. 您是否有更好的方法来执行整表散列,或者基本上比较来自不同环境的表,这些表通过低延迟和相对低带宽的网络连接?
  2. 在我看来,操作比I / O绑定更多的CPU绑定。我正在考虑将表数据存储在blob中,其中数据按记录正确排列,然后按列排列。然后在输出文件上执行哈希。这应该使操作完全受I / O限制。

    1. 最快的方法是什么?无论如何要在查询的select子句中执行此操作以删除任何开销的PL / SQL到SQL引擎上下文切换?
      • 我正在考虑修改此
      • 的全局blob
      • 还要删除批量收集结果的I / O开销。
    2. 任何可以让我获得更好表现的脚本的建议都将不胜感激。感谢。

2 个答案:

答案 0 :(得分:3)

首先,我认为接近"流氓管理员的方式"与Oracle的审计跟踪 Database Vault 功能相结合。

那就是说,我可以尝试一下:

1)创建自定义ODCI聚合函数以计算多行的散列作为聚合。 2)在表格上创建一个VIRTUAL NOT NULL列,该列是表中所有列的SHA哈希值 - 或者您关心保护的所有列。您始终保持这种状态 - 基本上可以换掉一些insert/update/delete性能,以便能够更快地计算哈希值。 3)在该虚拟列上创建一个非唯一索引 4)SELECT my_aggregate_hash_function(virtual_hash_column) FROM my_table得到结果。

此处的代码:

创建一个聚合函数来计算一堆行上的SHA哈希

CREATE OR REPLACE TYPE matt_hash_aggregate_impl AS OBJECT
(
  hash_value RAW(32000),
  CONSTRUCTOR FUNCTION matt_hash_aggregate_impl(SELF IN OUT NOCOPY matt_hash_aggregate_impl ) RETURN SELF AS RESULT,  
-- Called to initialize a new aggregation context
-- For analytic functions, the aggregation context of the *previous* window is passed in, so we only need to adjust as needed instead 
-- of creating the new aggregation context from scratch
  STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_hash_aggregate_impl) RETURN NUMBER,
-- Called when a new data point is added to an aggregation context  
  MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_hash_aggregate_impl, value IN raw ) RETURN NUMBER,
-- Called to return the computed aggragate from an aggregation context
  MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_hash_aggregate_impl, returnValue OUT raw, flags IN NUMBER) RETURN NUMBER,
-- Called to merge to two aggregation contexts into one (e.g., merging results of parallel slaves) 
  MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_hash_aggregate_impl, ctx2 IN matt_hash_aggregate_impl) RETURN NUMBER,
  -- ODCIAggregateDelete
  MEMBER FUNCTION ODCIAggregateDelete(self IN OUT matt_hash_aggregate_impl, value raw) RETURN NUMBER  
);

/

CREATE OR REPLACE TYPE BODY matt_hash_aggregate_impl IS

CONSTRUCTOR FUNCTION matt_hash_aggregate_impl(SELF IN OUT NOCOPY matt_hash_aggregate_impl ) RETURN SELF AS RESULT IS
BEGIN
  SELF.hash_value := null;
  RETURN;
END;


STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_hash_aggregate_impl) RETURN NUMBER IS
BEGIN
  sctx := matt_hash_aggregate_impl ();
  RETURN ODCIConst.Success;
END;


MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_hash_aggregate_impl, value IN raw ) RETURN NUMBER IS
BEGIN
  IF self.hash_value IS NULL THEN
    self.hash_value := dbms_crypto.hash(value, dbms_crypto.hash_sh1);
  ELSE 
      self.hash_value := dbms_crypto.hash(self.hash_value || value, dbms_crypto.hash_sh1);
  END IF;
  RETURN ODCIConst.Success;
END;

MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_hash_aggregate_impl, returnValue OUT raw, flags IN NUMBER) RETURN NUMBER IS
BEGIN
  returnValue := dbms_crypto.hash(self.hash_value,dbms_crypto.hash_sh1);
  RETURN ODCIConst.Success;
END;

MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_hash_aggregate_impl, ctx2 IN matt_hash_aggregate_impl) RETURN NUMBER IS
BEGIN
    self.hash_value := dbms_crypto.hash(self.hash_value || ctx2.hash_value, dbms_crypto.hash_sh1);
  RETURN ODCIConst.Success;
END;

-- ODCIAggregateDelete
MEMBER FUNCTION ODCIAggregateDelete(self IN OUT matt_hash_aggregate_impl, value raw) RETURN NUMBER IS
BEGIN
  raise_application_error(-20001, 'Invalid operation -- hash aggregate function does not support windowing!');
END;  

END;
/

CREATE OR REPLACE FUNCTION matt_hash_aggregate ( input raw) RETURN raw
PARALLEL_ENABLE AGGREGATE USING matt_hash_aggregate_impl;
/

创建一个可以使用的测试表(跳过这个,因为你有真正的表)

create table mattmsi as select * from mtl_system_items where rownum <= 200000;

创建每行数据的虚拟列哈希。确保它是NOT NULL

alter table mattmsi add compliance_hash generated always as ( dbms_crypto.hash(to_clob(inventory_item_id || segment1 || last_update_date || created_by || description), 3 /*dbms_crypto.hash_sh1*/) ) VIRTUAL not null ;

在虚拟列上创建索引;这样,您可以使用窄索引的完整扫描而不是胖表的完整扫描来计算哈希值

create index msi_compliance_hash_n1 on mattmsi (compliance_hash);  

将它们放在一起计算你的哈希值

SELECT matt_hash_aggregate(compliance_hash) from (select compliance_hash from mattmsi order by compliance_hash);

一些评论:

  1. 我认为使用哈希计算聚合很重要 (而不仅仅是在行级哈希上执行SUM(), 因为攻击者可以很容易地伪造正确的金额。
  2. 我认为你不能(轻松?)使用并行查询,因为它是 重要的是行被输入到a中的聚合函数 一致的顺序,否则哈希值会改变。

答案 1 :(得分:0)

您可以使用ORA_HASH并将表达式传递给几列

select sum(ORA_HASH(col1||col2||col3)) as hash from my_table

但是在这里,在AskTom上,有类似的讨论为什么它不是一个好方法:Creating a unique HASH value for the contents of a table