2011年3月9日 星期三

小插曲 Data Guard Archive Log Manage (Primary Site)

從 9i 開始到現在 10g (11g 小弟還沒使用過) ,所使用過的 DataGuard 有個缺點,就是設定之後,Archive Log 就必須自己管理,當然也可以不需要理會,完全交給 RMAN 備份去刪除就可以了,只是如果你擔心 Data Guard 的 Standby 在還沒傳輸完成及Apply Archive Log 前,Primary Site 的Log就被備份軟體刪除,你就必須自己控制哪個時間點才能刪除,尤其是有時在 dest_2 設定了 ARCH DELAY=XXX 時,萬一斷線發生時,Archvelog 又被備份軟體刪除就有點麻煩,尤其是對我的終端客戶而言,我可不想建立後一天到晚因為客戶的網路問題往那跑,在 UNIX 上面,可以寫 Shell Script 去控制。這裏僅提供 Primary 端的管理,其實 Standby 端的 Script 更簡單,只要修改幾個位置就可以了。

後來我發現,如果利用 RMAN 備份的話,RMAN 好像會去確認 Dataguard 另一端的 Archive Log 有沒有被 Apply ,進而保留未被 Apply 的 LOG ,一直到該 Archive Log 有被 Apply 後,下次的 RMAN 備份就會將之刪除了,所以這個 Script 其實是不需要了。不過這個特性不知是否是 10G 才有,隱約記得以前好像是沒這麼方便。

其實這個 Scripts 還挺簡陋的,不過我也不知道還有沒有更精簡的方法,至目前為止我都是如此使用的

#!/bin/ksh

export ORACLE_SID=PXX
export SCRIPT_HOME=/oracle/PXX/jobs/sapdr
export LOG_HOME=/oracle/PXX/jobs/logs
export DG_HOST=PXX_DR_HOST
export DG_TNSNAME=PXX_DR
TIME=`date +%m%d%H%M`
remote_cmd=rsh

case $remote_cmd in
rsh)
copy_method=rcp
break
;;
ssh)
copy_method=scp
break
;;
esac

#為了將來如果改使用 SSH 時,不需太多變更,因此加了上面的 Case 判斷

trap "rm /tmp/.lock_DG exit -1" 2 3 5 15
if [ -f /tmp/.lock_DG ] ; then
exit
fi
touch /tmp/.lock_DG

#避免此 Script 被重覆執行,因此用 trap 機制避免

cd $SCRIPT_HOME

#check remote site listener is alive
tnsping $DG_TNSNAME
if [ $? -ne 0 ] ; then
echo DG_HOST not exist > /tmp/rmarch.sh.$TIME
rm /tmp/.lock_log
exit 1
fi

#第一部當然要先確認對方的 TNS Names 是否存在

$remote_cmd $DG_HOST "cd $SCRIPT_HOME;sh ./get_MAX_log_dg.sh"

#從 Standby Site 取得目前己被 Apply 的 MAX Log Sequence#

# get_MAX_log_dg.sh 檔案的內容如下
#--> #!/bin/ksh
#--> export SCRIPT_HOME=/oracle/P02/jobs/sapdr
#--> TIME=`date +%m%d%H%M`
#--> echo "spool dg_log.lst" > $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "set echo off" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "set feedback off" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "set heading off" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "select max(sequence#)" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "from v\$log_history" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "where first_time < sysdate-7/24;" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "spool off" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> echo "exit" >> $SCRIPT_HOME/grlist_dg_pri.sql
#--> sqlplus '/ as sysdba' @$SCRIPT_HOME/grlist_dg_pri.sql
#--> rm $SCRIPT_HOME/grlist_dg_pri.sql

list1=`$remote_cmd $DG_HOST "cd $SCRIPT_HOME;cat dg_log.lst" | awk ' NR > 1 { print $1 }'`
echo $list1

$remote_cmd $DG_HOST "cd $SCRIPT_HOME;rm dg_log.lst"

