Oracle Database Standard
Editionでの運用いろいろ
  
  
  
  
GMOメディア株式会社 
北川 健太郎 
2016/09/12 
JPOUG in 15 minutes #1
自己紹介
名前: 北川健太郎
twitter:@keny_lala
github:kenken0807
所属
GMOメディア株式会社
DBA歴3年目
社内
MySQL:99%
Oracle Database:1%
最近はMySQLばかり触ってます。
1.SEでActive Session History
(ASH)っぽく解析する仕組み
はじめに
   
私はASHを使ったことないので解析とかこんな感じじゃな
いのかな。                             
 
ってことでいろいろ試行錯誤した結果で作ってますので
ORACLEのすごい人たち(皆様)から見て間違いがあれ
ば教えて下さい。                         
きっかけ
 
   
開発:「先週ぐらいにセッション詰まったっぽいんですが何
が原因?」
SE環境で調べられること
 
   
statspackぐらいしかない
原因を調査できる仕組みを
作る
15秒おきにv$sessionの情報を取得
mysql> SELECT sysdate AS SNAPSHOT_DATE , s.*
FROM v$session s
WHERE s.STATUS = 'ACTIVE'
AND s.TYPE = 'USER';
取得する項目                                 
1.  v$sessionすべてと取得時間(SNAPSHOT_DATE)       
 
取得する条件                                 
1.  STATUSがACTIVE                          
2.  TYPEがUSER 
(ACTIVEなユーザセッションを取得)
グラフ化
参考:Oracle®Database2日でパフォーマンス・チューニング・ガイド
 
 
v$sessionのwait_classで分かれているようだ           
約5分置きに平均active sessionをグラフ化する 
データストアとしてMySQL     
調査するときにSQLなのでわかりやすいし慣れてる
 
グラフ表示にMUNIN         
プラグインの作成が簡単で汎用的           
入社時点で使われてた(主にこれ)           
 
2つともOSS
調査する
例えばこんなグラフ 
2016­06­10 01:00~2016­06­10 01:10のセッション状況を確認
mysql> SELECT snapshot_date,wait_class,event ,count(*)
FROM session
WHERE snapshot_date between '2016-06-10 01:00:00' and '2016-06-10 01:10:00'
GROUP BY 1,2,3;
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| snapshot_date | wait_class | event | count(*) |
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
・
・
| 2016-06-10 01:06:45 | Application | enq: TX - row lock contention | 39 |
| 2016-06-10 01:06:45 | Idle | SQL*Net message from client | 2 |
| 2016-06-10 01:07:01 | Application | enq: TX - row lock contention | 41 |
| 2016-06-10 01:07:01 | Idle | SQL*Net message from client | 1 |
| 2016-06-10 01:07:01 | Network | SQL*Net message to client | 2 |
| 2016-06-10 01:07:16 | Application | enq: TX - row lock contention | 49 |
| 2016-06-10 01:07:16 | Idle | SQL*Net message from client | 1 |
| 2016-06-10 01:07:31 | Application | enq: TX - row lock contention | 56 |
| 2016-06-10 01:07:31 | Idle | SQL*Net message from client | 5 |
| 2016-06-10 01:07:46 | Application | enq: TX - row lock contention | 63 |
| 2016-06-10 01:07:46 | Idle | SQL*Net message from client | 4 |
| 2016-06-10 01:08:01 | Application | enq: TX - row lock contention | 92 |
| 2016-06-10 01:08:01 | Idle | SQL*Net message from client | 2 |
・
・
enq: TX ­ row lock contentionが多発
で、そのうち最も詰まってた時間帯を確認
mysql? SELECT
SNAPSHOT_DATE,
MACHINE,
SID,SERIAL_NO,
SQL_ID,EVENT ,
SECONDS_IN_WAIT ,
LAST_CALL_ET ,
ROW_WAIT_OBJ_NO,
ROW_WAIT_FILE_NO,
ROW_WAIT_BLOCK_NO,
ROW_WAIT_ROW_NO,
BLOCKING_SESSION
FROM session
WHERE SNAPSHOT_DATE = '2016-06-10 01:08:01'
――――――――――――――――――――――――――――――――――――――――――――――――――――――
| SNAPSHOT_DATE | MACHINE | sid | serial_no | SQL_ID
――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 2016-06-10 01:08:01 | ap3 | 1218 | 43717 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap3 | 92 | 8383 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 767 | 26109 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap3 | 2358 | 36357 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 844 | 20511 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 2525 | 4179 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 1693 | 22893 | 3tn6dvb9udn7j
――――――――――――――――――――――――――――――――――――――――――――――――――
| EVENT |SECONDS_IN_WAIT|LAST_CALL_ET
――――――――――――――――――――――――――――――――――――――――――――――――――
| enq: TX - row lock contention | 47 | 47
| enq: TX - row lock contention | 11 | 11
| enq: TX - row lock contention | 11 | 12
| enq: TX - row lock contention | 8 | 9
| enq: TX - row lock contention | 180 | 180
| enq: TX - row lock contention | 42 | 42
| enq: TX - row lock contention | 159 | 159
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| ROW_WAIT_OBJ_NO | ROW_WAIT_FILE_NO | ROW_WAIT_BLOCK_NO| ROW_WAIT_ROW_NO | BLOCKING_SESSION
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 104277 | 10 | 571485| 8 | 704
| 104277 | 8 | 564993| 12 | 704
| 104277 | 8 | 564993| 12 | 704
| 104277 | 8 | 564993| 12 | 704
| 104277 | 10 | 571485| 8 | 704
| 104277 | 8 | 561276| 25 | 704
| 104277 | 10 | 571485| 8 | 704
ここまでわかること
   
