Discussion:
クエリタイムアウトについて
(too old to reply)
keikon97
2006-02-08 06:00:27 UTC
Permalink
あるクエリをループにて何回も流す処理を行っています。
回数が少ない分には問題ないのですが、流す回数が多いとクエリタイムアウト(30秒)にて
落ちてしまいます。
ちなみに,クエリタイムアウトの時間を延長するなどの操作はしない方向で考えております。
SQLプロファイラにて、調べてみた所、以下の点がわかりました。

1.クエリタイムアウトとなっている個所ではReadsが必ず「2」多い
2.クエリタイムアウトとなっている個所ではEventClassのStmtCompletedがない。
(通常では、BatchStarting、StmtStarting、StmtCompleted、BatchCompletedの4つが表示される)
3.ロックはかかっていない。
4.クエリタイムアウトとなっている個所では、Execution Plan,Show Plan All,Show
PlanTextの値は通常時と変わらず、ShowPlanStatisticsだけが表示されない
5.パフォーマンスを監視したところ、特にメモリ不足でもない


同じクエリを流しているのに、なぜ落ちる場合があるのかがわかりません。
また、この処理のはじめと終わりでトランザクションの開始と終了を行っているのですが、
そこら辺も絡んでいるのでしょうか??
例えば、トランザクションを開始してからある一定時間が経過すると、COMMITかROLBACKするような設定がSQL SERVERにある等・・・。

考えられる原因と、解析方法があればご教授ください。
TimberLandChapel
2006-02-09 15:01:30 UTC
Permalink
Post by keikon97
あるクエリをループにて何回も流す処理を行っています。
回数が少ない分には問題ないのですが、流す回数が多いとクエリタイムアウト(30秒)にて
落ちてしまいます。
--
お疲れ様です。
TimberLandChapel です。

クエリをループさせているプラットフォームは何でしょう?

T-SQL で WHILE 文を使用していますか?
VB6.0 などから ADO の処理をループさせていますか?
VB.NET などから ADO.NET の処理をループさせていますか?

クエリタイムアウトということなので,プログラムからのアクセスだと予想はできますが。

また,
クエリを発行しているテーブルの情報と,
クエリの詳細を開示いただけますか?

「ロックがかかっていない」というところから,参照系の処理だと予想はできますが,
正確にはロック待ちが発生していないということだと思います。

どういった構成のテーブルにどんなクエリを発行しているのかがわかれば検討できると思います。

プロファイラでのイベントクラスの意味については,
TSQL イベントクラスを Books Online で見ていただければわかると思います。

また,一般的な SQL Server のクエリタイムアウトの情報は
query wait オプションを参照ください。
(これは基本的には大量にメモリを消費するハッシュ演算などでのお話になります)

よろしくお願いいたします。

----------
TimberLandChapel
http://blogs.timberlandchapel.com/blogs/workshop/archive/2006/02/04/649.aspx
keikon97
2006-02-10 02:02:08 UTC
Permalink
TimberLandChapel 様。
お忙しい所、レスありがとうございます。
実行環境ですが、
VBSにて以下のものをループさせています。
簡単に書きますと、
①DB接続

②トランザクション開始

③あるフォルダの中にあるCSVファイルの数を取得し、その数だけループ

④ 更にCSVファイルを読み込み、CSVファイルの行数分ループ

⑤ 「TEST」TBLにCSVと同じ内容のデータがあれば、更新、なければ挿入
↓ 
⑥正常に処理が行われていれば、④、または③へ戻ってひきつづき処理を行う
↓  エラーが発生していた場合はここでROLLBACK
⑦ 全てのループから抜けたらここでCommit、DB接続解除
といった感じです。
以下、実際のソースを載せました。
見にくかったらすみません(--;)

また、補足ですが、更にその後パフォーマンスモニタにて、色々試してみたところ、
この処理を流した場合、以下のカウンタに変化が見られました。
・SQLSever:Databasesオブジェクト 
 LogCache Reads/Secカウンタ →平常時(0)から処理が落ちた時に一時的に100まで跳ね上がった
・SQLSever:Locksオブジェクト 
 Lock Requests/Secカウンタ →処理を流したと同時に100まで跳ね上がり、処理が落ちたと同時に40~45(平常時)まで戻った
