동일한 LOB 데이터에 액세스 할 때 논리적 읽기가 다름


26

다음은 동일한 데이터를 읽지 만 매우 다른 논리적 읽기를보고하는 세 가지 간단한 테스트입니다.

설정

다음 스크립트는 100 개의 동일한 행이있는 테스트 테이블을 작성합니다. 각 테이블에는 행 외부에 저장하기에 충분한 데이터 가있는 xml 열이 포함되어 있습니다. 내 테스트 데이터베이스에서 생성 된 xml 의 길이는 각 행에 대해 20,204 바이트입니다.

-- Conditional drop
IF OBJECT_ID(N'dbo.XMLTest', N'U') IS NOT NULL
    DROP TABLE dbo.XMLTest;
GO
-- Create test table
CREATE TABLE dbo.XMLTest
(
    ID integer IDENTITY PRIMARY KEY,
    X xml NULL
);
GO
-- Add 100 wide xml rows
DECLARE @X xml;

SET @X =
(
    SELECT TOP (100) *
    FROM  sys.columns AS C
    FOR XML 
        PATH ('row'),
        ROOT ('root'),
        TYPE
);

INSERT dbo.XMLTest
    (X)
SELECT TOP (100)
    @X
FROM  sys.columns AS C;

-- Flush dirty buffers
CHECKPOINT;

테스트

다음 세 가지 테스트는 xml 열을 읽습니다 .

  1. 평범한 SELECT진술
  2. xml 을 변수에 할당
  3. SELECT INTO임시 테이블을 만드는 데 사용
-- No row count messages or graphical plan
-- Show I/O statistics
SET NOCOUNT ON;
SET STATISTICS XML OFF;
SET STATISTICS IO ON;
GO
PRINT CHAR(10) + '=== Plain SELECT ===='

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SELECT XT.X 
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== Assign to a variable ===='

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

DECLARE @X xml;

SELECT
    @X = XT.X
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== SELECT INTO ===='

IF OBJECT_ID(N'tempdb..#T', N'U') IS NOT NULL
    DROP TABLE #T;

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SELECT 
    XT.X
INTO #T
FROM dbo.XMLTest AS XT
GO
SET STATISTICS IO OFF;

결과

출력은 다음과 같습니다.

=== 일반 선택 ====
'XMLTest'테이블. 스캔 카운트 1, 논리적 읽기 3, 물리적 읽기 1, 미리 읽기 0,
    lob 논리적 읽기 (795), lob 물리적 읽기 (37), lob 미리 읽기 (796).

=== 변수에 할당 ====
'XMLTest'테이블. 스캔 카운트 1, 논리적 읽기 3, 물리적 읽기 1, 미리 읽기 0,
    lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기는 0을 읽습니다.

=== 안으로 선택 ====
'XMLTest'테이블. 스캔 카운트 1, 논리적 읽기 3, 물리적 읽기 1, 미리 읽기 0,
    lob 논리적 읽기 (300), lob 물리적 읽기 (37), lob 미리 읽기 (400).

질문

  • LOB 읽기가 다른 이유는 무엇입니까?
  • 각 테스트에서 정확히 동일한 데이터를 읽었습니까?

답변:


27

모든 읽기가 동일한 것은 아닙니다. SQL Server는 LOB 데이터 액세스 비용이 비싸다는 것을 알고 가능하면이를 피하려고합니다. 각 경우에 LOB 데이터를 읽는 방식에 자세한 차이점이 있습니다.

개요

다음과 같은 이유로 숫자가 다릅니다.

  • 선택은 패킷 크기 청크로 LOB를 읽습니다.
  • 변수 할당 테스트 LOB를 전혀 읽지 않습니다
  • "select into"테스트는 LOB를 전체 페이지 에서 읽습니다.

세부 묘사

  1. 평원 SELECT

    플랜 선택

    클러스터형 인덱스 스캔은 LOB 데이터를 읽지 않습니다. 스토리지 엔진 LOB 핸들 만 지정합니다 . 제어는 계획의 루트로 돌아올 때까지 핸들이 사용되지 않습니다.

    현재 행의 LOB 컨텐츠는 TDS 패킷 크기 청크로 읽혀지고 클라이언트로 스트리밍됩니다. 논리적 읽기는 페이지를 터치 한 횟수를 계산하므로 다음과 같습니다.

    보고 된 읽기 수는 LOB 페이지 전환이 발생할 때마다 하나씩 수행 된 청크 된 읽기 수와 같습니다.

    예를 들어, 프로세스는 스트림의 현재 위치에 해당하는 페이지를 터치 할 때 각 청크의 시작 부분에서 논리적 읽기가 계산됩니다. 패킷이 데이터베이스 페이지 (일반적인 경우)보다 작은 경우 동일한 페이지에 대해 여러 논리적 읽기가 계산됩니다. 패킷 크기가 너무 커서 전체 LOB가 한 청크에 들어갈 수있는 경우보고 된 논리적 읽기 수는 LOB 페이지 수입니다.

  2. 변수 할당

    가변 계획

    Clustered Index Scan 은 이전과 같이 LOB 핸들 을 할당합니다 . 계획의 루트에서 LOB 핸들이 변수에 복사됩니다. 변수를 읽지 않기 때문에 LOB 데이터 자체에 액세스하지 않습니다 (0 번 LOB 읽기). 그것이 되었더라도, 마지막으로 지정된 LOB 핸들을 통해서만 이루어집니다.

    LOB 데이터에 액세스하지 않으므로 LOB 읽기가 없습니다.

  3. SELECT INTO

    계획으로 선택

    이 계획은 벌크 행 세트 제공자를 사용하여 소스 테이블에서 새 테이블로 LOB 데이터를 복사합니다. 각 읽기에서 완전한 LOB 페이지를 처리합니다 (스트리밍 또는 청킹 없음).

    논리적 읽기 수는 테스트 테이블의 LOB 페이지 수에 해당합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.