1.  各セッションがどれくらい待機しているか。
2.  各セッションが実行しているSQL。
3.  各セッションが待機している行はどこか。
4.  どのセッションが原因で待機しているか。
1. 各セッションがどれくらい待機しているか。
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| SNAPSHOT_DATE | sid | EVENT | STATE |SECONDS_IN_WAIT|LAST_CALL_ET
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 2016-06-10 01:08:01 | 1218 | enq: TX - row lock | WAITING | 47 | 47
| 2016-06-10 01:08:01 | 92 | enq: TX - row lock | WAITING | 11 | 11
| 2016-06-10 01:08:01 | 767 | enq: TX - row lock | WAITING | 11 | 12
| 2016-06-10 01:08:01 | 2358 | enq: TX - row lock | WAITING | 8 | 9
| 2016-06-10 01:08:01 | 844 | enq: TX - row lock | WAITING | 180 | 180
| 2016-06-10 01:08:01 | 2525 | enq: TX - row lock | WAITING | 42 | 42
| 2016-06-10 01:08:01 | 1693 | enq: TX - row lock | WAITING | 159 | 159
| 2016-06-10 01:08:01 | 2212 | enq: TX - row lock | WAITING | 10 | 11
| 2016-06-10 01:08:01 | 919 | enq: TX - row lock | WAITING | 37 | 37
SECONDS_IN_WAIT・・セッションが待機中
(STATE=WAITING)の場合、現在の待機が開始されるまで待
機した秒数。 
WAIT_TIME_MICRO、TIME_SINCE_LAST_WAIT_MICRO列が設定されている場合、この列は非推奨
になる。
LAST_CALL_ET・・STATUSが現在ACTIVEである場合は、セ
ッションがアクティブになってからの経過時間(秒)を表す。
2. 各セッションが実行しているSQL。
――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| SNAPSHOT_DATE | MACHINE | sid | serial_no | SQL_ID
――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 2016-06-10 01:08:01 | ap3 | 1218 | 43717 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap3 | 92 | 8383 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 767 | 26109 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap3 | 2358 | 36357 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 844 | 20511 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 2525 | 4179 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 1693 | 22893 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap2 | 2212 | 8155 | 3tn6dvb9udn7j
| 2016-06-10 01:08:01 | ap3 | 919 | 25535 | 3tn6dvb9udn7j
SQL_IDから共有プールに残っていればv$sqltext、または
statspackのstats$sqltextからわかる。 
(ここはORACLEから確認する)
oracle> SELECT sql_text FROM v$sqltext WHERE SQL_ID = '3tn6dvb9udn7j' ORDER BY piece
3. 各セッションが待機している行はどこか。
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| sid |ROW_WAIT_OBJ_NO |ROW_WAIT_FILE_NO |ROW_WAIT_BLOCK_NO|ROW_WAIT_ROW_NO
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 1218| 104277 | 10 | 571485| 8
| 92| 104277 | 8 | 564993| 12
| 767| 104277 | 8 | 564993| 12
| 2358| 104277 | 8 | 564993| 12
| 844| 104277 | 10 | 571485| 8
| 2525| 104277 | 8 | 561276| 25
| 1693| 104277 | 10 | 571485| 8
| 2212| 104277 | 8 | 564993| 12
| 919| 104277 | 8 | 564993| 12
ROW_WAIT_OBJ_NO →V$SESSION.ROW_WAIT_OBJ#
ROW_WAIT_FILE_NO→V$SESSION.ROW_WAIT_FILE#
ROW_WAIT_BLOCK_NO→V$SESSION.ROW_WAIT_BLOCK#
ROW_WAIT_ROW_NO→V$SESSION.ROW_WAIT_ROW#
MySQLではカラム名に#を使う場合はバッククォートで囲う必要が
あるためNOにした
3. 各セッションが待機している行はどこか。
(ORACLEから確認) 
dba_objectsテーブルとdbms_rowidパッケージを使用して    
対象のテーブルとROWIDを取得                    
orscle> SELECT object_name,dbms_rowid.rowid_create (1,data_object_id,{ROW_WAIT_FILE_NO},
{ROW_WAIT_BLOCK_NO},{ROW_WAIT_ROW_NO}) FROM dba_objects WHERE data_object_id={ROW_WAIT_OBJ_NO}
――――――――――――――――――――――――――――――――
|OBJECT_NAME | DBMS_ROWID.ROWID_C
――――――――――――――――――――――――――――――――
|USERS_TABLE | AAAZdVAAKAACLhdAAI
OBJECT_NAME・・USERS_TABLE                 
ROWID・・AAAZdVAAKAACLhdAAI
 
