[英]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)
索引是:
T_ID_PK [t_id] UNIQUE
T_IN_1 [t_fk2_id,t_fk1_id,t_start_date,t_del_flag] NONUNIQUE
T_IN_2 [t_last_update,t_fk2_id] NONUNIQUE
T_IN_3 [t_fk2_id,t_fk1_id] NONUNIQUE
目前我已經想到了一些可能的解決方案,其中大部分我已經測試過了:
使用上述解決方案,我遇到了一些問題,例如:如果使用 /*+ 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.