대용량 데이터를 사용하는 SQLCommand 비동기 방식을 사용하면 성능이 저하됩니다.
비동기 호출을 사용할 때 SQL 퍼포먼스에 큰 문제가 있습니다.문제를 설명하기 위해 작은 케이스를 만들었습니다.
LAN(localDB가 아닌)에 상주하는 SQL Server 2016에 데이터베이스를 작성했습니다.
에는 표가 .WorkingCopy: 2열:
Id (nvarchar(255, PK))
Value (nvarchar(max))
DDL
CREATE TABLE [dbo].[Workingcopy]
(
[Id] [nvarchar](255) NOT NULL,
[Value] [nvarchar](max) NULL,
CONSTRAINT [PK_Workingcopy]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
표에는한 의 레코드가 .id ='PerfUnitTest',Valuemb 1.5 MB 、 JSON 、 zip ) 。
SSMS에서 쿼리를 실행하면 다음과 같이 됩니다.
SELECT [Value]
FROM [Workingcopy]
WHERE id = 'perfunittest'
즉시 결과를 얻을 수 있으며 SQL Servre Profiler에서 실행 시간이 약 20밀리초임을 알 수 있습니다.모두 정상입니다.
에서 쿼리를 실행하는 경우. NET(4.6을 .)SqlConnection:
// at this point, the connection is already open
var command = new SqlCommand($"SELECT Value FROM WorkingCopy WHERE Id = @Id", _connection);
command.Parameters.Add("@Id", SqlDbType.NVarChar, 255).Value = key;
string value = command.ExecuteScalar() as string;
이 실행 시간도 약 20~30밀리초입니다
단, 비동기 코드로 변경하는 경우:
string value = await command.ExecuteScalarAsync() as string;
실행 시간이 갑자기 1800ms가 됩니다.또한 SQL Server Profiler에서는 쿼리 실행 시간이 1초 이상임을 알 수 있습니다.프로파일러에 의해 보고된 실행 쿼리는 비 Async 버전과 완전히 동일합니다.
하지만 점점 더 나빠진다.접속 문자열의 Packet Size를 사용하여 재생하면 다음과 같은 결과가 나타납니다.
패킷 크기 32768 : [TIMING] :SQL Value Store의 Execute Scalar Async -> 경과시간 : 450 ms
패킷 크기 4096 : [TIMING] :SQL Value Store의 Execute Scalar Async -> 경과시간 : 3667 ms
패킷 크기 512 : [TIMING] :SQL Value Store의 Execute Scalar Async -> 경과시간 : 30776 ms
30,000밀리초!!비동기 버전보다 1000배 이상 느립니다.또한 SQL Server Profiler는 쿼리 실행에 10초 이상 소요되었다고 보고합니다.20초가 어디로 갔는지 설명도 안 돼!
그런 다음 동기화 버전으로 전환하고 패킷 크기를 가지고 놀았습니다. 실행 시간에 약간의 영향을 주긴 했지만 비동기 버전만큼 극적인 것은 없었습니다.
sidenote로서 작은 문자열(100바이트 미만)만 값에 넣으면 비동기 쿼리의 실행 속도는 동기 버전과 동일합니다(1 또는2 ms).
특히 빌트인을 사용하고 있기 때문에SqlConnectionORM도 아닙니다.또, 주위를 뒤져 보면, 이 행동을 설명할 수 있는 것은 아무것도 발견되지 않았습니다.좋은 생각 있어요?
큰 부하가 없는 시스템에서는 비동기 콜의 오버헤드가 약간 커집니다.I/O 작업 자체는 어떤 경우에도 비동기이지만 블록은 스레드 풀 태스크 전환보다 빠를 수 있습니다.
오버헤드가 얼마나 됩니까?타이밍 번호를 봅시다.블로킹 콜의 경우 30밀리초, 비동기 콜의 경우 450밀리초32kiB 패킷사이즈는 50여개의 개별 I/O 조작이 필요함을 의미합니다.즉, 각 패킷의 오버헤드는 약 8밀리초이며, 이는 다양한 패킷크기에 대한 측정치와 매우 일치합니다.비동기 버전에서는 동기 버전보다 훨씬 많은 작업을 수행해야 하지만 비동기 버전에서는 오버헤드가 발생하지 않습니다.동기 버전은 (간소화)1 요구 -> 50 응답인 것처럼 들립니다만, 비동기 버전은 결국 1 요구 ->1 응답 ->1 요구 ->1 응답 -> ...이 되어, 몇 번이고 비용을 지불하게 됩니다.
더 깊이. ExecuteReader와 마찬가지로 동작합니다.ExecuteReaderAsync다음 조작은Read그 뒤에 a가 붙는다.GetFieldValue- 거기서 흥미로운 일이 벌어집니다.둘 중 하나가 비동기일 경우 동작 전체가 느려집니다.따라서 실제로 비동기화 작업을 시작하면 분명히 매우 다른 일이 발생합니다.Read고속으로, 그 후 비동기적으로GetFieldValueAsync느릴 수도 있고 느릴 수도 있고ReadAsync, 그리고 둘 다GetFieldValue그리고.GetFieldValueAsync빨라요.스트림으로부터의 첫 번째 비동기 판독은 느리고 저속도는 행 전체의 크기에 전적으로 의존합니다.같은 크기의 행을 추가할 경우 각 행을 읽는 데 걸리는 시간은 마치 하나의 행만 있는 것처럼 같기 때문에 데이터가 여전히 한 행씩 스트리밍되고 있는 것이 분명합니다. 비동기 읽기를 시작하면 한 번에 전체 행을 읽는 것이 더 좋은 것 같습니다.첫 번째 행을 비동기적으로 읽고 두 번째 행을 동기적으로 읽으면 두 번째 행을 읽는 속도가 다시 빨라집니다.
따라서 문제가 개별 행 및/또는 열의 큰 크기임을 알 수 있습니다.총 데이터량은 중요하지 않습니다. 작은 행 100만 개를 비동기적으로 읽는 것은 동기화된 것만큼 빠릅니다.단, 하나의 패킷에 들어갈 수 없을 정도로 큰 필드를 1개만 추가하면 해당 데이터를 비동기적으로 읽어내는 비용이 발생합니다.예를 들어 각 패킷에 개별 요청 패킷이 필요하고 서버가 모든 데이터를 한 번에 전송할 수는 없는 것처럼 보입니다.사용.CommandBehavior.SequentialAccess는 기대대로 퍼포먼스를 향상시키지만 동기화와 비동기화 사이에는 여전히 큰 차이가 존재합니다.
제가 받은 최고의 성과는 모든 것을 제대로 했을 때입니다., ①을 사용한다는 입니다.CommandBehavior.SequentialAccess데이터를 명시적으로 스트리밍할 수 있습니다.
using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
{
while (await reader.ReadAsync())
{
var data = await reader.GetTextReader(0).ReadToEndAsync();
}
}
이것에 의해, 동기와 비동기간의 차이를 측정하기 어려워져 패킷사이즈를 변경해도, 종래와 같이 터무니없는 오버헤드가 발생하지 않게 됩니다.
케이스에서 로 하는 사용 가능한 의 툴을 해 주세요. 큰 .도움말과 같은 .이 경우 다음과 같은 도우미에 의존하지 말고 큰 컬럼 데이터를 스트리밍하십시오.ExecuteScalar ★★★★★★★★★★★★★★★★★」GetFieldValue.
언급URL : https://stackoverflow.com/questions/42415969/horrible-performance-using-sqlcommand-async-methods-with-large-data
'programing' 카테고리의 다른 글
| XAML에서 명령 매개 변수로 열거 값 전달 (0) | 2023.04.13 |
|---|---|
| 서브폴더/서브디렉토리를 무시하려면 어떻게 해야 합니까? (0) | 2023.04.13 |
| WPF 라벨의 줄바꿈? (0) | 2023.04.13 |
| UIScroll View 스크롤 가능 콘텐츠 크기 모호성 (0) | 2023.04.13 |
| WPF DataGrid에 버튼 추가 (0) | 2023.04.13 |