ROWIDを元に対象の行を取得                      
oracle> SELECT * FROM USERS_TABLE WHERE rowid='AAAZdVAAKAACLhdAAI';
4. どのセッションが原因で待機しているか。
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| SNAPSHOT_DATE | MACHINE | sid | serial_no | BLOCKING_SESSION
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 2016-06-10 01:08:01 | ap3 | 1218 | 43717 | 704
| 2016-06-10 01:08:01 | ap3 | 92 | 8383 | 704
| 2016-06-10 01:08:01 | ap2 | 767 | 26109 | 704
| 2016-06-10 01:08:01 | ap3 | 2358 | 36357 | 704
| 2016-06-10 01:08:01 | ap2 | 844 | 20511 | 704
| 2016-06-10 01:08:01 | ap2 | 2525 | 4179 | 704
| 2016-06-10 01:08:01 | ap2 | 1693 | 22893 | 704
| 2016-06-10 01:08:01 | ap2 | 2212 | 8155 | 704
| 2016-06-10 01:08:01 | ap3 | 919 | 25535 | 704
BLOCKING_SESSIONからどのセッションがブロックしているか
わかる 
→この場合はSID 704
待機している原因のSIDはわかったが、
今の仕組みでは
そのセッション情報を取得できてないかも。
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| SNAPSHOT_DATE | sid | status | MACHINE |program | AUDSID | LAST_CALL_ET |
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 2016-06-10 01:08:01 | 704 | INACTIVE| batch-server |JDBC Thin Client| 33893781 | 1 |
   