・SQLSever:Memory Managerオブジェクト 
 Lock Blocksカウンタ →処理を流してしばらくしてから一定間隔であがりつづけ、80くらいに達して落ちたと同時に平常時(50)まで戻った
 Lock Ownerカウンタ →処理を流してしばらくしてから一定間隔であがりつづけ、80くらいに達して落ちたと同時に平常時(50)まで戻った

この中で、特にSQLSever:Memory ManagerオブジェクトのLock
BlocksとLock Ownerが原因に絡んでいるのではないかと思っているのですが、
ロックブロックが何かよくわからずに先に進めずにおります。
ちなみに、以前プロファイラにて、Event Classのロックに関する項目にチェックを入れて処理を流してみたところ、ロックに関する文字はありませんでしたので、
ABENDの原因はロックではないと思っていたのですが、このロックとロックブロックとは別のものなのでしょうか?
そして、パフォーマンスモニタのロックブロックの値が一定間隔であがっていることからしてやはりロックが原因で落ちているのでしょうか・・?
また、ABEND時のロックブロックの詳細をみるにはどうしたらよいのでしょうか・・?
わからないことだらけです(TT)


 



Sub main()
'=======================================================================
' 準備
'=======================================================================
Dim dsn
Dim rcd
'** DSN 設定
dsn = "接続情報"
'** ODBC 接続
On Error Resume Next
Set s3cn_ado = CreateObject("ADODB.Connection")
s3cn_ado.Open dsn
s3cn_ado.CursorLocation = 3

rcd = Err.number

If rcd <> 0 Then
MsgBox "データベースに接続出来ません。"
Exit Sub
else
msgbox "接続"
End If
Call EXECUTE
Call DB_CLOSE
End Sub

'=======================================================================
' 後始末
'=======================================================================
Private Sub DB_CLOSE()

'** ODBC 接続解除
On Error Resume Next
s3cn_ado.Close
'On Error GoTo 0
End Sub

'=======================================================================
' メイン
'=======================================================================
Private Sub EXECUTE()

'** RDB 処理
If RdbShori() = False Then
s3cn_ado.RollbackTrans
'errmsg = "Description=" & objErrItem.Description & "Number=" &
objErrItem.Number
Call fncLogPut (Msg, gstrPath & "log.txt")
MsgBox Msg & "RDB 更新に失敗しました。"
Else
Call fncLogPut (Msg, gstrPath & "log.txt")
MsgBox "RDB 更新正常終了。"
End If

End Sub
'=======================================================================
' RDB 処理
'=======================================================================
Function RdbShori()
Dim strPath
Dim strflpt 'OPENファイルNAME保持用変数
Dim strData
Dim strLen
Dim strStr1, strStr2
Dim Filenames
Dim i
Dim objErrItem
Dim rstWork
dim myFSO
Dim myfile
Dim f1
Dim fc
Dim myFolder
Dim p
'** エラーセット
RdbShori = False
'** トランザクション開始
s3cn_ado.BeginTrans
'** エラートラップ開始
On Error Resume Next

SET rstwork = createobject("adodb.recordset")
set myFso = CreateObject("Scripting.FileSystemObject")
'フォルダ内のファイル数を取得し、その数だけループ
Set myFolder = myFso.GetFolder(gstrPath)
Set fc = myFolder.Files
For Each f1 in fc
Filenames = f1.name
If Filenames = "" Then
MsgBox "このフォルダには.csvブックは存在しません。"
Exit Function
ElseIF RIGHT(Filenames,3) = "csv" Then
strPath = gstrPath & Filenames
set myfile = myFso.OpenTextFile(strPath, 1,true)
blnFileFlg = True
II = 0
p = 1
Do While Not myfile.AtENDOFSTRG ' ファイルの終端までループを繰り返す

mytxt = myfile.ReadLine
For I = 0 TO 9
             strHBuf(i) = Left(mytxt,Instr(mytxt,",") - 1)
        mytxt = Mid(mytxt,Instr(mytxt,",") + 1)
If Instr(mytxt,",") = 0 then
             Exit For
           End IF
Next
strHbuf(9) = mytxt
     Erase strRow
strRow(0) = strHBuf(0)
strRow(1) = strHBuf(1)
strRow(2) = strHBuf(2)
strRow(3) = strHBuf(3)
strRow(4) = strHBuf(4)
strRow(5) = strHBuf(5)
strRow(6) = strHBuf(6)
strRow(7) = FormatDateTime(Now, 0)
strRow(8) = FormatDateTime(Now, 0)
strRow(9) = strHBuf(9)
'**データ型,桁数を調べる