echo "set echo off" > $SCRIPT_HOME/grlist_dg.sql
echo "set feedback off" >> $SCRIPT_HOME/grlist_dg.sql
echo "set heading off" >> $SCRIPT_HOME/grlist_dg.sql
echo "spool standby_log.lst" >> $SCRIPT_HOME/grlist_dg.sql
echo "select max(sequence#)" >> $SCRIPT_HOME/grlist_dg.sql
echo "from v\$log_history" >> $SCRIPT_HOME/grlist_dg.sql
#echo "where first_time < sysdate-1;" >> $SCRIPT_HOME/grlist_dg.sql
echo "where first_time < sysdate;" >> $SCRIPT_HOME/grlist_dg.sql
echo "spool off" >> $SCRIPT_HOME/grlist_dg.sql
echo "exit" >> $SCRIPT_HOME/grlist_dg.sql
sqlplus '/ as sysdba' @$SCRIPT_HOME/grlist_dg.sql

list1=`cat standby_log.lst| awk ' NR > 1 { print $1 }'`
echo $list1

echo "set echo off" > getrmlist.sql
echo "set feedback off" >> getrmlist.sql
echo "set heading off" >> getrmlist.sql
echo "set pagesize" >> getrmlist.sql
echo "spool rm_primary_arch.sh" >> getrmlist.sql
echo "select 'rm '||name " >> getrmlist.sql
echo " from v\$archived_log " >> getrmlist.sql
echo " where dest_id=1" >> getrmlist.sql
echo " and sequence#<${list1}" >> getrmlist.sql
echo " and backup_count>0 " >> getrmlist.sql
echo " and archived='YES'" >> getrmlist.sql
echo " and deleted='NO' " >> getrmlist.sql
echo " order by thread#, sequence#;" >> getrmlist.sql
echo "spool off" >> getrmlist.sql
echo "exit" >> getrmlist.sql

#產生要抓取 Archive Log 清單的 SQL ,但是 MAX Log 是由對面抓回來的,由此判斷己被 Standby 端 Apply 才會刪除

sqlplus "/ as sysdba" @getrmlist.sql > /dev/null
#由上面產生的 SQL 再產生要刪除的 Shell Scripts "rm_primary_arch.sh"

rm $SCRIPT_HOME/getrmlist.sql $SCRIPT_HOME/grlist_dg.sql $SCRIPT_HOME/standby_log.lst
#將本 Scripts 執行過程中產生的臨時程式 SQL 及 清單檔清除,Debug 時請將其 Remark 掉

chmod +x rm_primary_arch.sh
. ./rm_primary_arch.sh > /dev/null 2>&1
mv rm_primary_arch.sh $LOG_HOME/rm_primary_arch.sh."$TIME"

rman target / nocatalog msglog=$LOG_HOME/rmlog_ma_history <allocate channel for maintenance type disk;
change archivelog all crosscheck;
delete noprompt expired archivelog all;
release channel;
EOF
#在此是使用 RMAN 的 Maintenance Channel 來做 Crosscheck

find $LOG_HOME/ -name "rm_primary_arch.sh*" -ctime +7 -exec rm {} \;

#每次執行產生的 Log 使用 find 指令來刪除,以免執行的 Log 保留過多,這邊保留七天的 Log File

rm /tmp/.lock_DG
#最後記得 lock file 要刪掉,不然下次就不會再執行了

2011年3月8日 星期二

第十八回 SAP 建立 Oracle Data Guard 出現奇怪的 TNS 問題

上次的發言記錄是去年的 10/25 ,其實因為一些因緣際會,小弟換了家公司繼續 IT 服務,這幾天在建立一套 DataGuard 環境。發生了一些小插曲。

首先建立 DataGuard ,如果你不是用 Broker 來建立的話,依據文字介面的建立方式的部驟其實不難,這個隨便找也有一堆 Step by Step 的部驟,Metalink 上面也有官方參考的文件可看。我建了那麼多套,其實也沒用過 Broker 或是 GUI 建立,總覺得在命令模式下才會清楚自己究竟在幹麼,而且要操作時,只要有 Telnet 就可以完成全部的事情,而透過 Broker 反而也沒比較好操作。

