DM8高階日誌
高階日誌
簡介
行表和HUGE表在增刪改查效能上存在差異,因此在實際的生產環境中,使用者可能會同時使用一個行表來管理資料和一個HUGE表來分析資料。具體做法是對行表進行增刪改操作,然後把行表中的資料複製到HUGE表中用於查詢或分析。如果每次分析資料時都對行表進行全表查詢插入HUGE表,效能較低。
為此提出一種解決方案:給行表新增日誌輔助表用於記錄行表的增刪改和TRUNCATE操作,可以根據日誌表實現對HUGE表的增量更新,以此來提高從行表複製資料到HUGE表的效能。
使用須知
增量更新過程,我們只提供日誌的記錄以及日誌記錄規則的制定,真正執行增量更新是由使用者根據日誌記錄自行操作。輔助表中登記資訊,為某一時間點後源表資料的增量變化資訊登記。
建立日誌輔助表
建立日誌輔助表,有兩種方式:一是建表時建立;二是修改表時建立。
1. 建表時候使用< 高階日誌子句>建立日誌輔助表
語法格式
CREATE TABLE < 表名定義> < 表結構定義>; < 表名定義> ::= [< 模式名>.] < 表名> < 表結構定義>::=< 表結構定義1> | < 表結構定義2> < 表結構定義1>::= (< 列定義> {,< 列定義>} [,< 表級約束定義>{,< 表級約束定義>}]) [ON COMMIT ROWS] [][< 空間限制子句>] [][< 壓縮子句>] []< 高階日誌子句> [] [] …… < 高階日誌子句>::= WITH ADVANCED LOG 省略號(……)
2. 修改表時使用< 高階日誌子句>新增日誌表
語法格式
ALTER TABLE < 高階日誌子句>;
刪除日誌輔助表
語法格式
ALTER TABLE xxx WITHOUT ADVANCED LOG;
刪除日誌輔助表的資料
語法格式
ALTER TABLE TRUNCATE ADVANCED LOG;
資料清除後可能導致源表和HUGE無法同步,需慎重操作。
使用日誌輔助表的規則與約束
日誌輔助表命名為“表名$ALOG”,用於記錄源表的操作但不涉及具體資料。規則與約束:
1. 每個源表僅支援設定一個日誌輔助表。
2. 表刪除的同時刪除其日誌輔助表。
3. 表更名時,日誌表同步更名。
4. 由於其日誌表名長度不得超過128,因此表名長度不得超過123。
5. 輔助表僅登記源表相關增刪改及TRUNCATE等涉及資料變化的操作,卻不涉及具體資料。
6. 源表執行ADD/DROP/MODIFY COLUMN的DDL操作時,也必須保證日誌輔助表為空。
7. 如果表設定了高階日誌功能,禁止或者不建議以下操作:
1) 禁止對源表建立聚集索引
2) 禁止刪除源表上本存在的聚集索引
3) 禁止直接對分割槽表的子表執行DELETE、UPDATE、INSERT以及TRUNCATE
4) 禁止在ALTER TABLE時,新建、刪除或者修改主鍵,使主鍵失效或者生效,或者刪除主鍵列
5) 禁止對臨時表、HUGE表和間隔分割槽表設定高階日誌表,禁止查詢插入建表方式設定高階日誌表。
6) 禁止直接刪除高階日誌表以及建立字尾為”$ALOG”的表
7) 禁止合併分割槽
8) 禁止對錶加列、刪除列和修改列,禁止新增、分裂、交換和刪除分割槽。交換分割槽時的普通表也禁止帶有高階日誌
9) 表備份還原後無法控制資料跟蹤,無法保證同步資料的正確性。因此不建議對該表進行備份還原操作,或操作後需要人工干預處理
日誌輔助表結構
高階日誌輔助表“表名$ALOG”的結構如下:
列 資料型別 說明 ORG_ROWID BIGINT 源表ROWID。當OP_TYPE=0時,ORG_ROWID=0 OP_TYPE SMALLINT 登記記錄日誌動作。 0:TRUNCATE 1:行插入 2:批次插入起始 3:批次插入結束 4:更新 5:刪除 6:刪除後再插入(僅用於堆表) COLMAP VARBINARY(2048) 當OP_TYPE=3時,記錄的是批次插入結束的ROWID; 當OP_TYPE=4時,是記錄的更新列的列號。例如0xA3,即二進位制的10100011, 表示更新的列為第1、2、6、8列,與DM_BIT_TEST()配合使用;其他情況為null COL_0 與源表的第一個主鍵列型別相同 源表的第一個主鍵列 COL_1 與源表的第二個主鍵列型別相同 源表的第二個主鍵列 COL_n ... ...
系統過程
高階日誌輔助表中的COLMAP列記錄的資料,用&操作只能獲取前64列的更新情況,因為會資料溢位。增加系統過程DM_BIT_TEST()用於獲取一個VARBINARY資料的第N位的數值。
語法格式
DM_BIT_TEST(DATA varbinary, nth int);
功能:返回二進位制資料varbinary第nth位是0還是1(最低位序號為1)。如果超過了位數則返回0。
例 0xF1轉為二進位制後為11110001,從低位開始第5位為1。二進位制1011從低位開始第三位為0。
SQL> SELECT DM_BIT_TEST(0xF1,5),DM_BIT_TEST(1011,3); LINEID DM_BIT_TEST(0xF1,5) DM_BIT_TEST(1011,3) ---------- ------------------- ------------------- 1 1 0
使用高階日誌同步資料的原則
使用者根據表定義建立資料同步的目標表,自己編寫同步DMSQL指令碼來進行同步。對於同步,建議遵守如下的原則:
1. 如果源表有主鍵,如果使用者沒有特殊的限制或要求,目標表最好也設定同樣的主鍵。
2. 如果源表沒有主鍵,為了準確同步,最好在目標表上新增一個輔助同步的主鍵列,同步時將org_rowid列的值插入該列中。
3. 使用者同步資料的指令碼基本邏輯如下:
declare /*遍歷日誌表的遊標*/ cursor c IS select * from t01$alog for update; /*同步用的變數*/ r t01$alog %rowtype; /*同步批次插入用的變數*/ bi_start t01$alog %rowtype; org_rec t01%rowtype; begin /*遍歷日誌表,根據各記錄的op_type進行同步*/ open c; loop fetch c into r; exit when c%notfound; if (r.op_type = 0) then print 'truncate' ; execute immediate 'truncate table t01'; elseif (r.op_type = 1 or r.op_type = 6) then print 'insert ' || r.org_rowid; execute immediate 'insert ....' elseif (r.op_type = 2) then bi_start = r; print 'batch insert start'; elseif (r.op_type = 3) then print 'batch insert last ' || bi_start.org_rowid || ' ' || cast( r.colmap as bigint); execute immediate 'insert ....' elseif (r.op_type = 4) then print 'update ' || r.org_rowid; select * into org_rec from t01 where ……; execute immediate 'update ....' using bi_start… r…; elseif (r.op_type = 5) then print 'delete ' || r.org_rowid; execute immediate 'delete ....' end if; end loop; close c; /*清理日誌表*/ execute immediate 'alter table t01 truncate advanced log'; end; /
4. 如果在資料同步時源表仍有併發的DML,指令碼中查詢日誌時要使用for update子句。
5. 同步指令碼根據源表的結構有所不同:
1) 如果源表有聚集主鍵
在同步時可使用日誌輔助表中的org_rowid和主鍵列輔助源表定位。使用主鍵列定位目標表。
2) 如果源表有主鍵,但不是聚集主鍵
直接根據org_rowid定位資料,最好不要使用主鍵列來定位源表。主鍵列僅用來定位目標表。
如果該情況下更新了主鍵列,對於聚集主鍵,將是刪除後更新,如果不是聚集主鍵,仍是記錄更新,日誌輔助表中的主鍵列仍是原值,所以非聚集主鍵時主鍵列不要用來定位源表。
3) 如果沒有主鍵
使用org_rowid來進行源表的定位;目標表的定義根據使用者自己的方式使用org_rowid定位。
6. 如果源表中沒有聚集索引,批次插入時可以根據OP_TYPE=3時的org_rowid(批次插入起始ROWID)和COLMAP中的資料(批次插入結束ROWID)範圍查詢源表插入目標表;如果有聚集索引,考慮到組合索引無法進行範圍查詢,只能使用第一個主鍵和rowid進行範圍查詢。
7. MPP環境下,因為高階日誌表是本地表,所以同步資料的時候,只能各個節點單獨做同步。
應用例項
建立不帶主鍵的源表
1. 建立源表
SQL> Create table t01(a int, b int, c varchar); executed successfully used time: 16.049(ms). Execute id is 2438. SQL> insert into t01 values(88,88, '原始資料1'); affect rows 1 used time: 0.689(ms). Execute id is 2440. SQL> insert into t01 values(99,99, '原始資料2'); affect rows 1 used time: 0.430(ms). Execute id is 2442.
2. 在源表上建立日誌輔助表
SQL> Alter table t01 with advanced log; executed successfully used time: 28.284(ms). Execute id is 2443.
3. 檢視日誌輔助表結構
SQL> Select tabledef('SYSDBA','T01$ALOG'); LINEID TABLEDEF('SYSDBA','T01$ALOG') ---------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1 CREATE TABLE "SYSDBA"."T01$ALOG" ( "ORG_ROWID" BIGINT NOT NULL, "OP_TYPE" SMALLINT NOT NULL, "COLMAP" VARBINARY(2048), CLUSTER PRIMARY KEY("ORG_ROWID", "OP_TYPE")) STORAGE(ON "MAIN", CLUSTERBTR) ; used time: 0.846(ms). Execute id is 2445.
4. 在源表中刪除1行資料。
SQL> delete from t01 where a=88; affect rows 1 used time: 1.329(ms). Execute id is 2447. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP ---------- -------------------- ----------- ---------- 1 1 5 NULL used time: 0.567(ms). Execute id is 2449.
5. 在源表中更新1行資料。
SQL> update t01 set c='hello world' where a=99; affect rows 1 used time: 0.907(ms). Execute id is 2451. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP ---------- -------------------- ----------- ---------- 1 1 5 NULL 2 2 4 0x04 used time: 0.220(ms). Execute id is 2452.
6. 在源表中再次更新同1行資料。這一操作在日誌表中沒有記錄。因為將源表上一條(99,99, '原始資料2')的資料更新為(99,99,'hello world')之後,又再次更新為(99,99,'hello world!')。這兩步更新操作的最終結果就和直接更新為(99,99,'hello world!')一樣,所以兩步操作只有一條記錄。
SQL> update t01 set c='hello world!' where a=99; affect rows 1 used time: 0.976(ms). Execute id is 2457. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP ---------- -------------------- ----------- ---------- 1 1 5 NULL 2 2 4 0x04 used time: 0.307(ms). Execute id is 2458.
7. 先清空源表資料,再檢視日誌輔助表的變化。發現日誌輔助表中也清空了之前的記錄,只記錄下了清空源表的操作。
SQL> Truncate table t01; executed successfully used time: 28.025(ms). Execute id is 2460. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP ---------- -------------------- ----------- ---------- 1 0 0 NULL used time: 0.414(ms). Execute id is 2461.
8. 在源表中批次插入100行資料。單機情況下,大於100條才叫批次插入。
SQL> insert into t01 select level a,level+1 b,level c connect by level< =100 order by a,b; affect rows 100 used time: 2.692(ms). Execute id is 2464. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP ---------- -------------------- ----------- ------------------ 1 0 0 NULL 2 1 2 NULL 3 1 3 0x0000000000000064 used time: 0.343(ms). Execute id is 2466.
9. 在源表中插入1行資料。
SQL> insert into t01 values(1001,1002,1003); affect rows 1 used time: 0.539(ms). Execute id is 2468. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP ---------- -------------------- ----------- ------------------ 1 0 0 NULL 2 1 2 NULL 3 1 3 0x0000000000000064 4 101 1 NULL used time: 0.178(ms). Execute id is 2470. 10.同步資料 建立huge表。因為不帶主鍵,為了準確同步,在目標表huge_t01上新增一個輔助同步的主鍵列c_rowid,同步時將org_rowid列的值插入該列中
SQL> create huge table huge_t01 (c_rowid bigint, a int, b int, c varchar(1024)); executed successfully used time: 26.784(ms). Execute id is 2474.
執行同步指令碼。同步指令碼由使用者根據實際情況自行編寫。本例中指令碼如下:
SQL> declare 2 /*遍歷日誌表的遊標*/ 3 cursor c IS select * from t01$alog for update; 4 /*同步用的變數*/ 5 r t01$alog %rowtype; 6 /*同步批次插入用的變數*/ 7 bi_start t01$alog %rowtype; 8 set_sql varchar; 9 upd_sql varchar; 10 i int; 11 begin 12 /*遍歷日誌表,根據各記錄的op_type進行同步*/ 13 open c; 14 loop 15 fetch c into r; 16 exit when c%notfound; 17 if (r.op_type = 0) then 18 print 'truncate' ; 19 execute immediate 'truncate table huge_t01;'; 20 elseif (r.op_type = 1 or r.op_type = 6) then 21 print 'insert ' || r.org_rowid; 22 execute immediate 'insert into huge_t01 select rowid,* from t01 where rowid=?;' using r.org_rowid; 23 elseif (r.op_type = 2) then 24 bi_start = r; 25 print 'batch insert start'; 26 elseif (r.op_type = 3) then 27 print 'batch insert last ' || bi_start.org_rowid || ' ' || cast( r.colmap as bigint); 28 execute immediate 'insert into huge_t01 select rowid,* from t01 where rowid>= ? and rowid< = ?;' using r.org_rowid, cast(r.colmap as bigint); 29 elseif (r.op_type = 4) then 30 print 'update ' || r.org_rowid; 31 set_sql = ''; 32 i = 0; 33 if (dm_bit_test(r.colmap,1)) = 1 then set_sql = set_sql || 'a = org.a'; i = i+1; end if; 34 if (dm_bit_test(r.colmap,2)) = 1 then if i > 0 then set_sql = set_sql ||','; end if; set_sql = set_sql || 'b = org.b'; i = i+1; end if; 35 if (dm_bit_test(r.colmap,3)) = 1 then if i > 0 then set_sql = set_sql ||','; end if; set_sql = set_sql || 'c = org.c'; i = i+1; end if; 36 upd_sql = 'declare org t01%rowtype; begin select * into org from t01 where rowid=?; update huge_t01 set ' || set_sql || ' where c_rowid=?; end;'; 37 execute immediate upd_sql using r.org_rowid, r.org_rowid; 38 elseif (r.op_type = 5) then 39 print 'delete ' || r.org_rowid; 40 execute immediate 'delete from huge_t01 where c_rowid=?;' using r.org_rowid; 41 end if; 42 end loop; 43 close c; 44 /*清理日誌表*/ 45 execute immediate 'alter table t01 truncate advanced log'; 46 end; 47 / DMSQL executed successfully used time: 197.177(ms). Execute id is 2478.
11.查詢huge表中的資料。可以看出,huge_t01上的資料都是源表建立了日誌輔助表之後的增量資料。
SQL> Select count(*) from huge_t01; LINEID COUNT(*) ---------- -------------------- 1 101 used time: 0.622(ms). Execute id is 2479.
建立帶主鍵的源表
1. 建立帶有日誌輔助表的源表
SQL> Create table t01(a int, b int, c varchar, primary key(a,b)) with advanced log; executed successfully used time: 14.030(ms). Execute id is 2834.
2. 檢視日誌輔助表結構
SQL> Select tabledef('SYSDBA','T01$ALOG'); LINEID TABLEDEF('SYSDBA','T01$ALOG') ---------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 1 CREATE TABLE "SYSDBA"."T01$ALOG" ( "ORG_ROWID" BIGINT NOT NULL, "OP_TYPE" SMALLINT NOT NULL, "COLMAP" VARBINARY(2048), "COL_0" INTEGER, "COL_1" INTEGER, CLUSTER PRIMARY KEY("ORG_ROWID", "OP_TYPE")) STORAGE(ON "MAIN", CLUSTERBTR) ; used time: 0.637(ms). Execute id is 2844.
3. 清空源表
SQL> Truncate table t01; executed successfully used time: 14.417(ms). Execute id is 2849. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP COL_0 COL_1 ---------- -------------------- ----------- ---------- ----------- ----------- 1 0 0 NULL NULL NULL used time: 0.760(ms). Execute id is 2857.
4. 在源表中插入一條記錄
SQL> insert into t01 values(1001,1002,1003); affect rows 1 used time: 0.621(ms). Execute id is 2860. SQL> Select * from t01$alog; LINEID ORG_ROWID OP_TYPE COLMAP COL_0 COL_1 ---------- -------------------- ----------- ---------- ----------- ----------- 1 0 0 NULL NULL NULL 2 1 1 NULL 1001 1002 used time: 0.313(ms). Execute id is 2861.
5. 同步資料
建立huge表。
SQL> create huge table huge_t01 (a int, b int, c varchar(1024), primary key(a,b)); executed successfully used time: 24.819(ms). Execute id is 2868.
執行同步指令碼。同步指令碼由使用者根據實際情況自行編寫。本例中指令碼如下:
SQL> declare 2 /*遍歷日誌表的遊標*/ 3 cursor c IS select * from t01$alog for update; 4 /*同步用的變數*/ 5 r t01$alog %rowtype; 6 /*同步批次插入用的變數*/ 7 bi_start t01$alog %rowtype; 8 set_sql varchar; 9 upd_sql varchar; 10 i int; 11 begin 12 /*遍歷日誌表,根據各記錄的op_type進行同步*/ 13 open c; 14 loop 15 fetch c into r; 16 exit when c%notfound; 17 if (r.op_type = 0) then 18 print 'truncate' ; 19 execute immediate 'truncate table huge_t01;'; 20 elseif (r.op_type = 1 or r.op_type = 6) then 21 print 'insert ' || r.org_rowid; 22 execute immediate 'insert into huge_t01 select * from t01 where rowid=?;' using r.org_rowid; 23 elseif (r.op_type = 2) then 24 bi_start = r; 25 print 'batch insert start'; 26 elseif (r.op_type = 3) then 27 print 'batch insert last ' || bi_start.org_rowid || ' ' || cast( r.colmap as bigint); 28 execute immediate 'insert into huge_t01 select * from t01 where rowid>= ? and rowid< = ?;' using r.org_rowid, cast(r.colmap as bigint); 29 elseif (r.op_type = 4) then 30 print 'update ' || r.org_rowid; 31 set_sql = ''; 32 i = 0; 33 if (dm_bit_test(r.colmap,1)) = 1 then set_sql = set_sql || 'a = org.a'; i = i+1; end if; 34 if (dm_bit_test(r.colmap,2)) = 1 then if i > 0 then set_sql = set_sql ||','; end if; set_sql = set_sql || 'b = org.b'; i = i+1; end if; 35 if (dm_bit_test(r.colmap,3)) = 1 then if i > 0 then set_sql = set_sql ||','; end if; set_sql = set_sql || 'c = org.c'; i = i+1; end if; 36 upd_sql = 'declare org t01%rowtype; begin select * into org from t01 where rowid=?; update huge_t01 set ' || set_sql || ' where a = ? and b = ?; end;'; 37 execute immediate upd_sql using r.org_rowid, r.col_0, r.col_1; 38 elseif (r.op_type = 5) then 39 print 'delete ' || r.org_rowid; 40 execute immediate 'delete from huge_t01 where a= ? and b = ?;' using r.col_0, r.col_1; 41 end if; 42 end loop; 43 close c; 44 /*清理日誌表*/ 45 execute immediate 'alter table t01 truncate advanced log'; 46 end; 47 / DMSQL executed successfully used time: 134.846(ms). Execute id is 2872.
6. 查詢huge表中的資料。可以看出,huge_t01上的資料都是源表建立了日誌輔助表之後的增量資料。
SQL> select * from huge_t01; LINEID A B C ---------- ----------- ----------- ---- 1 1001 1002 1003 used time: 1.276(ms). Execute id is 2875.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26015009/viewspace-2848578/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- DM8 日誌挖掘
- nginx高階訪客日誌切割Nginx
- Apche日誌系列(5):高階技術(轉)
- DM7和DM8的日誌量比較,DM8關閉單表的邏輯附加日誌
- OpenGL高階版本學習日誌2:光照模型&材質模型
- Linux 下高階日誌檔案檢視器Log File NavigatorLinux
- 達夢資料庫DM8之REDOLOG重做日誌檔案管理資料庫
- 達夢資料庫DM8之刪除歸檔日誌檔案資料庫
- 程式碼日數:高階迴圈
- 《MySQL 進階篇》十九:事務日誌MySql
- 瀚高資料庫日誌挖掘方法資料庫
- mysql之 日誌體系(錯誤日誌、查詢日誌、二進位制日誌、事務日誌、中繼日誌)MySql中繼
- php日誌,記錄日誌PHP
- 日誌分析-apache日誌分析Apache
- Android進階:一、日誌列印和儲存策略Android
- 聯機重做日誌、歸檔日誌、備用重做日誌
- mysql 日誌之錯誤日誌MySql
- mysql 日誌之慢查詢日誌MySql
- 【Oracle日誌】- 日誌檔案重建Oracle
- 為何要列印日誌?C++在高併發下如何寫日誌檔案(附原始碼)?C++原始碼
- 從高階函式--->高階元件函式元件
- 日誌
- [日誌分析篇]-利用ELK分析jumpserver日誌-日誌拆分篇Server
- Nodejs 進階:用 debug 模組列印除錯日誌NodeJS除錯
- 透過alert日誌看Oracle Startup的三個階段Oracle
- mysql 日誌之普通查詢日誌MySql
- Apche日誌系列(4):日誌分析(轉)
- SQL server資料庫高可用日誌傳送的方法SQLServer資料庫
- CSS高階進階CSS
- mysql 日誌之二進位制日誌MySql
- MYSQL啟用日誌和檢視日誌MySql
- Apche日誌系列(1):訪問日誌(轉)
- Apche日誌系列(2):錯誤日誌(轉)
- Apche日誌系列(3):定製日誌(轉)
- 日誌篇:模組日誌總體介紹
- C# 使用Log4Net記錄日誌(進階篇)C#
- “高階”的程式語言是否真的高階?
- 日誌配置