'**TESTTBLのレコード存在チェック
strSql = ""
strSql = "SELECT * "
strSql = strSql & "FROM TEST "
strSql = strSql & "WHERE TEST.A = '" & strRow(0) & "'"
strSql = strSql & " AND FRIM(TEST.B) = '" & strRow(1) & "'"
strSql = strSql & " AND TEST.C = '" & strRow(2) & "'"
rstWork.Open strSql, s3cn_ado, 3,1
If rstWork.EOF Then

'** TESTにデータがなければ挿入、あれば更新
strSql = ""
strSql = "INSEF INTO TEST Values "
strSql = strSql & "( '" & strRow(0) & "', "
     strSql = strSql & "'" & strRow(1) & "', "
strSql = strSql & "'" & strRow(2) & "', "
strSql = strSql & strRow(3) & ", "
strSql = strSql & strRow(4) & ", "
strSql = strSql & strRow(5) & ", "
strSql = strSql & strRow(6) & ", "
strSql = strSql & "'" & CDate(strRow(7)) & "', "
strSql = strSql & "'" & CDate(strRow(8)) & "', "
strSql = strSql & "'" & strRow(9) & "')"
Else

strSql = ""
'strSql = "SET STATISTICS
PROFILE ON"
strSql = strSql & "UPDATE TEST "
'strSql = strSql & "SET A = '" & strRow(0) & "', "
'strSql = strSql & "B = '" & strRow(1) & "' , "
'strSql = strSql & "C = '" & strRow(2) & "', "
strSql = strSql & "SET D = " & strRow(3) & ", "
strSql = strSql & "E = " & strRow(4) & ", "
strSql = strSql & "F = " & strRow(5) & ", "
strSql = strSql & "G = " & strRow(6) & ", "
'strSql = strSql & "H = '" & CDate(strRow(7)) & "', "
strSql = strSql & "I = '" & CDate(strRow(8)) & "', "
strSql = strSql & "J = '" & strRow(9) & "' "
strSql = strSql & " WHERE TEST.A = '" & strRow(0) & "'"
strSql = strSql & " AND TEST.B = '" & strRow(1) & "'"
strSql = strSql & " AND TEST.C = '" & strRow(2) & "'"

         'strSql = strSql & ":: fn_trace_getinfo(default)"
End If

  s3cn_ado.Execute (strSql)
'**エラーナンバーが0以外だったらロールバックして終了
If Err.Number <> 0 Then
s3cn_ado.RollbackTrans
Set objErrItem = s3cn_ado.Errors.Item(0)
errmsg = "Description=" & objErrItem.Description &
"Number=" & objErrItem.Number
Call fncLogPut (errmsg, gstrPath & "log.txt")
Exit Function
     End If

rstWork.Close
P = P + 1
Loop
Msg = Msg & vbCrLf & Filenames & "の処理完了。"

'**csvファイルを閉じる
myfile.Close
end if
Next
'** エラートラップ終了
'** トランザクション終了
s3cn_ado.CommitTrans
'** 正常セット
RdbShori = True
End Function
'**********************************************************************
'**********************************************************************
Function fncLogPut(ByVal strLog, ByVal strFlNm)
Dim objFso
Dim objLog
Dim strFilePath
strFilePath = strFlNm
Set objFso = CreateObject("Scripting.FileSystemObject")
Set objLog = objFso.OpenTextFile(strFilePath, 8, True)
objLog.WriteLine (strLog )
objLog.Close
Set objLog = Nothing
Set objFso = Nothing
End Function
Post by TimberLandChapel
Post by keikon97
あるクエリをループにて何回も流す処理を行っています。
回数が少ない分には問題ないのですが、流す回数が多いとクエリタイムアウト(30秒)にて
落ちてしまいます。
--
お疲れ様です。
TimberLandChapel です。
クエリをループさせているプラットフォームは何でしょう?
T-SQL で WHILE 文を使用していますか?
VB6.0 などから ADO の処理をループさせていますか?
VB.NET などから ADO.NET の処理をループさせていますか?
クエリタイムアウトということなので,プログラムからのアクセスだと予想はできますが。
また,
クエリを発行しているテーブルの情報と,
クエリの詳細を開示いただけますか?
「ロックがかかっていない」というところから,参照系の処理だと予想はできますが,
正確にはロック待ちが発生していないということだと思います。
どういった構成のテーブルにどんなクエリを発行しているのかがわかれば検討できると思います。
プロファイラでのイベントクラスの意味については,
TSQL イベントクラスを Books Online で見ていただければわかると思います。
また,一般的な SQL Server のクエリタイムアウトの情報は
query wait オプションを参照ください。
(これは基本的には大量にメモリを消費するハッシュ演算などでのお話になります)
よろしくお願いいたします。
----------
TimberLandChapel
http://blogs.timberlandchapel.com/blogs/workshop/archive/2006/02/04/649.aspx
TimberLandChapel
2006-02-10 03:55:27 UTC
Permalink
Post by keikon97
TimberLandChapel 様。
お忙しい所、レスありがとうございます。
実行環境ですが、
VBSにて以下のものをループさせています。
簡単に書きますと、
①DB接続