這次建立的環境是 SAP ERP 的環境,依照同樣的部驟,我完成所有設定後,在啟動 log_archive_dest_2 的 MRP Service 後,發現 Archive Log 並沒有造我的期望傳過去,一直出現 Ora 12154 could not resolve service name 的錯誤,可是明明在 SQLPLUS 之下,我己經確認過在兩邊的 Oracle User 的提示字元下都能透過 TNS 順利連線至對方的資料庫,查了半天,確認沒有問題,又試了一次,結果仍然是一樣 ORA 12154 ,這個錯誤訊息就僅僅是 TNSNAMES.ORA 設定錯誤,讓 Oracle 無法解析指定資料庫的位置而己,而我確定我的資訊都是對的。

重新將整個設定全部確認過並重建後,再重新看了一下 Listener.ora 的設定,發現這台機器之前有疑似 DataGuard 的 Listener 存在,從 Alert Log 的保存資料裏面也有對應資訊,可能是從前有測試過該 Function ,所以我就用之前的 Listener 設定重新又做了一次。

終於..........還是不行,只是錯誤訊息不同,更神了,變成 ORA 12541 No Listener,一整個搞不清楚怎麼回事的狀況下,我只覺得我的 Source Instance 是不是瘋了,好像沒有吃到我的設定一樣。將 Metalink 整個翻過了一遍就是沒有對應的錯誤,尤其是 No Listener 才是怪異,根本不可能的事情啊,後來我將環境還原成原來 ORA 12154 的狀態。然後打電話問前同事有沒有遇過這種怪事。

前同事非常熱情的連線過來幫忙看我的狀況,首先,因為我還沒告知他相關的資料庫資訊,因此,他一連上終端機就用 ps -ef 查詢 Oracle Instance 是否有啟動,然後以 su 方式切換至所獲得的 User 資訊的 OS User 下操作,他一試就發現問題,tnsping 並無法 ping 到對方啊,我說怎可能,我測過兩邊的 sqlplus 都連線成功,怎可能 tnsping 不會過。結果他告知我他使用的是 SAP R3 的那個 Service 帳號,不是 Oracle 的帳號。

一聽到這個寶貴的資訊後,我完全了解是怎麼回事了,我馬上切換成 SAP R3 的帳號,echo $TNS_ADMIN ,果然答案出來了,這個 User 有自己的 TNS_ADMIN 位置,所以我從昨天一整個改到翻掉的ORACLE_HOME 裏的那個預設 tnsnames.ora 根本就不是現在啟動的這個資料庫使用的。

但是....................為什麼 Oracle 啟動會使用 SAP 帳號而不是 Oracle 帳號呢? 這是因為,大部份 SAP 都是透過 SAP User 直接執行 SAP 提供的 scripts,就會將 Database 和 R3 Instance 一起啟動,這時,Oracle Process 的 Owner 就會是 SAP 帳號,因為該帳號也有 DBA 權限,所以他是可以操作 Oracle 的檔案的。

但,如果之前啟動是先 su 至 Oracle 帳號啟動資料庫,再 su 成 SAP R3 帳號啟動 R3 ,就不會有這種問題了。而最終的解決方式是,最好將兩個 User 的 TNS_ADMIN 設定的位置都設為相同,這樣才不會有類似的問題發生。在 Oracle ERP 下面也是要注意類似的問題,因為 Oracle ERP 為了相容舊版的Client ,我記得它有多個 ORACLE_HOME ,但在 SAP 上我就沒想到這個問題。

果然是旁觀者清,我查了快一天的問題,沒有 SAP 經驗的同事連線過來2分鐘就搞定了。而原本 ORA 12541 NO Listener 的錯誤是怎麼回事呢,因為之前有人做過設定,在 SAPR3 的 TNSNAMES 裏面是有定義的,但是目地主機早就不存在了,所以我從 Oracle 裏更改對應資料時,他才出現 ORA 12541 ,因為程式讀的根本就不是我後來設定的目地主機。