SID 704のbatch­server のトランザクションが原因でロックして
いることがわかった。
 
犯人はbatch­serverだ!
んで、開発側へ報告。
 
私:「その時間帯はbatch­serverが長いトランザクションだったり、
commitせずに待機してたのが原因でした。(どや顔)」
 
開発:「じゃーそのトランザクションのSQL内容教えて」
 
  私:「Oracleの場合、原因のSQLはわからないんですよ。」
開発:「えっ、DBAなのにそんなこともわからな
いの!??」
 
私:「ぐぬぬ。くっそくっそ」
どうやって原因のSQL調べるか。
調査した結果、無理だった。
   
新久保さんのJPOUGの資料を参考。 
http://www.slideshare.net/kshinkub/jpoug­20120721?qid=7a17193a­07d4­4e42­9dc5­4b0250538747&v=&b=&from_search=6
ただLogMinerを使えば、       
そのトランザクション内での      
更新テーブルと更新内容はわかる 
(サプリメンタルロギング必須)                               
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| SNAPSHOT_DATE | status | MACHINE |program | sid | AUDSID | LAST_CALL_ET |
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
| 2016-06-10 01:08:01 | INACTIVE| batch-server |JDBC Thin Client| 704 | 33893781 | 1 |
――――――――――――――――――――――――――――――――――
|XID | XID_START_DATE |
――――――――――――――――――――――――――――――――――
|5300020053F00900 | 2016-06-10 01:02:11 |
XID・・5300020053F00900
XID_START_DATE・・2016­06­10 01:02:11
 
このXIDを元に対象の時間のアーカイブログから解析。
Oracle­ArchiveLog­Analyzer
https://github.com/kenken0807/Oracle­ArchiveLog­Analyzer
ツールを作成
LogMinerの出力結果を加工して表示
不要なSQL(内部SQLなど)は出力しない
WHERE句内のROWIDをカット
SQLを実行順にいい感じ出力する
実行                                       
$ ./Oracle-ArchiveLog-Analyzer.pl --sid orcl --pass xxxx --xid 5300020053F00900
o1_mf_1_47446_csd0sdrgdas_.arc
   
結果                                       
-- START_TIME: 2016-06-10 01:02:11 COMMIT_TIME: 2016-06-10 01:08:50
-- START_SCN: 47902287496 COMMIT_SCN: 47902287503
-- TRANSACTION ID: 5300020053F00900
-- SESSION INFO: login_username=ORAUSER client_info= OS_username=orcl Machine_name=batch-server
OS_terminal=unknown OS_process_id=1234 OS_program_name=JDBC Thin Client
2016/06/10 01:02:11 : set transaction read write;
2016/06/10 01:02:11 : update "ORAUSER"."USERS_TABLE" set "ID" = '300025', "STATUS_ID" = '1',
"DEL_FLG" = '1' where "ID" = '7599332' and "STATUS_ID" = '1' and "DEL_FLG" is null ;
2016/06/10 01:02:11 : insert into "ORAUSER"."USERS_HISTORY"("ID","TYPE") values
('300025','delete');
2016-06-10 01:08:06 : commit;
これを提出して完了
2.各テーブルのDML回数を保存
1時間置きに以下を実行
DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO;
を実行
dba_tab_modifications テーブルの内容をMySQLに保存
各DML回数は累積なので差分をグラフ化
  注意:統計情報取得時に累積情報はリセットされる
3.各テーブルとインデックスサイズのグラフ化
1時間置きに以下を実行
dba_segmentsからBYTE数を取得
条件:SEGMENT_TYPE in ('INDEX','TABLE')
条件:BYTES > 1GB
テーブルと対応するインデックスをまとめてグラフ化
まとめ
 
Oracle Database Standard Editionでも 
OSSを駆使したり自作すればなんとかなることもある。
ご清聴ありがとうございました

Oracle Database Standard Editionでの運用いろいろ