②トランザクション開始

③あるフォルダの中にあるCSVファイルの数を取得し、その数だけループ

④ 更にCSVファイルを読み込み、CSVファイルの行数分ループ

⑤ 「TEST」TBLにCSVと同じ内容のデータがあれば、更新、なければ挿入
↓ 
⑥正常に処理が行われていれば、④、または③へ戻ってひきつづき処理を行う
↓  エラーが発生していた場合はここでROLLBACK
⑦ 全てのループから抜けたらここでCommit、DB接続解除
--
お疲れ様です。
TimberLandChapel です。

まず,一般的な話として,
トランザクションの実行時間は可能な限り短くしなければなりません。

今のスクリプトの構成ですと,トランザクション実行中に,更新用クエリの組み立てが行われています。

■更新用のクエリ構築はトランザクション実行の外に出すべきです。

配列変数などに,実行する INSERT,UPDATE 分をプールして,
必要な更新処理の一群が出来上がってからトランザクションで一気に実行する

または,
ADO.Recordset の BatchUpdate の機能を利用して一括更新を行う

などのようにスクリプトの構成を見直されたほうが良いと思います。

あとは,CRUD を気にしながらロックを調べてください。
この場合,Cache などのオブジェクトはまだ見る必要がないと思います。

「sp_lock」「現在の利用状況」あたりから調べてください。

こちらは Web の話ですが参考に
 ↓
http://www.microsoft.com/japan/msdn/sqlserver/columns/webtech/webtech2.asp
----------
TimberLandChapel
http://blogs.timberlandchapel.com/blogs/workshop/archive/2006/02/04/649.aspx
keikon97
2006-02-10 09:52:27 UTC
Permalink
お疲れ様です。
TimberLandChapel様。

丁寧なアドバイスをありがとうございました。
検証しまして、再度ご報告致しますね。
ひとまず失礼致します。
keikon97
2006-02-13 08:19:27 UTC
Permalink
TimberLandChapel 様

お世話になってます。
先日の
Post by TimberLandChapel
配列変数などに,実行する INSERT,UPDATE 分をプールして,
必要な更新処理の一群が出来上がってからトランザクションで一気に実行する
または,
ADO.Recordset の BatchUpdate の機能を利用して一括更新を行う
などのスクリプトの見直しを行う必要がある
とのアドバイスを参考にいたしまして、とりあえず、配列変数を使用し、
必要更新処理を文字列として格納してから、
トランザクションを開始し、ループで
更新処理のみを行ってみました。

結果、やはり、ロックブロックで落ちました・・・。
この場合のように、いっきにトランザクションを開始してから更新処理だけをまとめて行う場合でも、
途中でCommit文を入れないとだめなのだろうかと思い
ループの中に、IF文を入れて、600レコード更新した時点で一回Commitを行う
という作りにしてみたところ、うまく最後までいきました!!


また、もうひとつの方法の
ADO.Recordset の BatchUpdateの機能を利用して一括更新を行うという方法ですが、
全く知識がなかったため、色々ネットで調べて以下のようなサンプルコードを見つけたのですが、
「必要な変更を全て格納する」という部分が具体例として載っていなかったので、いまいち使用方法がわかりませんでした。

例えば、TESTテーブルのDATE(日付)列をまとめて更新したい場合、

