更新履歷
- [更新日期:20200530] :存儲過程是網上找的資料 原始鏈接 ,從代碼上看好像原理沒有問題,但一些小地方可能存在異常。我以此修改一下,拋磚引玉吧。
問題描述
客戶現場有一套基於Oracle的業務系統。他們的數據庫版本是Oracle 10g R2(10.2.0.5) Standard Edition,數據量超過1 TB。
當前存在三個問題:
- 數據量不斷的增長,每天增長量達到3GB或以上;
- 業務系統涉及報表與查詢的功能異常緩慢,甚至無法查詢數據;
- RMAN備份壓力非常大,異常恢復無法滿足RTO要求;
問題分析
本番數據庫數據量龐大,開發人員收到現場反饋性能緩慢就創建索引。不過在此數據規模之下,索引已經沒有太大作用反而會影響增刪改的效率。在不升級現有數據庫爲企業版運用分區表功能,那麼我們就必須要建立歷史庫定期卸載本番庫的數據到歷史庫。歷史庫可以針對數據量大的表實施按年分表、按月分表等操作縮減單表數據量。
如此本番庫將會保持在可控範圍內,歷史庫出現異常不會對產線生產造成影響,其修復時間與壓力也可以減少。
問題處理
通過PL/SQL存儲過程定期任務實現從本番庫抽取數據到歷史庫並刪除本番庫已卸載的數據,大致流程參考如下:
- 構建測試數據
# 創建測試表
create table operate_log(str01 varchar2(50),cdate varchar2(20));
# 插入測試數據 (將此設置到腳本並設定到計劃任務執行,比如30秒執行一次。)
begin
for i in 1..100 loop
INSERT INTO operate_log VALUES(dbms_random.string('x', 20),to_char(SYSDATE,'yyyy-mm-dd hh24:mi:ss'));
end loop;
commit;
end;
/
# 創建測試索引
create index idx_operate_log on operate_log(cdate);
- 存儲過程
鑑於客戶保密協議,無法提供本番環境腳本。只能用網上的腳本來修改演示。
腳本說明:腳本是按月分表,與我上面的圖示有一些差異的。
CREATE OR REPLACE
procedure operate_log_proc(return_code OUT VARCHAR2,return_msg OUT VARCHAR2)
authid current_user
is
err_index NUMBER;-- 錯誤定位索引
table_name VARCHAR2(20);-- 源分表名稱
log_table_name VARCHAR2(20);-- 目標分表名稱
current_month_start DATE;
create_table_cursor NUMBER(10);
create_table_sql VARCHAR2(1000);
insert_data_sql VARCHAR2(1000);
delete_data_sql VARCHAR2(1000);
v_count NUMBER(10);
begin
table_name := 'operate_log';
return_msg := '執行[OPERATE_LOG_PROC]成功';
return_code := '1';
err_index := 1;
-- 生成分表的表名
SELECT 'operate_log_' ||
TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1), 'yyyymm')
INTO log_table_name
FROM DUAL;
-- 表存在不用創建
select count(1) into v_count from user_tables where table_name = log_table_name;
if v_count <= 0 THEN
-- 打開遊標
create_table_cursor := DBMS_SQL.OPEN_CURSOR;
-- 拼接創建表的SQL語句,並執行
create_table_sql := 'CREATE TABLE "' || log_table_name || '" (
"STR01" VARCHAR2(50 BYTE) NOT NULL ,
"CDATE" VARCHAR2(50 BYTE) NOT NULL
)';
DBMS_SQL.PARSE(create_table_cursor, create_table_sql, DBMS_SQL.V7);
DBMS_SQL.CLOSE_CURSOR(create_table_cursor);
end if;
err_index := 2;
-- 當前月起始時間,如2020-05-01 00:00:00
SELECT TRUNC(SYSDATE, 'MM') INTO current_month_start FROM DUAL;
-- 將Operate_Log表中上月記錄添加到新創建的表中
insert_data_sql := 'INSERT INTO ' || log_table_name || ' (SELECT * FROM ' || table_name || ' WHERE CDATE < ''' || current_month_start || ''')';
dbms_output.put_line('Insert SQL: '||insert_data_sql);
EXECUTE IMMEDIATE insert_data_sql;
err_index := 3;
-- 從Operate_Log表中刪除上月記錄
delete_data_sql := 'DELETE FROM ' || table_name || ' WHERE CDATE < ''' || current_month_start || '''';
dbms_output.put_line('Delete SQL: '||delete_data_sql);
EXECUTE IMMEDIATE delete_data_sql;
err_index := 4;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
return_msg := '過程[OPERATE_LOG_PROC]出錯,' || '第[' || err_index || ']塊語句出錯';
return_code := '0';
ROLLBACK;
end operate_log_proc;
/
- 計劃任務定時執行
執行時間:每月1日 0:30
網上腳本不足點:
- 沒有操作日誌記錄,無法與其他系統關聯輸出操作結果。
- 沒有分批次實施,閒忙月履歷數據量是不一樣的,一次性操作10W行記錄與一次性操作1000W行記錄性能完全不一樣。
常見問題
- 歷史數據遷移後,應用程序怎麼查詢?
應用程序需要根據分表情況實施一定的改造。通過界面的查詢區間來判斷應用程序發出的SQL是否跨越兩個數據庫,查詢區間未超過番庫保留週期則執行SQL方案一,查詢區間超過番庫保留週期則執行SQL方案二,查詢區間不包含本番庫保留週期則執行SQL方案三。
完整數據 = 生產數據 + 歷史數據