簡體   English   中英

Oracle 數據遷移【數據修改】-數據調優

[英]Oracle Data Migration [data modification] - Data Tuning

我面臨數據遷移,我的目標是在不到 8 小時內更新 2.5M 行,這是因為客戶可以停用服務的時間有限 window。 此外,在執行期間無法鎖定該表,因為它被其他程序使用,我只能鎖定記錄。 執行將通過批處理完成。 可能在這種情況下遷移不是正確的詞,最好說“改變數據”......

系統:Oracle 11g

表信息

表名:Tab1 總行數:520.000.000 AVG 行長度:57

DESC Tab1;
Name             Null?    Type
---------------- -------- -----------
t_id             NOT NULL NUMBER
t_fk1_id                  NUMBER
t_fk2_id                  NUMBER
t_start_date     NOT NULL DATE
t_end_date                DATE
t_del_flag       NOT NULL NUMBER(1)
t_flag1          NOT NULL NUMBER(1)
f_falg2          NOT NULL NUMBER(1)
t_creation_date           DATE
t_creation_user           NUMBER(10)
t_last_update             DATE
t_user_update             NUMBER(10)
t_flag3                   NUMBER(1)

索引是:

  1. T_ID_PK [t_id] UNIQUE
  2. T_IN_1 [t_fk2_id,t_fk1_id,t_start_date,t_del_flag] NONUNIQUE
  3. T_IN_2 [t_last_update,t_fk2_id] NONUNIQUE
  4. T_IN_3 [t_fk2_id,t_fk1_id] NONUNIQUE

目前我已經想到了一些可能的解決方案,其中大部分我已經測試過了:

  1. 插入+刪除:選擇現有數據,插入需要修改的新記錄,刪除舊記錄[這個結果是最慢的方法~21h]
  2. 合並:使用合並命令更新現有數據[這個結果是最快的方法~16h]
  3. 更新:更新現有數據[~18h]

使用上述解決方案,我遇到了一些問題,例如:如果使用 /*+ parallel(x) / 選項執行表被鎖定,則 / + RESULT_CACHE */ 似乎不會影響所有選擇時間。 我最后的想法是用一個新列對表進行分區,並使用它來避免表鎖定並繼續解決方案 1。


這里用於合並選項的查詢(對於其他兩個或多或少是相同的):

DECLARE
v_recordset NUMBER;
v_row_count NUMBER;
v_start_subset NUMBER;
v_tot_loops NUMBER;
BEGIN
--set the values manually for example purpose, I've use the same values
v_recordset := 10000;
v_tot_loops := 10000;
  BEGIN
    SELECT NVL(MIN(MOD(m_id,v_recordset)), 99999)
    INTO v_start_subset 
    FROM MIGRATION_TABLE
    WHERE m_status = 0; -- 0=not migrated , 1=migrated
  END;
  FOR v_n_subset IN v_start_subset..v_tot_loops
    LOOP
      BEGIN
        MERGE INTO Tab1 T1
        USING (
          SELECT m.m_new_id, c2.c_id, t.t_id
          FROM MIGRATION_TABLE m
            JOIN Tab1 t ON t.t_fk_id = m.m_old_id
            JOIN ChildTable c ON c.c_id = t.t_fk2_id
            JOIN ChildTable c2 ON c.c_name = c2.c_name --c_name is an UNIQUE index of ChildTable
          WHERE MOD(m.m_id,v_recordset) = v_n_subset 
            AND c.c_fk_id = old_product_id --value obtained from another subsystem
            AND c2.c_fk_id = new_product_id --value obtained from another subsystem
            AND t.t_del_flag = 0 --not deleted items
        ) T2
        ON (T1.t_id = T2.t_id)
        WHEN MATCHED THEN
          UPDATE T1.t_fk_id = T2.m_new_id, T1.t_fk2_id = T2.c_id, T1.t_last_update = trunc(sysdate)
        ;
        --Update the record as migrated and proceed
        COMMIT;
      EXCEPTION WHEN OTHERS THEN
        ROLLBACK;
      END;
  END LOOP;
END;

在上面的腳本中,我刪除了並行和緩存選項,但我已經對這兩個選項進行了測試,但沒有獲得任何錯誤結果。

任何人,拜托,你們能幫我解決這個問題嗎,在一個多星期內我無法達到預期的時間? 有任何想法嗎?


遷移表

CREATE TABLE MIGRATION_TABLE(
 m_customer_from VARCHAR2(5 BYTE),
 m_customer_to VARCHAR2(5 BYTE),
 m_old_id NUMBER(10,0) NOT NULL,
 m_new_id NUMBER(10,0) NOT NULL,
 m_status VARCHAR2(100 BYTE),
 CONSTRAINT M_MIG_PK_1
 (
  m_old_id 
 )
 ENABLE
)
CREATE UNIQUE INDEX M_MIG_PK_1 ON MIGRATION_TABLE (m_old_id ASC)

子表