変数"A"に "UPDATE TEST SET DATE = '20060101',UPDATE TEST SET DATE =
'20060102'......"
というような変更SQL文を羅列したものを格納し、

rsSQL.UpdateBatch A
でいっきに変更できるということなのでしょうか・・・?

参考例など、アドバイスをいただければありがたいです。。
よろしくお願いいたします。


Dim cnSQL As ADODB.Connection
Const strCN_STRING As String = "Provider=SQLOLEDB.1;" & _
"Data Source=SRVNAME;Initial Catalog=DBNAME;"

・・・(必要な変更を全て格納します)・・・

Set cnSQL = New ADODB.Connection
cnSQL.Open strCN_STRING ・・・①
Set rsSQL.ActiveConnection = cnSQL ・・・②
rsSQL.UpdateBatch ・・・③
TimberLandChapel
2006-02-14 03:46:28 UTC
Permalink
Post by keikon97
結果、やはり、ロックブロックで落ちました・・・。
例えば、TESTテーブルのDATE(日付)列をまとめて更新したい場合、
Dim cnSQL As ADODB.Connection
Const strCN_STRING As String = "Provider=SQLOLEDB.1;" & _
"Data Source=SRVNAME;Initial Catalog=DBNAME;"
・・・(必要な変更を全て格納します)・・・
Set cnSQL = New ADODB.Connection
cnSQL.Open strCN_STRING ・・・①
Set rsSQL.ActiveConnection = cnSQL ・・・②
rsSQL.UpdateBatch ・・・③
--
おつかれさまです。
TimberLandChapel です。

まず前半から,
ブロック時に sp_lock を実行するとどんな結果になっていますか?
テーブル内のロックオブジェクトを確認すると,さらにもう一歩進むかもしれません。

後半の BatchUpdate ですが,
こちらは,Recordset オブジェクトに変更を格納していく形になります。

strSql ="SELECT * FROM [TEST];"
rsSQL.Open strSql, Conn

とした上で,
For Loop などでレコード数分ループするなどして,

rsSQL.Fields("DATE") = <更新する値>

といったように,

①更新したいレコードセットを作成
②レコードセットのフィールドを更新
③BatchUpdate

という構成をとります。

古い記事ですがまとめたものがありますので参考まで。
http://timberlandchapel.com/adotips.html

----------
TimberLandChapel
http://blogs.timberlandchapel.com/blogs/workshop/archive/2006/02/04/649.aspx
keikon97
2006-02-16 09:46:29 UTC
Permalink
TimberLandChapel様

お疲れ様です。
早々とアドバイスを頂いたのにご報告が遅くなりすみません。

まず、
Post by TimberLandChapel
ブロック時に sp_lock を実行するとどんな結果になっていますか?
sp_lockを実行すると通常10行ほどしか表示されませんが、
この処理を流すと、
トランザクションを開始して更新処理を始めて
1クエリを流すごとに100行単位で増えていき、
3000行を過ぎたところで落ちました。
以下、落ちる直前のsp_lockを実行した結果になります。

spid 2桁の同一ID
dbid 1桁の同一ID
ObjId 11桁の同一ID
IndID 1
Type KEY
Mode X
Status GRANT

ObjIdが何を表しているのかわからないのですが、3000行とも同じIDなので、このID番号が原因なのでしょうか・・・?
Post by TimberLandChapel
テーブル内のロックオブジェクトを確認すると,さらにもう一歩進むかもしれません。
ロックオブジェクトの見方がわからないのですが・・・(TT)
できれば詳しく教えていただければとてもありがたいです。
Post by TimberLandChapel
後半の BatchUpdate ですが,
こちらは,Recordset オブジェクトに変更を格納していく形になります。
こちらのほうは時間の関係でまだ試していませんが、
また、できましたらご報告したいと思います。

よろしくお願いいたします。
TimberLandChapel
2006-02-20 03:41:26 UTC
Permalink
Post by keikon97
ロックオブジェクトの見方がわからないのですが・・・(TT)
できれば詳しく教えていただければとてもありがたいです。
--
TimberLandChapel です。

なんかこの辺に記事を書いたような記憶があります。
このときはデッドロックだったような気がしますが。。。

http://blogs.timberlandchapel.com/blogs/timberlandchapel/archive/2005/06/28/22.aspx

----------
TimberLandChapel
http://blogs.timberlandchapel.com/blogs/workshop/
Loading...