Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Der schnellste Weg, um den Hash einer ganzen Tabelle zu berechnen

Zuallererst denke ich, dass der Weg, "unseriöse Administratoren" anzusprechen, eine Kombination aus dem Audit-Trail von Oracle ist und Datenbanktresor Funktionen.

Das heißt, hier ist, was ich versuchen könnte:

1) Erstellen Sie eine benutzerdefinierte ODCI-Aggregatfunktion, um einen Hash aus mehreren Zeilen als Aggregat zu berechnen. 2) Erstellen Sie ein VIRTUAL NOT NULL Spalte in der Tabelle, die ein SHA-Hash aller Spalten in der Tabelle war – oder aller, die Sie schützen möchten. Sie würden dies die ganze Zeit über behalten – im Grunde tauschen Sie einige insert/update/delete aus Leistung im Austausch, um Hashes schneller berechnen zu können.3) Erstellen Sie einen nicht eindeutigen Index für diese virtuelle Spalte4) SELECT my_aggregate_hash_function(virtual_hash_column) FROM my_table um die Ergebnisse zu erhalten.

Hier ist der Code:

Erstellen Sie eine Aggregatfunktion, um einen SHA-Hash über eine Reihe von Zeilen zu berechnen

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;
/

Erstellen Sie eine Testtabelle, mit der Sie arbeiten können (Sie überspringen dies, da Sie Ihre echte Tabelle haben)

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

Erstellen Sie einen virtuellen Spalten-Hash der Daten jeder Zeile. Stellen Sie sicher, dass es NOT NULL ist

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 ;

Erstellen Sie einen Index für die virtuelle Spalte; Auf diese Weise können Sie Ihren Hash mit einem vollständigen Scan des Narrow-Index anstelle eines vollständigen Scans der Fat-Tabelle berechnen

create index msi_compliance_hash_n1 on mattmsi (compliance_hash);  

Fügen Sie alles zusammen, um Ihren Hash zu berechnen

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

Ein paar Anmerkungen:

  1. Ich denke, es ist wichtig, einen Hash zu verwenden, um das Aggregat zu berechnen (anstatt nur eine SUM() zu machen über die Hashes auf Zeilenebene, da ein Angreifer sehr leicht die richtige Summe fälschen könnte.
  2. Ich glaube nicht, dass Sie (einfach?) eine parallele Abfrage verwenden können weil es wichtig ist, dass die Zeilen der Aggregatfunktion in einer konsistenten Reihenfolge zugeführt werden, sonst ändert sich der Hash-Wert.