안녕하세요,
쿼리 실행 관련해 궁금한 것이 있어 질문드립니다.
동일한 쿼리를 응용프로그램과 SQL Management 에서 실행하였는데 응용 프로그램에서는 Timeout Error 가 발생합니다.
무엇이 문제인지 궁금합니다.
자세한 상황은 아래와 같습니다.
현재 응용프로그램에서 Select 쿼리를 사용합니다.
응용 프로그램에서 Select 쿼리를 SqlDataAdapter 클래스의 객체를 생성할 때 사용해 데이터를 받아와 데이터 테이블에 담습니다.
코드는 아래와 같습니다.
string query = "Select A from B";
DataTable table = new DataTable();
using (SqlDataAdapter adapter = new SqlDataAdapter(this.query))
{
adapter.Fill(table);
}
그런데 Fill 을 수행하는 과정에서 Timeout Error 가 간헐적으로(꽤 자주) 발생합니다.
쿼리의 문제이거나 DB 성능의 문제인가 싶어서 SQL Management 에서 직접 동일한 쿼리를 실행해보았는데
이 때는 큰 시간의 부하 없이 정상적으로 작동하였습니다.
동일한 쿼리의 실행인데 응용프로그램에서만 문제가 발생하는 것은 응용프로그램에 문제가 있는걸까요?
추가로 에러 로그에 남은 Fill 이후의 진행을 붙입니다.
------------------------------------------------------------------------------------------------------------------------------------------------
System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32 maxRecords, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
-----------------------------------------------------------------------------------------------------------------------
SqlDataAdapter 클래스에 대해 잘 아시거나 추측되는 문제가 있어보이시면 답글 부탁드립니다.
Comment 2
-
이리
2020.08.20 13:59
-
약은 중
2020.08.24 19:44
위에 이리님께서 말씀하신 부분이 맞을 겁니다.
첨언을 해보면,
동일한 쿼리를 SSMS에서 실행했을때와 WEB에서 호출했을때가 달라지는 경우는 발생할 가능성이 어느정도 있으며,
이는 하나의 쿼리가 서로 다른 실행계획을 만들기 때문입니다.(2개 이상의 실행계획이 만들어지는 경우는 많이 발생합니다.)
이리님께서 말씀하신 arithbort 등등..여러가지 setting값의 차이에 의해서 발생한다고 전문가들이 말하고 있습니다.
제 경험상으로,이런 경우가 발생할때는,
쿼리자체가 복잡하거나,동적으로 쿼리가 조립되거나 일때 였었습니다.
해결했던 방법은(이 또한 이리님께서 말씀을 해 주셨습니다.)
사실 개발자는 어느테이블을 시작으로 해서,다음 어느테이블을 (어떤조인방식으로)조인을 걸로,
어떤 인덱스를 탈것을 어느정도 예상가능하고,그렇게 예상한 방식으로 쿼리를 만들게 됩니다.
특별히 hint를 주지 않더라도 대부분,옵티마이저가 알아서 잘 해주는데,
안될 경우에는 강제해 줘야 할 수 있습니다
INNER LOOP JOIN과 INDEX= 정도만 이용해서도 상당히 좋아집니다.(오라클만큼 복잡하지 않습니다.)
SSMS에서는 빨랐다고 하셨으므로,
1.SSMS에서 실행했을때의 실행계획을 확인하셔서, 테이블간 조인 순서,조인방식,사용하는 인덱스등을 확인하셔서
2.그에 맞춰서 쿼리를 수정해서 WEB이든 응용프로그램에서 시도를 해보시면 좋은 결과가 나오리라 봅니다.
응용프로그램과 SSMS의 세션 설정 값에 따라서 실행계획이 다르게 생성 될 수도 있습니다.
(SET ARITHBORT 등등..)
실제 쿼리 시간이 오래 걸리는지는 실시간 모니터링 혹은 성능 수집을 통해서 확인해 보셔야 할것 같습니다.