作者簡介:
張樂奕,通常使用的網名為kamus,也曾用過seraphim,現在任職于北京某大型軟件公司,Oracle數據庫DBA,主要負責證券行業的核心交易系統數據庫治理及維護工作。
熱切關注Oracle技術和相關操作系統技術,出沒于各大數據庫技術論壇,目前是中國最大的Oracle技術論壇www.itpub.net的數據庫治理版版主。
我的郵件地址:kamus@itpub.net
我的MSN: kamusis@hotmail.com
期望與各位朋友討論Oracle相關技術。
本人所有文章,不經許可,不得轉載。
目錄
一、 前言... 4
二、 思路... 4
三、 vmstat腳本及步驟... 4
1. 安裝statspack. 4
2. 創建stats$vmstat表... 4
3. 創建vmstat目錄... 6
4. 創建get_vmstat.ksh腳本... 6
5. 創建run_vmstat.ksh腳本... 8
6. 創建crontab作業,定時執行run_vmstat.ksh腳本... 9
7. 分析數據... 9
1) 異常報告... 9
2) 每小時趨勢報告... 13
3) 周趨勢報告... 14
4) 長期趨勢報告... 14
四、 使用Excel生成趨勢圖... 15
五、 參考資料... 15
一、 前言
作為UNIX系統中的Oracle數據庫治理員,時刻監控操作系統的性能無疑是非常重要的,幸運的是UNIX系統提供了大量的監控命令,比如vmstat, iostat, sar, top等等,這些監控命令均以字符界面輸出結果,再加上UNIX系統中shell的強大分析功能,這樣我們只需要編寫一些腳本就可以實現自動的后臺監控,當有問題的時候再自動發送郵件給DBA。
其實相類似的一些監控腳本可能已經隨處可見了,但是本文提到的一個新的思路,就是利用Oracle數據庫的statspack空間來存儲監控的結果,再利用數據庫天生的檢索優勢,這樣比以往靠shell分析甚或是人工分析生成的監控結果文件要更加輕松,智能,同時也能實現更持久和更廣泛的監控。
本文大部分素材來源自Donald K. Burleson的Oracle9i UNIX Administration Handbook,但是對于腳本中的一些錯誤和不合理的地方作了修改,并且添加了一些功能,本文的測試環境全部基于Sun Solaris 8 Sparc 64bit + Oracle9.2.0.5。
本文第一版主要以監控內存消耗和CPU等待的vmstat命令為例,將陸續添加iostat等其它命令的監控腳本。
二、 思路
首先安裝statspack,然后手工創建一個用于存儲vmstat命令輸出結果的表,再作一個shell定時執行vmstat,將結果全部插入數據庫表中,最后就是通過SQL定時分析表中的數據,超過預先指定的門限值就告警。同時根據表中的數據,我們還能作出系統性能的趨勢報告。
三、 vmstat腳本及步驟1. 安裝statspack
sqlplus “/ as sysdba”
SQL> @?/rdbms/admin/spcreate.sql
按照提示設定perfstat用戶的密碼之后,由于是10g,我選擇了SYSAUX表空間作為perfstat用戶的默認表空間,而沒有另行創建表空間。
2. 創建stats$vmstat表
SQL> connect perfstat/perfstat
SQL> @create_vmstat_tab.sql
# create_vmstat_tab.sql
drop table stats$vmstat;
create table stats$vmstat
(
start_date date,
duration number,
server_name varchar2(20),
running_queue number,
waiting_queue number,
swap_in number,
swap_out number,
kbytes_page_in number,
Kbytes_page_out number,
page_scan number,
user_cpu number,
system_cpu number,
idle_cpu number,
wait_cpu number
)
tablespace sysaux
storage (initial 10m
next 1m
pctincrease 0)
;
comment on column stats$vmstat.start_date
is '監控時間';
comment on column stats$vmstat.duration
is '監控時長';
comment on column stats$vmstat.server_name
is '服務器名稱';
comment on column stats$vmstat.running_queue
is '執行隊列';
comment on column stats$vmstat.waiting_queue
is '等待隊列';
comment on column stats$vmstat.swap_in
is '每秒平均交換進Lwps數';
comment on column stats$vmstat.swap_out
is '交換出進程數';
comment on column stats$vmstat.kbytes_page_in
is '換頁進字節(K)';
comment on column stats$vmstat.kbytes_page_out
is '換頁出字節(K)';
comment on column stats$vmstat.page_scan
is '換頁守護進程掃描頁數';
comment on column stats$vmstat.user_cpu
is '用戶操作占用的CPU';
comment on column stats$vmstat.system_cpu
is '系統操作占用的CPU';
comment on column stats$vmstat.idle_cpu
is 'CPU空閑率';
comment on column stats$vmstat.wait_cpu
is 'CPU等待率(AIX獨有)';
3. 創建vmstat目錄
在oracle用戶主目錄下創建用于存放所有相關腳本的vmstat目錄。
$ cd
$ mkdir vmstat
4. 創建get_vmstat.ksh腳本
改腳本用于定時執行vmstat命令并且將結果存入數據庫。
# get_vmstat.ksh
#!/bin/ksh
#----------------------------------------
# 首先設定環境變量,根據實際環境修改. . . .
#----------------------------------------
ORACLE_SID=kamusdb
eXPort ORACLE_SID
ORACLE_HOME=`cat /var/opt/oracle/oratabgrep /^$ORACLE_SID:cut -f2 -d':'`
export ORACLE_HOME
PATH=$ORACLE_HOME/bin:$PATH
export PATH
SERVER_NAME=`uname -aawk '{PRint $2}'`
typeset -u SERVER_NAME
export SERVER_NAME
#----------------------------------------
# 每5分鐘運行一次vmstat(300秒),可以根據實際情況修改 . . . .
#----------------------------------------
SAMPLE_TIME=300
#----------------------------------------
# 腳本一旦運行將不會停止,除非關閉操作系統 . . . .
# -S參數表示監控swap空間的情況,報告si,so列
# msg$$中的$$表示一個任意2位數字,系統自動生成
#----------------------------------------
while true
do
vmstat –S ${SAMPLE_TIME} 2 > /tmp/msg$$
#----------------------------------------
# Solaris系統的vmstat沒有wait CPU統計,所以我們在那一列中存入0
# $1, $2, $6, $7等數字分別表示vmstat輸出中的第幾列,
# 每個Unix系統中的vmstat輸出可能都不一樣,
# 所以修改這些列號,就可以應對不同的操作系統。
#----------------------------------------
cat /tmp/msg$$sed 1,3d awk '{ printf("%s %s %s %s %s %s %s %s %s %s /n", $1, $2, $6, $7, $8, $9, $12, $20, $21, $22) }' while read RUNQUE WAITQUE SWAPIN SWAPOUT PAGE_IN PAGE_OUT PAGE_SCAN USER_CPU SYSTEM_CPU IDLE_CPU
do
$ORACLE_HOME/bin/sqlplus perfstat/perfstat <<EOF
insert into perfstat.stats/$vmstat
values (
SYSDATE,
$SAMPLE_TIME,
'$SERVER_NAME',
$RUNQUE,
$WAITQUE,
$SWAPIN,
$SWAPOUT,
$PAGE_IN,
$PAGE_OUT,
$PAGE_SCAN,
$USER_CPU,
$SYSTEM_CPU,
$IDLE_CPU,
0
);
commit;
EXIT
EOF
done
done
#----------------------------------------
#刪除臨時文件
#----------------------------------------
rm /tmp/msg$$
5. 創建run_vmstat.ksh腳本
該腳本放在crontab中,用來定時檢查get_vmstat.ksh腳本有沒有正常運行,假如在運行,那么不作任何動作,假如不在運行,那么就運行get_vmstat.ksh腳本。
這個腳本的意義在于防止重新啟動操作系統之后忘記運行get_vmstat.ksh腳本。
# run_vmstat.ksh
#!/bin/ksh
#----------------------------------------
# 首先設定環境變量,根據實際環境修改. . . .
#----------------------------------------
SCRipT_PATH=`echo ~oracle/vmstat`
export SCRIPT_PATH
ORACLE_SID=kamusdb
export ORACLE_SID
ORACLE_HOME=`cat /var/opt/oracle/oratabgrep $ORACLE_SID:cut -f2 -d':'`
export ORACLE_HOME
PATH=$ORACLE_HOME/bin:$PATH
export PATH
#----------------------------------------
# 作更進一步地控制,在系統沒有操作的時間內停止監控
# 晚上8點到第二天凌晨8點之間停止監控
# 其它時間假如get_vmstat不在運行,就運行它
#----------------------------------------
HOUR=`date +"%H"`
check_stat=`ps -efgrep get_vmstatgrep -v grepwc -l`
vmstat_num=`expr $check_stat`
if [ $HOUR -gt 19 ] [ $HOUR -lt 8 ]
then
if [ $vmstat_num -gt 0 ]
then kill -9 `ps -efgrep get_vmstatgrep -v grepawk '{print $2 }'` > /dev/null
fi
else
if [ $vmstat_num -le 0 ]
then nohup $SCRIPT_PATH/get_vmstat.ksh > /dev/null 2>&1 &
fi
fi
6. 創建crontab作業,定時執行run_vmstat.ksh腳本
該作業每半小時運行一次。
$ crontab –l > oracle.cron
$ echo ’00,30 * * * * /export/home/oracle/vmstat/run_vmstat.ksh >> /export/home/oracle/vmstat/run.lst 2>&1’ >> oracle.cron
$ crontab oracle.cron
7. 分析數據
至此已經完成了定時運行vmstat和在數據庫中存儲vmstat結果的步驟。自然,僅僅是搜集了統計信息是遠遠不夠的,下面我們要分析搜集來的信息,產生操作系統的性能報告。
將報告分為以下幾類,分別用腳本實現。
異常報告:顯示超過了門限值的時間段(vmstat_alert.ksh + vmstat_alert.sql)
每小時趨勢報告:顯示一天內每小時的系統平均利用情況(rpt_vmstat_hr.sql)
周趨勢報告:顯示天天的系統平均利用情況(rpt_vmstat_dy.sql)
長期趨勢報告:顯示系統性能的一個長期趨勢線(rpt_vmstat.sql))
1) 異常報告
# vmstat_alert.sql
REM ----------------------------------------
REM 該SQL用于報告Oracle環境中每個服務器的異常情況。
REM 根據get_vmstat.ksh腳本得到的信息,報告每個小時的平均值。
REM DBA發現異常時間段,則可以深入檢查每5分鐘的具體數據。
REM 在這個腳本中接受一個參數,表示需要報告的時間跨度。
REM 對于runing queue的門限值應該設置為CPU數,
REM 表示假如出現大量等待執行的任務就報警,這通常表示CPU負載過重。
REM 對于page scan(sr)的門限值設置為1,
REM 表示只要出現page daemon掃描頁就報警,這通常表示內存不足。
REM 對于CPU利用率,設置為70,表示超過70%以上的利用率才報警。
REM 比如運行vmstat_alert 7
REM 表示輸出當前日期之前7天之內的執行隊列大于4,
REM sr大于1,CPU利用率超過70%的按照小時統計的報告。
REM ----------------------------------------
set lines 80;
set pages 999;
set feedback off;
set verify off;
column my_date heading 'date hour' format a20
column c2 heading waitq format 9999
column c3 heading pg_in format 9999
column c4 heading pg_ot format 9999
column c5 heading usr format 9999
column c6 heading sys format 9999
column c7 heading idl format 9999
column c8 heading wt format 9999
ttitle 'run queue > CPUsMay indicate an overloaded CPUWhen runqueue exceeds the number of CPUs on the server, tasks are waiting for service.';
select
server_name,
to_char(start_date,'YY/MM/DD HH24') my_date,
avg(running_queue) c2,
avg(kbytes_page_in) c3,
avg(kbytes_page_out) c4,
avg(user_cpu) c5,
avg(system_cpu) c6,
avg(idle_cpu) c7
from
perfstat.stats$vmstat
WHERE
running_queue > 4
and start_date > sysdate-&1
group by
server_name,
to_char(start_date,'YY/MM/DD HH24')
ORDER BY
server_name,
to_char(start_date,'YY/MM/DD HH24')
;
ttitle 'page_scan > 1May indicate overloaded memoryWhenever Unix performs a page-in, the RAM memory on the server has been exhausted and swap pages are being used.';
select
server_name,
to_char(start_date,'YY/MM/DD HH24') my_date,
avg(running_queue) c2,
avg(kbytes_page_in) c3,
avg(kbytes_page_out) c4,
avg(user_cpu) c5,
avg(system_cpu) c6,
avg(idle_cpu) c7
from
perfstat.stats$vmstat
WHERE
page_scan > 1
and start_date > sysdate-&1
group by
server_name,
to_char(start_date,'YY/MM/DD HH24')
ORDER BY
server_name,
to_char(start_date,'YY/MM/DD HH24')
;
ttitle 'user+system CPU > 70%Indicates periods with a fully-loaded CPU subssystem.Periods of 100% utilization are only a concern when runqueue values exceeds the number of CPs on the server.';
select
server_name,
to_char(start_date,'YY/MM/DD HH24') my_date,
avg(running_queue) c2,
avg(kbytes_page_in) c3,
avg(kbytes_page_out) c4,
avg(user_cpu) c5,
avg(system_cpu) c6,
avg(idle_cpu) c7
from
perfstat.stats$vmstat
WHERE
(user_cpu + system_cpu) > 70
and start_date > sysdate-&1
group by
server_name,
to_char(start_date,'YY/MM/DD HH24')
ORDER BY
server_name,
to_char(start_date,'YY/MM/DD HH24')
;
# vmstat_alert.ksh
#----------------------------------------
# 可以將此shell加入cron中,天天7點運行
#----------------------------------------
#!
/bin/ksh
#----------------------------------------
# 首先設定環境變量,根據實際環境修改. . . .
# 接受一個參數輸入,表示當前要報告的數據庫SID
#----------------------------------------
ORACLE_SID=$1
export ORACLE_SID
ORACLE_HOME=`cat /var/opt/oracle/oratabgrep $ORACLE_SID:cut -f2 -d':'`
export ORACLE_HOME
PATH=$ORACLE_HOME/bin:$PATH
export PATH
SCRIPT_PATH=`echo ~oracle/vmstat`
export SCRIPT_PATH
sqlplus perfstat/perfstat<<!
spool /tmp/vmstat_$ORACLE_SID.lst
@$SCRIPT_PATH/vmstat_alert 7 4
spool off;
exit;
!
#----------------------------------------
# 檢查vmstat_alert.sql的輸出結果
# 正常情況應該只包含下面2行
# SQL> @/export/home/oracle/vmstat/vmstat_alert 7
# SQL> spool off;
# 假如超過3行則表示有異常值,那么直接郵件給DBA
#----------------------------------------
check_stat=`cat /tmp/vmstat_$ORACLE_SID.lstwc -l`;
oracle_num=`expr $check_stat`
if [ $oracle_num -gt 3 ]
then
cat /tmp/vmstat_$ORACLE_SID.lstmailx -s "System vmstat alert" kamus@itpub.net some_other_dba@mail.address.net
fi
1. 創建crontab作業,天天7點定時執行vmstat_alert.ksh腳本
$ crontab –l > oracle.cron
$ echo ’00 7 * * * /export/home/oracle/vmstat/vmstat_alert.ksh kamusdb >> /export/home/oracle/vmstat/runalert.lst 2>&1’ >> oracle.cron
$ crontab oracle.cron
2) 每小時趨勢報告
# rpt_vmstat_hr.sql
REM ----------------------------------------
REM 該SQL用于報告Oracle環境中每個服務器一天內小時平均的CPU使用率
REM 接受一個參數,用于指定需要報告的日期,格式為YYYYMMDD
REM ----------------------------------------
connect perfstat/perfstat;
set pages 9999;
set feedback off;
set verify off;
column server_name heading 'server' format a10
column my_hour heading 'hour' format a10
column c2 heading runq format 9999
column c3 heading pg_in format 9999
column c4 heading pg_ot format 9999
column c5 heading usr format 9999
column c6 heading sys format 9999
column c7 heading idl format 9999
column c8 heading wt format 9999
select server_name,
to_char(start_date, 'HH24') my_hour,
avg(runing_queue) c2,
avg(kbytes_page_in) c3,
avg(kbytes_page_out) c4,
avg(user_cpu + system_cpu) c5,
avg(system_cpu) c6,
avg(idle_cpu) c7
from stats$vmstat
where trunc(start_date) = to_date(&1,'yyyymmdd')
group BY server_name,to_char(start_date, 'HH24')
order by server_name,to_char(start_date, 'HH24');
3) 周趨勢報告
# rpt_vmstat_dy.sql
REM ----------------------------------------
REM 該SQL用于報告本周內Oracle環境中每個服務器的日平均CPU使用率
REM ----------------------------------------
connect perfstat/perfstat;
set pages 9999;
set feedback off;
set verify off;
column server_name heading 'server' format a10
column my_day heading 'day' format a20
column c2 heading runq format 9999
column c3 heading pg_in format 9999
column c4 heading pg_ot format 9999
column c5 heading usr format 9999
column c6 heading sys format 9999
column c7 heading idl format 9999
column c8 heading wt format 9999
select server_name,
to_char(start_date, 'day') my_day,
avg(runing_queue) c2,
avg(kbytes_page_in) c3,
avg(kbytes_page_out) c4,
avg(user_cpu + system_cpu) c5,
avg(idle_cpu) c7
from stats$vmstat
where trunc(start_date) >= trunc(next_day(sysdate, 'MONDAY')) - 7
and trunc(start_date) < trunc(next_day(sysdate, 'MONDAY'))
group BY server_name, to_char(start_date, 'day')
order by server_name, to_char(start_date, 'day');
4) 長期趨勢報告
# rpt_vmstat.sql
REM ----------------------------------------
REM 該SQL用于報告Oracle環境中每個服務器日平均的CPU使用率
REM 報告范圍為已搜集的所有數據
REM ----------------------------------------
connect perfstat/perfstat;
set pages 9999;
set feedback off;
set verify off;
column server_name heading 'server' format a10
column my_date heading 'date' format a20
column c2 heading runq format 9999
column c3 heading pg_in format 9999
column c4 heading pg_ot format 9999
column c5 heading usr format 9999
column c6 heading sys format 9999
column c7 heading idl format 9999
column c8 heading wt format 9999
select server_name,
trunc(start_date) my_date,
avg(runing_queue) c2,
avg(kbytes_page_in) c3,
avg(kbytes_page_out) c4,
avg(user_cpu + system_cpu) c5,
avg(idle_cpu) c7
from stats$vmstat
group BY server_name, trunc(start_date)
order by server_name, trunc(start_date);