在數據庫的遷移和升級場景中,我們經常會遇到一個問題:在做壓力測試時,如何模擬真實的業務壓力,解決這個問題的方法有很多,比如:應用方開發模擬程序或者使用壓力測試工具模擬,如load runner,但是,如果要說哪種方法能最大限度地模擬真實業務壓力,我認為還是Oracle的Database Replay(數據庫重放)功能,Database Replay功能是Real Application Testing的一部分,它的基本原理圖如下:
簡單說,Database Replay可以在生產數據庫上“捕獲”負載(workload capture),保存成一定格式的二進制文件。將保存的負載文件復制到測試環境,經過一定的處理後,就可以在測試環境“重放”負載(workload replay),從而達到模擬真實壓力,進行壓力測試的目的。
Database Replay是11g才有的特性,但是,負載的捕獲,支持10.2.0.4之後的數據庫,這一點也正好符合國內很多客戶的需求,因為國內還有相當一部分客戶,因為穩妥起見,系統還都一直運行在10gR2版本的Oracle數據庫上,都有升級的需求。
Consolidated Database Replay 是11.2.0.2之後才出現的功能,字面意思是整合的數據庫重放,它可以將不同系統“捕獲”的負載,整合到一起重放,適用於數據庫整合測試和Scale-Up測試。
後面我們用實驗來詳細介紹一下Database Replay的相關功能,實驗采用10.2.0.5作為負載的捕獲端,用12.1.0.2作為負載的重放端。
首先要根據文檔 Mandatory Patches for Database Testing Functionality for Current and Earlier Releases (Doc ID 560977.1),檢查該打的補丁是否打過了。捕獲端和重放端都要打。
捕獲端是一個安裝在OL5.9上的10.2.0.5,數據庫SID是test1.
在OS創建目錄並創建相應的數據庫目錄對象,用於保存負載數據:
$ cd /u01/app/oracle
$ mkdir wrc_dir1
SQL>CREATE OR REPLACE DIRECTORY wrc_dir1 AS '/u01/app/oracle/wrc_dir1/';
雖然Database Replay支持10.2.0.4以上的版本進行負載“捕獲”,但是10gR2的數據庫要想正常使用這個功能,需要對數據庫設置一個初始化參數,聯機文檔上講需要運行腳本$ORACLE_HOME/rdbms/admin/wrrenbl.sql,其實這個腳本只做一件事,就是設置初始化參數PRE_11G_ENABLE_CAPTURE=true。
[oracle@test10g ~]$ sqlplus "/ as sysdba"
SQL*Plus: Release 10.2.0.5.0 - Production on Fri Dec 23 10:57:26 2016
Copyright (c) 1982, 2010, Oracle. All Rights Reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> @?/rdbms/admin/wrrenbl.sql
SQL> SET FEEDBACK 1
SQL> SET NUMWIDTH 10
SQL> SET LINESIZE 80
SQL> SET TRIMSPOOL ON
SQL> SET TAB OFF
SQL> SET PAGESIZE 100
SQL> alter system set PRE_11G_ENABLE_CAPTURE=true sid='*';
System altered.
修改後需要重啟數據庫,不僅僅為了讓參數生效,也是為了讓所有會話都重新連接。
創建數據庫用戶app1,並在app1 schema中創建表test_table1:
SQL>create table test_table1 (col1 number, col2 varchar2(20));
使用sys用戶執行:
BEGIN
DBMS_WORKLOAD_CAPTURE.start_capture (name => 'test1_cap',
dir => 'WRC_DIR1',
duration => NULL);
END;
/
使用app1 用戶登錄數據庫,執行:
begin
for i in 1..10000 loop
insert into test_table1 values(i,'test1:'||i);
end loop;
commit;
end;
/
在test_table1表中插入10000條記錄。
然後斷開會話。
SQL>exec dbms_workload_capture.finish_capture;
重放端是一個安裝在OL7.1上的12.1.0.2數據庫,數據庫SID是test3。
創建目錄及數據庫對象:
$ mkdir –p /u01/app/oracle/con_dir/wrc_dir1
SQL>CREATE OR REPLACE DIRECTORY wrc_dir1 AS '/u01/app/oracle/con_dir/wrc_dir1/';
復制捕獲的負載文件到/u01/app/oracle/con_dir/wrc_dir1下。
在重放端創建app1用戶,並創建和捕獲端相同結構的表test_table1。
用SYS用戶執行:
BEGIN
DBMS_WORKLOAD_REPLAY.PROCESS_CAPTURE (capture_dir => 'WRC_DIR1', parallel_level=>1);
END;
/
BEGIN
DBMS_WORKLOAD_REPLAY.INITIALIZE_REPLAY (replay_name => 'replay1',
replay_dir => 'WRC_DIR1');
END;
/
exec dbms_workload_replay.prepare_replay ;
WRC客戶端是負責讀取負載文件,重放負載的程序主體。重放除了在SQL環境中發出指令,還需要WRC客戶端的配合。啟動WRC客戶端的命令:
$ wrc system/welcome1 mode=replay replaydir=./wrc_dir1
SQL> exec dbms_workload_replay.start_replay;
WRC客戶端退出才標志重放完成,下面是實際執行的輸出:
[oracle@ol71vm1 con_dir]$ wrc system/welcome1 mode=replay replaydir=./wrc_dir1
Workload Replay Client: Release 12.1.0.2.0 - Production on Sun Jan 1 20:46:15 2017
Copyright (c) 1982, 2014, Oracle and/or its affiliates. All rights reserved.
Wait for the replay to start (20:46:15)
Replay client 1 started (20:46:45)
Replay client 1 finished (20:50:27)
這時我們能夠看到重放端app1用戶test_table1表中被插入了10000條記錄,說明負載被正確地重放了。
另外,完成後可以查看重放報告,取得報告的命令:
SQL>set pagesize 0 long 30000000 longchunksize 2000 linesize 155
SQL>select DBMS_WORKLOAD_REPLAY.REPORT(replay_id => 71, format => 'HTML') from dual;
當然,要想做更詳細的分析,還是看相應時間段的AWR報告比較好。
比如有兩個APP,分別運行在不同的數據庫上,客戶希望將兩個應用整合到一個數據庫上,但是不知道新數據庫是否能承受住壓力。那麼就可以用Consolidated Database Replay來模擬測試一下。
前面的例子我們做的捕獲是從數據庫test1上做的,我們再創建另一個10.2.0.5的數據庫叫test2,在test2上也捕獲一份負載。
$ cd /u01/app/oracle
$ mkdir wrc_dir2
在test2數據庫中執行:
SQL>CREATE OR REPLACE DIRECTORY wrc_dir2 AS '/u01/app/oracle/wrc_dir2/';
創建數據庫用戶app2,並在app2 schema中創建表test_table2:
SQL>create table test_table2 (col1 number, col2 varchar2(20));
修改初始化參數,重啟test2數據庫。
用戶SYS用戶執行:
BEGIN
DBMS_WORKLOAD_CAPTURE.start_capture (name => 'test2_cap',
dir => 'WRC_DIR2',
duration => NULL);
END;
/
用app2用戶登錄數據庫test2,執行下面的腳本模擬壓力:
begin
for i in 1..10000 loop
insert into test_table2 values(i,'test2:'||i);
end loop;
commit;
end;
/
然後斷開連接。
SQL>exec dbms_workload_capture.finish_capture;
我們還是使用前面實驗用過的OL7.1上的12.1.0.2的這個環境,使用test3這個數據庫。
創建Consolidated database replay目錄:
SQL>CREATE OR REPLACE DIRECTORY con_dir AS '/u01/app/oracle/con_dir/';
創建存放test2負載的目錄wrc_dir2。(wrc_dir1在前面的實驗裡已經創建過了。)
$ cd /u01/app/oracle/con_dir
$ mkdir wrc_dir2
SQL>CREATE OR REPLACE DIRECTORY wrc_dir2 AS '/u01/app/oracle/con_dir/wrc_dir2/';
WRC_DIR1和WRC_DIR2都是CON_DIR的子目錄。
在數據庫test3上創建app2用戶,並創建和捕獲端相同結構的表test_table2。
(app1用戶和test_table1已經在上個實驗中創建在數據庫test3上了)
使用SYS用戶執行:
SQL>exec dbms_workload_replay.set_replay_directory('CON_DIR');
SQL>exec dbms_workload_replay.begin_replay_schedule('S1'); -- S1是給時間表起的名字
SQL>select dbms_workload_replay.add_capture('WRC_DIR1') from dual;
SQL>select dbms_workload_replay.add_capture('WRC_DIR2') from dual;
SQL>exec dbms_workload_replay.end_replay_schedule; -- 保存重放時間表
SQL>exec dbms_workload_replay.initialize_consolidated_replay('CR1','S1');
SQL>exec dbms_workload_replay.prepare_consolidated_replay (synchronization => 'OBJECT_ID');
-- 參數synchronization決定replay重放負載的方式,OBJECT_ID代表replay重放負載時,對象上的操作順序,在對象及被引用的對象范圍內,保證和捕獲時的順序一致。
因為是2個不同的負載,所以需要啟動至少2個WRC客戶端
$ wrc system/welcome1 mode=replay replaydir=/u01/app/oracle/con_dir &
$ wrc system/welcome1 mode=replay replaydir=/u01/app/oracle/con_dir &
用SYS用戶執行:
SQL>exec dbms_workload_replay.start_consolidated_replay;
完成重放後,我們可以看到,在數據庫test3中app1和app2兩個用戶下的test_table1和test_table2表中都被插入了10000條記錄,證明負載被正確地重放了。
有的時候,我們想知道未來業務量增長了,數據庫是否能承受的了,比如模擬測試未來業務增加一倍的情況,也可以使用Consolidated Database Replay。這樣使用的最簡單方式就是一份捕獲,復制多份,同時replay。
比如上面的實驗,從test1庫上捕獲的負載,我們可以復制2份,分別放在WRC_DIR1和WRC_DIR2中,其他步驟完全一樣,當我們重放時,就是執行了雙份的test1上捕獲的負載。
實際效果就是,app1用戶下的test_table1表中被插入了20000條記錄。
當然,Scale-Up並不只是這麼簡單,相應的還有Time Shifting,就是通過調整時間,讓多個負載的高峰重合到一起,還有Workload Folding,就是將一個負載,分成不同的時間段,然後將不同時間段的負載並行執行。篇幅有限,留待大家自己研究了。
如果出現沒有任何報錯,但是重放端不重放負載的情況,可能是WRC客戶端有問題,可以嘗試在重放端,重新編譯WRC客戶端:
$ cd $ORACLE_HOME/rdbms/lib
$ make -f ins_rdbms.mk iwrc