問題
現在大多數的Web 應用都需要連接到數據庫, 對數據庫的數據進行操作. 有些時候Web應用對數據庫的數據進行操作時, 會發生一些性能問題. 這個時候如果能找到一些有用的數據, 對性能調優是非常有用的. 例如 SQL Connection, Connection String, SQL Command Text. 本文將介紹如何從DUMP中找到這些數據.
以下是我們的示例代碼. 這是非常且簡單的步驟. 打開一個連接, 執行一條語句.
public void ExecuteReader() { String connectionString = @"Database=MyDataBaseMASTER;Server=MyTestServer;UID=myUID20130531;Min Pool Size=5;Max Pool Size=100;Connect Timeout=100;Password=Password!01"; using (SqlConnection conn = new SqlConnection(connectionString)) { String commandText = "sp_MySqlProcedure"; conn.Open(); using (SqlCommand sqlCommand = new SqlCommand(commandText,conn)) { sqlCommand.CommandType = CommandType.StoredProcedure; using (IDataReader reader = sqlCommand.ExecuteReader()) { while (reader.Read()) { // do something } } } } }
場景: 有一個頁面調用到了這個ExecuteReader Method. 但是頁面一直白屏, 沒有任何的反應. 在問題發生的時候, 抓了一個DUMP.現在我們並不清楚發生問題的SQL語句是什么, 也不知道這個Web應用的連接字符串是什么. Connection Pool是不是已經滿了. 我們期望從DUMP里面看出一些線索.
步驟
1. 首先我們需要加載SOS.DLL. 實例應用為64-bit, .net framework 2.0, SOS.dll 的路徑為C:\Windows\Microsoft.NET\Framework64\v2.0.50727\SOS.dll
0:087> .load C:\Windows\Microsoft.NET\Framework64\v2.0.50727\sos.dll
2. 找到發生問題的線程, 執行!clrstack可以檢查CLR 的CALLSTACK. 這個線程當前已經將請求發送到了數據庫, 正在做數據讀取的動作. 是什么原因導致這個線程一直處於讀取狀態?
我們下一步希望從這個DUMP中知道所執行的SQL 語句以及SQL Connection方面的信息。
0:087> !clrstack OS Thread Id: 0x2a3c (87) Child-SP RetAddr Call Site 0000000020b3d1e0 000007fee09e0b5b SNINativeMethodWrapper.SNIReadSync(System.Runtime.InteropServices.SafeHandle, IntPtr ByRef, Int32) 0000000020b3d2e0 000007fee09e09fa System.Data.SqlClient.TdsParserStateObject.ReadSni(System.Data.Common.DbAsyncResult, System.Data.SqlClient.TdsParserStateObject) 0000000020b3d380 000007fee09e2efe System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket() 0000000020b3d3e0 000007fee09e2ecc System.Data.SqlClient.TdsParserStateObject.ReadBuffer() 0000000020b3d410 000007fee09e2924 System.Data.SqlClient.TdsParserStateObject.ReadByte() 0000000020b3d440 000007fee09d7b4a System.Data.SqlClient.TdsParser.Run(System.Data.SqlClient.RunBehavior, System.Data.SqlClient.SqlCommand, System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.BulkCopySimpleResultSet, System.Data.SqlClient.TdsParserStateObject) 0000000020b3d510 000007fee09d7410 System.Data.SqlClient.SqlDataReader.ConsumeMetaData() 0000000020b3d560 000007fee09d43c9 System.Data.SqlClient.SqlDataReader.get_MetaData() 0000000020b3d5e0 000007fee09d42ad System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.RunBehavior, System.String) 0000000020b3d660 000007fee09d3d48 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, Boolean) 0000000020b3d740 000007fee09d3b8c System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String, System.Data.Common.DbAsyncResult) 0000000020b3d7f0 000007fee09d39c3 System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String) 0000000020b3d830 000007fee09d37b3 System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior, System.String) 0000000020b3d8e0 000007ff004cedf1 System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(System.Data.CommandBehavior) 0000000020b3e0a0 000007fef60432b3 ASP.mytestpage_aspx.btnExecute_Click(System.Object, System.EventArgs) 0000000020b3e130 000007fef6042ecc System.Web.UI.WebControls.Button.OnClick(System.EventArgs) 0000000020b3e170 000007fef57e207d System.Web.UI.WebControls.Button.RaisePostBackEvent(System.String) 0000000020b3e1c0 000007fef57fb475 System.Web.UI.Page.RaisePostBackEvent(System.Web.UI.IPostBackEventHandler, System.String) 0000000020b3e1f0 000007fef57fa750 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean) 0000000020b3e2c0 000007fef57fa67b System.Web.UI.Page.ProcessRequest(Boolean, Boolean) 0000000020b3e320 000007fef57fa610 System.Web.UI.Page.ProcessRequest() 0000000020b3e380 000007ff023fb639 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext) 0000000020b3e3e0 000007fef5801ab7 ASP.mytestpage_aspx.ProcessRequest(System.Web.HttpContext)
3. 由於這個線程與SQL COMMAND的執行有關. 執行!dso命令, 從heap中找到SqlCommand對象. 並且對這個對象進行檢查.
0:087> !dso OS Thread Id: 0x2a3c (87) RSP/REG Object Name 0000000020b3d178 00000001cfc81600 System.Data.SqlClient.SqlCommand 0:087> !do 00000001cfc81600 Name: System.Data.SqlClient.SqlCommand MethodTable: 000007fee0a2d180 EEClass: 000007fee0896478 Size: 224(0xe0) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name 000007fef72573f8 400018a 8 System.Object 0 instance 0000000000000000 __identity 000007fef66b9ba8 40008e0 10 ...ponentModel.ISite 0 instance 0000000000000000 site 000007fef66e94a0 40008e1 18 ....EventHandlerList 0 instance 0000000000000000 events 000007fef72573f8 40008df 208 System.Object 0 shared static EventDisposed >> Domain:Value 0000000002384230:NotInit 000000000245bf00:000000019f8ae5c0 << 000007fef725ed78 40016f3 b0 System.Int32 1 instance 9892277 ObjectID 000007fef7257b08 40016f4 20 System.String 0 instance 00000001200c00c0 _commandText 000007fee0a2e7f8 40016f5 b4 System.Int32 1 instance 4 _commandType 0:087> !do 00000001200c00c0 Name: System.String MethodTable: 000007fef7257b08 EEClass: 000007fef6e5e550 Size: 56(0x38) bytes (C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) String: sp_MySqlProcedure
從SqlCommand對象, 我們可以看到_commandText里面的值是”sp_MySqlProcedure”. 這正是我們示例代碼里面所付的值” String commandText = "sp_MySqlProcedure";”. 那么 我們怎么確定Command的類型? 我們在代碼中定義的是” sqlCommand.CommandType = CommandType.StoredProcedure;”.
使用ILSpy反編譯SqlCommand的代碼. 可以確認CommandType會被賦值給_commandType. 這是一個enum. StoreProcedure的值為4.
4. 要找到ConnectionString. 首先需要需要先找到SqlConnection對象. 回到剛才的SqlCommand對象. 里面有個field “_activeConnection”. 這是當前的Connection.
0:087> !do 00000001cfc81600 Name: System.Data.SqlClient.SqlCommand MethodTable: 000007fee0a2d180 EEClass: 000007fee0896478 Size: 224(0xe0) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name ….. 000007fee0a2cad0 40016fe 38 ...ent.SqlConnection 0 instance 00000001cfc818e0 _activeConnection 0:087> !do 00000001cfc818e0 Name: System.Data.SqlClient.SqlConnection MethodTable: 000007fee0a2cad0 EEClass: 000007fee08963c0 Size: 104(0x68) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name 000007fef72573f8 400018a 8 System.Object 0 instance 0000000000000000 __identity 000007fef66b9ba8 40008e0 10 ...ponentModel.ISite 0 instance 0000000000000000 site 000007fef66e94a0 40008e1 18 ....EventHandlerList 0 instance 0000000000000000 events 000007fef72573f8 40008df 208 System.Object 0 shared static EventDisposed >> Domain:Value 0000000002384230:NotInit 000000000245bf00:000000019f8ae5c0 << 000007fee0f42ca0 4000be5 20 ...hangeEventHandler 0 instance 0000000000000000 _stateChangeEventHandler 000007fee0f543a0 400172c 28 ...t.SqlDebugContext 0 instance 0000000000000000 _sdc 000007fef7256cd8 400172d 58 System.Boolean 1 instance 0 _AsycCommandInProgress 000007fee0a33528 400172e 30 ...ent.SqlStatistics 0 instance 0000000000000000 _statistics 000007fef7256cd8 400172f 59 System.Boolean 1 instance 0 _collectstats 000007fef7256cd8 4001730 5a System.Boolean 1 instance 0 _fireInfoMessageEventOnUserErrors 000007fee0a305c0 4001733 38 ...ConnectionOptions 0 instance 000000019f8cae50 _userConnectionOptions 000007fee0a2fcd0 4001734 40 ...nnectionPoolGroup 0 instance 000000019f8cb720 _poolGroup
通過ILSpy來檢查SQLCommand的代碼. SQLConnection對象是復制給它的property : Connection. 在這個Property的get中, 該值賦給了_activeConnection. 這是我們從_activeConnection中找SQLConnection的原因.
5. SQL Connection的對象是從SQL Connection Pool里面取到. 連接池使新連接必須打開的次數得以減少. Pooler 保持物理連接的所有權. 通過為每個給定的連接配置保留一組活動連接來管理連接. 每當用戶在連接上調用Open時, 池進程就會查找池中可用的連接。 如果某個池連接可用,會將該連接返回給調用者,而不是打開新連接。 應用程序在該連接上調用 Close 時, 池進程會將連接返回到活動連接池集中, 而不是關閉連接. 連接返回到池中之后, 即可在下一個 Open 調用中重復使用.
只有配置相同的連接可以建立池連接. ADO.NET 同時保留多個池, 每種配置各一個. 在使用集成的安全性時, 連接按照連接字符串以及 Windows 標識分到多個池中.
請參考 : http://msdn.microsoft.com/zh-cn/library/8xx3tyca.aspx
6. 從SQL Connection中找到連接字符串, 必須先找到ConnectionPoolOption對象. 從代碼中能看到來自於_userConnectionOptions. ConnectionString保存在_usersConnectionString中.
0:087> !do 00000001cfc818e0 Name: System.Data.SqlClient.SqlConnection MethodTable: 000007fee0a2cad0 EEClass: 000007fee08963c0 Size: 104(0x68) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name 000007fef72573f8 400018a 8 System.Object 0 instance 0000000000000000 __identity 000007fef66b9ba8 40008e0 10 ...ponentModel.ISite 0 instance 0000000000000000 site 000007fef66e94a0 40008e1 18 ....EventHandlerList 0 instance 0000000000000000 events 000007fef72573f8 40008df 208 System.Object 0 shared static EventDisposed >> Domain:Value 0000000002384230:NotInit 000000000245bf00:000000019f8ae5c0 << 000007fee0f42ca0 4000be5 20 ...hangeEventHandler 0 instance 0000000000000000 _stateChangeEventHandler 000007fee0f543a0 400172c 28 ...t.SqlDebugContext 0 instance 0000000000000000 _sdc 000007fef7256cd8 400172d 58 System.Boolean 1 instance 0 _AsycCommandInProgress 000007fee0a33528 400172e 30 ...ent.SqlStatistics 0 instance 0000000000000000 _statistics 000007fef7256cd8 400172f 59 System.Boolean 1 instance 0 _collectstats 000007fef7256cd8 4001730 5a System.Boolean 1 instance 0 _fireInfoMessageEventOnUserErrors 000007fee0a305c0 4001733 38 ...ConnectionOptions 0 instance 000000019f8cae50 _userConnectionOptions 0:087> !do 000000019f8cae50 Name: System.Data.SqlClient.SqlConnectionString MethodTable: 000007fee0a304c0 EEClass: 000007fee08babc0 Size: 184(0xb8) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name 000007fef7257b08 4000bef 8 System.String 0 instance 000000019f840ba8 _usersConnectionString 0:087> !do 000000019f840ba8 Name: System.String MethodTable: 000007fef7257b08 EEClass: 000007fef6e5e550 Size: 308(0x134) bytes (C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) String: Database=MyDataBaseMASTER;Server=MyTestServer;UID=myUID20130531;Min Pool Size=5;Max Pool Size=100;Connect Timeout=100;Password=Password!01
從ILSpy中檢查SQLConnection.ConnectionString這個屬性. 通過這個屬性, 我們能在應用中取得ConnectionString. 而ConnectionString實際是來源與_userConnectionOptions(ConnectionPoolOption類型) 保存在中的_usersConnectionString (String 類型)
7. “既然Connection是從ConnectionPool里分配出來, 我們是否能從DUMP中看到當前已經分配的Connection數目?” 有些情況下, 程序員沒有在使用完Connection之后, 顯示的關閉Connection. 這些Connection或一直active直到TimeOut才會回到ConnectionPool中等待重用. 所以, 有些場合中, 我們也需要從DUMP中檢查ConnectionPool已經分配了多少Connection.
首先需要從SqlConnection對象中找到_innerConeection. 再找到 _connectionPool. _connectionPool中有一個_totalObjects, 記錄了分配了多少Connection. 如果這個值跟MaxConnection相等, ConnectionPool就不會再分配新的Connection.
0:087> !do 00000001cfc818e0 Name: System.Data.SqlClient.SqlConnection MethodTable: 000007fee0a2cad0 EEClass: 000007fee08963c0 Size: 104(0x68) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name 000007fef72573f8 400018a 8 System.Object 0 instance 0000000000000000 __identity 000007fef66b9ba8 40008e0 10 ...ponentModel.ISite 0 instance 0000000000000000 site _userConnectionOptions 000007fee0a2fcd0 4001734 40 ...nnectionPoolGroup 0 instance 000000019f8cb720 _poolGroup 000007fee0a30808 4001735 48 ...onnectionInternal 0 instance 00000001ab87b918 _innerConnection 0:087> !do 00000001ab87b918 Name: System.Data.SqlClient.SqlInternalConnectionTds MethodTable: 000007fee0a33e18 EEClass: 000007fee08bc040 Size: 248(0xf8) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name 000007fef725ed78 4000f8a 38 System.Int32 1 instance 283 _objectID 000007fef7256cd8 4000f8d 44 System.Boolean 1 instance 0 _allowSetConnectionString 000007fef7256cd8 4000f8e 45 System.Boolean 1 instance 1 _hidePassword 000007fee0a38500 4000f8f 3c System.Int32 1 instance 1 _state 000007fef724f020 4000f90 8 System.WeakReference 0 instance 00000001ab87ba10 _owningObject 000007fee0a30808 4000f91 10 ...onnectionInternal 0 instance 0000000000000000 _nextPooledObject 000007fee0a2ff20 4000f92 18 ....DbConnectionPool 0 instance 000000019f8cc558 _connectionPool 0:087> !do 000000019f8cc558 Name: System.Data.ProviderBase.DbConnectionPool MethodTable: 000007fee0a2ff20 EEClass: 000007fee0897080 Size: 176(0xb0) bytes (C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll) Fields: MT Field Offset Type VT Attr Value Name 000007fef725ed78 400153c 88 System.Int32 1 instance 190000 _cleanupWait 000007fee0a337c8 400153d 8 ...ctionPoolIdentity 0 instance 000000019f8cc4d8 _identity 000007fee0a2f9c8 400153e 10 ...ConnectionFactory 0 instance 000000019f8ba208 _connectionFactory 000007fee0a2fcd0 400153f 18 ...nnectionPoolGroup 0 instance 000000019f8cb720 _connectionPoolGroup 000007fee0a30a58 4001540 20 ...nPoolGroupOptions 0 instance 000000019f8cb6f8 _connectionPoolGroupOptions 000007fee0f52588 4001541 28 ...nPoolProviderInfo 0 instance 0000000000000000 _connectionPoolProviderInfo 000007fee0f1ed90 4001542 8c System.Int32 1 instance 1 _state 000007fee0a338a8 4001543 30 ...InternalListStack 0 instance 000000019f8cc7c8 _stackOld 000007fee0a338a8 4001544 38 ...InternalListStack 0 instance 000000019f8cc7e0 _stackNew 000007fef7246130 4001545 40 ...ding.WaitCallback 0 instance 000000019f8ccf48 _poolCreateRequest 000007fef7247070 4001546 48 ...Collections.Queue 0 instance 0000000000000000 _deactivateQueue 000007fef7246130 4001547 50 ...ding.WaitCallback 0 instance 0000000000000000 _deactivateCallback 000007fef725ed78 4001548 90 System.Int32 1 instance 0 _waitCount 000007fee0a33928 4001549 58 ...l+PoolWaitHandles 0 instance 000000019f8cc8e8 _waitHandles 000007fef7257dd0 400154a 60 System.Exception 0 instance 0000000000000000 _resError 000007fef7256cd8 400154b a0 System.Boolean 1 instance 0 _errorOccurred 000007fef725ed78 400154c 94 System.Int32 1 instance 5000 _errorWait 000007fef7289e60 400154d 68 ...m.Threading.Timer 0 instance 0000000000000000 _errorTimer 000007fef7289e60 400154e 70 ...m.Threading.Timer 0 instance 000000019f8cd110 _cleanupTimer 000007fee0a33c58 400154f 78 ...tedConnectionPool 0 instance 000000019f8ccca8 _transactedConnectionPool 0000000000000000 4001550 80 0 instance 000000019f8cc940 _objectList 000007fef725ed78 4001551 98 System.Int32 1 instance 12 _totalObjects
總結
1. SQL語句 以及 它的類型可以分別從System.Data.SqlClient.SqlCommand對象的_commandText 字段和 _commandType字段中找到.
2. SQLConnection是從ConnectionPool中分配出來.
3. 連接字符串, 以及相應的數據可以從ConnectionPoolOption對象中找到. 它位於SQLConnection對象的_userConnectionOptions字段下.
4. ConnectionPool已經分配的Connection數目, 可以從System.Data.ProviderBase.DbConnectionPool對象的_totalObjects字段中找到. 這個對象位於SQLConnection對象的_innerConnection下面.
希望以上內容對您有所幫助
Richard Chen