CREATE TABLE ChildTable(
 c_id NUMBER(10, 0) NOTE NULL,
 c_fk_id NUMBER(10, 0),
 c_name VARCHAR2(100 BYTE),
 c_date DATE,
 c_note VARCHAR2(100 BYTE),
 CONSTRAINT C_CT_PK_1
 (
  c_id
 )
 ENABLE
)
CREATE UNIQUE INDEX C_CT_PK_1 ON ChildTable (c_id ASC)
CREATE UNIQUE INDEX C_CT_PK_2 ON ChildTable (c_name ASC, c_fk_id ASC)

方法 2 與方法 1 類似,但它使用 ROWID 而不是主鍵。 從理論上講,它應該因此更快一些。

CREATE TABLE migration_temp NOLOGGING AS
SELECT t.t_id, 
       t.rowid    AS rid,
       m.m_new_id AS new_fk1_id, 
       c2.c_id    AS new_fk2_id 
  FROM MIGRATION_TABLE m
  JOIN Tab1 t        ON t.t_fk1_id = m.m_old_id
  JOIN ChildTable c1 ON c1.c_id = t.t_fk2_id
  JOIN ChildTable c2 ON c1.c_name = c2.c_name
 WHERE t.t_del_flag = 0
 ORDER BY t.rowid;
EXEC DBMS_STATS.GATHER_TABLE_STATS(null,'migration_temp');

MERGE INTO Tab1 t USING migration_temp m ON (t.rowid = m.rid)
 WHEN MATCHED THEN UPDATE SET 
      t.t_fk1_id = m.new_fk1_id,
      t.t_fk2_id = m.new_fk2_id,
      t.t_last_update = trunc(sysdate);

您可以考慮基於 ROWID 塊對 MERGE 進行批處理。 那些在邏輯上往往是並置的,因此它應該更快一些。

哇,5.2 億行,但是。 更新其中的 250 萬只是 0.5%。 那應該是可行的,不知道你的數據。 我的第一個假設是 MERGE 內部 Tab1 x Tab1 的自連接占用了大部分時間。 可能還有許多連接到 migration- 和 child_tables,索引 T_IN_1、2 和 3 需要維護。 也。

正如您所說要更新的行是固定的,我會嘗試准備繁重的工作。 這不會鎖定表,也不會計入停機時間:

CREATE TABLE migration_temp NOLOGGING AS
SELECT t.t_id, 
       m.m_new_id AS new_fk1_id, 
       c2.c_id    AS new_fk2_id 
  FROM MIGRATION_TABLE m
  JOIN Tab1 t        ON t.t_fk1_id = m.m_old_id
  JOIN ChildTable c1 ON c1.c_id = t.t_fk2_id
  JOIN ChildTable c2 ON c1.c_name = c2.c_name
 WHERE t.t_del_flag = 0;

我省略了舊/新 product_ids 的位,因為我不完全理解它應該如何工作,但希望這不是問題。

方法 1 是通過主鍵連接:

ALTER TABLE migration_temp ADD CONSTRAINT pk_migration_temp PRIMARY KEY(t_id);
EXEC DBMS_STATS.GATHER_TABLE_STATS(null,'migration_temp');

MERGE INTO Tab1 t USING migration_temp m ON (t.t_id = m.t_id)
 WHEN MATCHED THEN UPDATE SET 
      t.t_fk1_id = m.new_fk1_id,
      t.t_fk2_id = m.new_fk2_id,
      t.t_last_update = trunc(sysdate);

我不喜歡批量更新。 由於您有時間估算,因此看起來您有一個測試系統。 我建議給它一個 go 並分批試用。

如果方法 1 和方法 2 仍然太慢,您可以按照您的分區思路進行操作。 比如引入一個列來區分需要遷移的行。 因為DEFAULT... NOT NULL這將非常快:

ALTER TABLE Tab1 ADD (todo NUMBER DEFAULT 0 NOT NULL);

現在將您的表分成兩個部分,一個包含遷移數據,一個包含您不會接觸的 rest。 我對在應用程序運行時引入分區沒有太多經驗,但我認為它是可以解決的,例如在線重定義或

ALTER TABLE Tab1 MODIFY 
  PARTITION BY LIST (todo) (
  PARTITION pdonttouch VALUES (0),
  PARTITION pmigration VALUES (1) 
) ONLINE UPDATE INDEXES (
  T_ID_PK GLOBAL, T_IN_1  GLOBAL,
  T_IN_2  GLOBAL, T_IN_3  GLOBAL
);

現在您可以識別要移動的行。 這可以逐行完成,不會影響其他進程,也不應計入您的停機時間。 遷移行將從分區pdonttouch移動到分區pmigration ,因此您需要啟用行移動。

ALTER TABLE Tab1 ENABLE ROW MOVEMENT;
UPDATE Tab1 SET todo=1 WHERE .... JOIN ...;

現在您可以在分區PMIGRATION上工作並更新那里的數據。 這應該比原始表快得多,因為分區的大小僅為整個表的 0.5%。 不過,不知道索引。

理論上,您可以創建一個與PMIGRATION具有相同結構和數據的表,在該表上工作,完成后,使用EXCHANGE PARTITION交換分區和工作表。 再次不知道索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM