LOB_DATA, 느린 테이블 스캔 및 일부 I / O 질문


19

열 중 하나가 XML 데이터이고 평균 XML 항목 크기가 ~ 15 킬로바이트 인 다소 큰 테이블이 있습니다. 다른 모든 열은 일반 int, bigint, GUID 등입니다. 구체적인 숫자를 나타 내기 위해 테이블에 백만 개의 행이 있고 크기가 ~ 15GB라고 가정합니다.

내가 주목 한 것은 모든 열을 선택하려면이 테이블에서 데이터를 선택하는 것이 실제로 느리다는 것입니다. 내가 할 때

SELECT TOP 1000 * FROM TABLE

결과에서 순서를 지정하지 않아도 디스크에서 데이터를 읽는 데 약 20-25 초가 걸립니다. 콜드 캐시로 쿼리를 실행합니다 (예 : after DBCC DROPCLEANBUFFERS). IO 통계 결과는 다음과 같습니다.

스캔 카운트 1, 논리적 읽기 (364), 물리적 읽기 (24), 미리 읽기 (7919), lob 논리적 읽기 (7924), lob 물리적 읽기 (1690), lob 미리 읽기 (3968).

~ 15MB의 데이터를 가져옵니다. 실행 계획에 예상대로 클러스터형 인덱스 스캔이 표시됩니다.

내 쿼리 외에 디스크에서 IO가 발생하지 않습니다. 또한 클러스터형 인덱스 조각화가 0 %에 가깝다는 것을 확인했습니다. 이것은 소비자 용 SATA 드라이브이지만 SQL Server가 ~ 100-150MB / 분보다 빠르게 테이블을 스캔 할 수 있다고 생각합니다.

XML 필드가 있으면 대부분의 테이블 데이터가 LOB_DATA 페이지에 위치합니다 (실제로 테이블 페이지의 ~ 90 %가 LOB_DATA 임).

내 질문은-LOB_DATA 페이지는 크기 때문에뿐만 아니라 테이블에 많은 LOB_DATA 페이지가있을 때 SQL Server가 클러스터형 인덱스를 효과적으로 스캔 할 수 없기 때문에 느린 스캔을 일으킬 수 있다고 생각하는 것이 맞습니까?

더 광범위하게-그러한 테이블 구조 / 데이터 패턴을 갖는 것이 합리적인 것으로 간주됩니까? Filestream 사용에 대한 권장 사항은 일반적으로 훨씬 더 큰 필드 크기를 나타내므로 실제로 해당 경로를 가고 싶지 않습니다. 이 특정 시나리오에 대한 좋은 정보를 찾지 못했습니다.

XML 압축에 대해 생각하고 있었지만 클라이언트 또는 SQLCLR을 사용하여 수행해야하며 시스템에서 구현하려면 상당한 작업이 필요합니다.

압축을 시도했는데 XML이 중복성이 높기 때문에 (ac # 앱에서) XML을 20KB에서 ~ 2.5KB로 압축하여 VARBINARY 열에 저장하여 LOB 데이터 페이지의 사용을 막을 수 있습니다. 내 테스트에서 SELECT의 속도가 20 배 빨라집니다.


Alex : 내 답변과 관련된 토론을 보았는지 확실하지 않지만 (링크가 내 답변 아래에 주석으로 표시되어 있음) 시나리오를 재현 할 수있었습니다. 설명과 일치하는 테이블을 채우고 매우 유사한 I / O 통계를 얻었습니다. "LOB 실제 읽기"는 결코 가깝지 않았습니다. 따라서 XML을 업데이트했는지 (다른 열은 아님) 데이터 파일이 실제로 조각화되었는지 궁금합니다. 여전히 테이블의 DDL과 각 데이터 파일에 대한 자동 증가 설정을 얻는 것이 마음에 들지 않으며 데이터 파일을 축소합니까?
Solomon Rutzky

우선, 자세한 답변을 주셔서 감사합니다. 시간이 부족하여 당시 토론에 참여할 수 없었습니다. 이제 당신은 이것을 언급했습니다 (질문을 물을 때 생각하지 않았습니다)-XML 필드는 생성 된 후 여러 번 업데이트되며 작게 생성됩니다. 따라서 처음에는 행에 저장되어 있고 일부 업데이트 후 LOB 페이지 구조로 이동 한 다음 더 많은 업데이트를 얻는 것으로 의심됩니다.
Alexander Shelemin

(계속) 질문을하기 전에 파일의 물리적 조각화를 확인했으며 기본 제공 Windows 도구가 문제가 없다고 생각했기 때문에 더 이상 조사하지 않았습니다. 자동 증가는 기본적으로 1MB 정도이며 데이터 파일은 축소되지 않았습니다.
Alexander Shelemin

내 특별한 경우에 상위 1000 *를 선택하는 것이 중요합니다. 나는 그것이 나쁜 습관으로 간주된다는 것을 확실히 알고 있지만, 일부 응용 프로그램 디자인 결정은 오랫동안 적용된 후에 실제로 변경하기가 어렵습니다. Select *는 기본적으로 앱의 다른 구성 요소 간 데이터베이스 간 복제 전략으로 사용됩니다. 예를 들어 데이터 / 스키마를 사용하여 많은 임의의 조작을 수행 할 수있는 장점이 있습니다.이 기능은 내장 된 복제 기술로는 어려울 수 있지만 문제가 있습니다.
Alexander Shelemin

Alex SELECT *는 XML 데이터가 필요한 경우 문제가되지 않습니다. XML 데이터를 원하지 않는 경우에만 문제가됩니다.이 경우 쿼리를 느리게하여 사용하지 않는 데이터를 다시 가져 오는 이유는 무엇입니까? LOB 페이지의 조각화가 정확하게보고되지 않았는지 궁금한 XML 업데이트에 대해 질문했습니다. 클러스터링 된 인덱스가 조각화되지 않았다는 것을 어떻게 정확히 결정 했습니까? 실행 한 명령을 제공 할 수 있습니까? 그리고 클러스터형 인덱스에서 전체 리빌드를 수행 했습니까? (계속)
Solomon Rutzky

답변:


11

XML 필드가 있으면 대부분의 테이블 데이터가 LOB_DATA 페이지에 위치합니다 (실제로 테이블 페이지의 ~ 90 %가 LOB_DATA 임).

테이블에 XML 열만 있으면 효과가 없습니다. 그것은 XML의 존재이다 데이터 , 특정 조건 하에서 , 행의 데이터의 일부가 LOB_DATA 페이지에 행을 저장됩니다. 그리고 하나 (또는 ​​아마도 여러 개의 ;-)는 duh라고 주장 할 수 있지만 XML열은 실제로 XML 데이터가 있음을 암시하지만 XML 데이터가 행 외부에 저장되어 있어야한다고 보장하지는 않습니다. XML 데이터 이외의 작은 문서 (최대 8000 바이트)는 한 줄에 들어가고 LOB_DATA 페이지로 이동하지 않을 수 있습니다.

LOB_DATA 페이지는 크기 때문에뿐만 아니라 테이블에 많은 LOB_DATA 페이지가있을 때 SQL Server가 클러스터형 인덱스를 효과적으로 스캔 할 수 없기 때문에 느린 스캔을 일으킬 수 있다고 생각하고 있습니까?

스캐닝은 모든 행을 보는 것을 말합니다. 물론 데이터 페이지를 읽을 때 열의 서브 세트를 선택한 경우에도 모든 행 내 데이터를 읽습니다. LOB 데이터와의 차이점은 해당 열을 선택하지 않으면 행 외부 데이터를 읽을 수 없다는 것입니다. 따라서 SQL Server가이 클러스터형 인덱스를 정확하게 테스트하지 않았거나 정확히 테스트하지 않았기 때문에이 클러스터형 인덱스를 얼마나 효율적으로 스캔 할 수 있는지에 대한 결론을 내리는 것은 불공평합니다. XML 열을 포함하여 모든 열을 선택했으며 앞에서 언급했듯이 대부분의 데이터가있는 열을 선택했습니다.

따라서 우리는 이미 SELECT TOP 1000 *테스트가 일련의 8k 데이터 페이지를 한 줄로 읽는 것이 아니라 각 행마다 다른 위치로 점프하는 것임을 이미 알고 있었습니다 . 해당 LOB 데이터의 정확한 구조는 데이터의 크기에 따라 달라질 수 있습니다. 여기에 표시된 연구 ( Varchar, Varbinary 등의 (MAX) 유형에 대한 LOB 포인터의 크기는 얼마입니까? )에 따라 두 가지 유형의 오프라인 LOB 할당이 있습니다.

  1. 인라인 루트-8001-40,000 (실제 42,000) 바이트 사이의 데이터에 대해 공간이 허용되는 경우 LOB 페이지를 직접 가리키는 1-5 개의 포인터 (24-72 바이트) IN ROW가 있습니다.
  2. TEXT_TREE-42,000 바이트를 초과하는 데이터의 경우 또는 1-5 개의 포인터가 행에 맞지 않을 경우 LOB 페이지에 대한 포인터 목록의 시작 페이지에 24 바이트 포인터 만 있습니다 (예 : " text_tree "페이지).

이 두 가지 상황 중 하나는 8000 바이트를 초과하거나 행에 맞지 않는 LOB 데이터를 검색 할 때마다 발생합니다 . PasteBin.com ( LOB 할당 및 읽기 테스트를위한 T-SQL 스크립트)에 테스트 스크립트를 게시 하여 3 가지 유형의 LOB 할당 (데이터 크기를 기준으로 함)과 각각이 논리적 및 물리적 판독. 귀하의 경우, XML 데이터가 실제로 행당 42,000 바이트 미만인 경우, 그 중 어느 것도 가장 효율적인 TEXT_TREE 구조에 있지 않아야합니다.

SQL Server가 해당 클러스터형 인덱스를 얼마나 빨리 검색 할 수 있는지 테스트하려면 해당 XML 열을 포함 하지 않는SELECT TOP 1000 하나 이상의 열을 지정하십시오 . 결과에 어떤 영향이 있습니까? 꽤 빠릅니다.

그러한 테이블 구조 / 데이터 패턴을 갖는 것이 합리적인 것으로 간주됩니까?

실제 테이블 구조 및 데이터 패턴에 대한 설명이 불완전한 경우 누락 된 세부 사항이 무엇인지에 따라 어떤 답변도 최적이 아닐 수 있습니다. 이를 염두에두고 테이블 구조 또는 데이터 패턴에 대해 분명히 불합리한 것은 없다고 말하고 싶습니다.

(ac # 앱에서) XML을 20KB에서 ~ 2.5KB로 압축하여 VARBINARY 열에 저장하여 LOB 데이터 페이지의 사용을 방지 할 수 있습니다. 내 테스트에서 SELECT의 속도가 20 배 빨라집니다.

모든 열을 선택하거나 심지어 XML 데이터 (현재 VARBINARY) 만 더 빨리 선택했지만 실제로는 "XML"데이터를 선택하지 않는 쿼리가 손상됩니다. 다른 열에 약 50 바이트가 있고 FILLFACTOR100 이 있다고 가정하면 다음을 수행하십시오.

  • 압축 없음 : 15k의 XML데이터에는 2 개의 LOB_DATA 페이지가 필요하며 인라인 루트에 대한 2 개의 포인터가 필요합니다. 첫 번째 포인터는 24 바이트이고 두 번째 포인터는 12이며, XML 데이터의 경우 행에 총 36 바이트가 저장됩니다. 총 행 크기는 86 바이트이며 이러한 행 중 약 93 개를 8060 바이트 데이터 페이지에 맞출 수 있습니다. 따라서 백만 개의 행에는 10,753 개의 데이터 페이지가 필요합니다.

  • 사용자 지정 압축 : 2.5k의 VARBINARY데이터가 줄에 맞습니다. 총 행 크기는 2610 (2.5 * 1024 = 2560) 바이트이며 이러한 행 중 3 개만 8060 바이트 데이터 페이지에 맞출 수 있습니다. 따라서 백만 개의 행에는 333,334 개의 데이터 페이지가 필요합니다.

Ergo, 사용자 지정 압축을 구현 하면 클러스터형 인덱스의 데이터 페이지 가 30 배 증가 합니다. 즉, 클러스터형 인덱스 스캔을 사용하는 모든 쿼리에는 읽을 데이터 페이지 가 약 322,500 개 더 있습니다. 이러한 유형의 압축 수행에 대한 추가 효과는 아래의 세부 섹션을 참조하십시오.

의 성능을 기반으로 리팩토링을 수행하지 않도록주의해야합니다 SELECT TOP 1000 *. 이는 애플리케이션이 발행 할 쿼리 일 가능성이 높으며, 잠재적으로 불필요한 최적화를위한 유일한 기초로 사용되어서는 안됩니다.

보다 자세한 정보와 추가 테스트를 보려면 아래 섹션을 참조하십시오.


이 질문에 대한 명확한 답을 제시 할 수는 없지만, 우리는 적어도 정확한 진전을 찾아내는 데 도움이되도록 최소한의 진전을 이루고 추가 연구를 제안 할 수 있습니다 (이상적으로 근거에 근거 함).

우리가 아는 것 :

  1. 테이블에는 약 백만 개의 행이 있습니다
  2. 테이블 크기는 약 15GB
  3. 표가 하나 포함 XML열 및 유형의 여러 가지 다른 열을 : INT, BIGINT, UNIQUEIDENTIFIER, "등"
  4. XML열 "size"는 평균 약 15k입니다.
  5. 를 실행 한 후 DBCC DROPCLEANBUFFERS다음 쿼리를 완료하는 데 20-25 초가 걸립니다.SELECT TOP 1000 * FROM TABLE
  6. 클러스터형 인덱스를 스캔하는 중
  7. 클러스터형 인덱스의 조각화는 0 %에 가깝습니다.

우리가 생각하는 것 :

  1. 이 쿼리 이외의 다른 디스크 활동은 없습니다. 확실합니까? 다른 사용자 쿼리가 없더라도 백그라운드 작업이 수행됩니까? IO 중 일부를 차지할 수있는 동일한 시스템에서 SQL Server 외부에서 실행중인 프로세스가 있습니까? 없을 수도 있지만 제공된 정보만으로는 명확하지 않습니다.
  2. 15MB의 XML 데이터가 리턴되고 있습니다. 이 숫자는 무엇을 기준으로합니까? 1000 행에 행당 평균 15k의 XML 데이터를 곱한 추정치? 또는 해당 쿼리에 대해 수신 된 프로그램의 집계입니까? 그것이 단지 추정이라면, XML 데이터의 분포가 단순한 평균으로 암시되는 방식이 아닐 수도 있기 때문에 나는 그것에 의존하지 않을 것입니다.
  3. XML 압축이 도움이 될 수 있습니다. .NET에서 압축을 정확히 어떻게 수행 하시겠습니까? 비아 GZipStream 또는 DeflateStream 클래스? 이것은 비용이 들지 않는 옵션이 아닙니다. 확실히 일부 데이터를 큰 비율로 압축하지만 매번 데이터를 압축 / 압축 해제하는 추가 프로세스가 필요하므로 더 많은 CPU가 필요합니다. 이 계획은 또한 다음과 같은 능력을 완전히 제거합니다.

    • 를 통해 XML 데이터를 쿼리합니다 .nodes..value , .query, 및 .modifyXML 기능을 제공합니다.
    • XML 데이터를 색인화하십시오.

      XML데이터 유형이 사전에 요소 및 속성 이름을 저장하고 각 항목에 정수 색인 ID를 할당 한 다음 해당 정수 ID를 사용하여 데이터 유형이 이미 최적화되어 있음 을 명심하십시오 (XML은 "중복 중복" 이므로) 문서 전체에서 (따라서 각 사용마다 전체 이름을 반복하지 않으며 요소의 닫는 태그로 다시 반복하지 않습니다). 실제 데이터에도 불필요한 공백이 제거되었습니다. 이것이 추출 된 XML 문서가 원래 구조를 유지하지 않는 이유와 빈 요소 <element /><element></element>. 따라서 GZip (또는 다른 것)을 통한 압축으로 얻는 이득은 요소 및 / 또는 속성 값을 압축하여 만 찾을 수 있습니다. 바로 위에 언급 된 기능.

      또한 XML 데이터를 압축하고 VARBINARY(MAX)결과를 저장 한다고해서 LOB 액세스가 제거되는 것이 아니라 감소시킬뿐입니다. 행에있는 나머지 데이터의 크기에 따라 압축 된 값이 행에 맞거나 LOB 페이지가 여전히 필요할 수 있습니다.

이 정보는 도움이 되더라도 충분하지 않습니다. 쿼리 성능에 영향을 미치는 많은 요소가 있으므로 진행 상황에 대한보다 자세한 그림이 필요합니다.

우리가 모르는 것, 그러나 :

  1. 왜 성능이 SELECT *중요합니까? 코드에서 사용하는 패턴입니까? 그렇다면 왜 그렇습니까?
  2. XML 열만 선택하는 성능은 무엇입니까? 다음과 같은 경우 통계 및 타이밍은 무엇입니까 SELECT TOP 1000 XmlColumn FROM TABLE;?
  3. 이 1000 개의 행을 반환하는 데 걸리는 20 ~ 25 초의 시간은 네트워크 요소 (전세계에서 데이터 가져 오기)와 관련이 있으며 클라이언트 요소와 관련된 정도 (약 15MB + SSMS의 그리드에 XML 데이터가 있거나 디스크에 저장할 수 있습니까?

    작업의이 두 가지 측면을 제외하면 단순히 데이터를 반환하지 않고 수행 할 수 있습니다. 이제 임시 테이블 또는 테이블 변수를 선택하려고 생각할 수도 있지만 몇 가지 새로운 변수 (예 : 디스크 I / O tempdb, 트랜잭션 로그 쓰기, tempdb 데이터 및 / 또는 로그 파일의 자동 증가)가 필요합니다. 버퍼 풀의 공간 등). 이러한 모든 새로운 요소는 실제로 쿼리 시간을 늘릴 수 있습니다. 대신, 나는 일반적으로 열을 SQL_VARIANT각각의 새로운 행 (즉 SELECT @Column1 = tab.Column1,...)으로 덮어 쓰는 변수 (적절한 데이터 유형; 아닌 )에 저장합니다 .

    그러나이 DBA.StackExchange Q & A에서 @PaulWhite가 지적한 것처럼 Logical은 동일한 LOB 데이터에 액세스 할 때 PasteBin에 게시 된 자체 연구에 대한 추가 연구와 함께 다른 읽기를 읽습니다 (LOB 읽기에 대한 다양한 시나리오를 테스트하는 T-SQL 스크립트 ) , LOB를 일관 간의 액세스되지 않는 SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/'),와 SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). 따라서 우리의 옵션은 여기에서 조금 더 제한적이지만 여기에 수행 할 수있는 작업이 있습니다.

    1. SSMS 또는 SQLCMD.EXE에서 SQL Server를 실행하는 서버에서 쿼리를 실행하여 네트워크 문제를 배제하십시오.
    2. 쿼리 옵션-> 결과-> 그리드로 이동하여 "실행 후 결과 폐기"옵션을 확인하여 SSMS에서 클라이언트 문제를 배제하십시오. 이 옵션은 메시지를 포함한 모든 출력을 방지하지만 SSMS가 각 행당 메모리를 할당 한 다음 그리드에 그리는 데 걸리는 시간을 배제하는 데 여전히 유용 할 수 있습니다.
      또는 SQLCMD.EXE를 통해 쿼리를 실행하고 다음을 통해 출력이 아무데도 가지 않도록 지시 할 수 -o NUL:있습니다.
  4. 이 쿼리와 관련된 대기 유형이 있습니까? 그렇다면 대기 유형은 무엇입니까?
  5. 뭐라고입니다 실제 에 대한 데이터 크기 XML열을 반환 ? 전체 테이블에서 해당 열의 평균 크기는 "TOP 1000"행에 전체 XML데이터 의 상당 부분이 포함되어 있으면 실제로 중요하지 않습니다 . TOP 1000 행에 대해 알고 싶다면 해당 행을보십시오. 다음을 실행하십시오 :

    SELECT TOP 1000 tab.*,
           SUM(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [TotalXmlKBytes],
           AVG(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [AverageXmlKBytes]
           STDEV(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [StandardDeviationForXmlKBytes]
    FROM   SchemaName.TableName tab;
  6. 정확한 테이블 스키마. 모든 색인을 포함 하여 전체 CREATE TABLE 설명을 제공하십시오 .
  7. 쿼리 계획? 게시 할 수있는 것입니까? 그 정보는 아마도 아무것도 바꾸지 않을 것이지만 그것이 잘못되지 않을 것이라고 추측하는 것보다 그렇지 않다는 것을 아는 것이 더 좋습니다 ;-)
  8. 데이터 파일에 물리적 / 외부 조각화가 있습니까? 이것이 중요한 요인은 아니지만 SSD 또는 초고가 SATA가 아닌 "소비자 급 SATA"를 사용하고 있기 때문에 하위 섹터 순서의 영향은 특히 해당 섹터 수에 따라 더욱 두드러집니다 읽을 필요가 증가합니다.
  9. 다음 쿼리 의 정확한 결과는 무엇입니까?

    SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(),
                              OBJECT_ID(N'dbo.SchemaName.TableName'), 1, 0, N'LIMITED');

최신 정보

비슷한 상황이 발생하는지 확인하기 위해이 시나리오를 재현해야합니다. 그래서 몇 개의 열 (질문의 모호한 설명과 유사)이있는 테이블을 만든 다음 백만 개의 행으로 채우고 XML 열에는 행 당 약 15k의 데이터가 있습니다 (아래 코드 참조).

내가 찾은 것은 SELECT TOP 1000 * FROM TABLE처음 8 초 만에 완료 하고 그 이후에 2-4 초마다 완료하는 것입니다 (예, DBCC DROPCLEANBUFFERSSELECT *쿼리 실행 전에 실행 ). 그리고 몇 년 전의 랩톱은 SQL Server 2012 SP2 Developer Edition, 64 비트, 6GB RAM, 이중 2.5Ghz Core i5 및 5400RPM SATA 드라이브로 빠르지 않습니다 . SSMS 2014, SQL Server Express 2014, Chrome 및 기타 여러 가지도 실행 중입니다.

시스템의 응답 시간을 기준으로 20-25 초의 응답 시간의 원인을 좁히기 위해 더 많은 정보 (예 : 테이블 및 데이터, 제안 된 테스트 결과 등)가 필요하다는 점을 반복합니다. 당신이보고있는.

SET ANSI_NULLS, NOCOUNT ON;
GO

IF (OBJECT_ID(N'dbo.XmlReadTest') IS NOT NULL)
BEGIN
    PRINT N'Dropping table...';
    DROP TABLE dbo.XmlReadTest;
END;

PRINT N'Creating table...';
CREATE TABLE dbo.XmlReadTest 
(
    ID INT NOT NULL IDENTITY(1, 1),
    Col2 BIGINT,
    Col3 UNIQUEIDENTIFIER,
    Col4 DATETIME,
    Col5 XML,
    CONSTRAINT [PK_XmlReadTest] PRIMARY KEY CLUSTERED ([ID])
);
GO

DECLARE @MaxSets INT = 1000,
        @CurrentSet INT = 1;

WHILE (@CurrentSet <= @MaxSets)
BEGIN
    RAISERROR(N'Populating data (1000 sets of 1000 rows); Set # %d ...',
              10, 1, @CurrentSet) WITH NOWAIT;
    INSERT INTO dbo.XmlReadTest (Col2, Col3, Col4, Col5)
        SELECT  TOP 1000
                CONVERT(BIGINT, CRYPT_GEN_RANDOM(8)),
                NEWID(),
                GETDATE(),
                N'<test>'
                  + REPLICATE(CONVERT(NVARCHAR(MAX), CRYPT_GEN_RANDOM(1), 2), 3750)
                  + N'</test>'
        FROM        [master].[sys].all_columns sac1;

    IF ((@CurrentSet % 100) = 0)
    BEGIN
        RAISERROR(N'Executing CHECKPOINT ...', 10, 1) WITH NOWAIT;
        CHECKPOINT;
    END;

    SET @CurrentSet += 1;
END;

--

SELECT COUNT(*) FROM dbo.XmlReadTest; -- Verify that we have 1 million rows

-- O.P. states that the "clustered index fragmentation is close to 0%"
ALTER INDEX [PK_XmlReadTest] ON dbo.XmlReadTest REBUILD WITH (FILLFACTOR = 90);
CHECKPOINT;

--

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 * FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,       physical reads 1,     read-ahead reads 4436,
              lob logical reads 5676, lob physical reads 1, lob read-ahead reads 3967.

 SQL Server Execution Times:
   CPU time = 171 ms,  elapsed time = 8329 ms.
*/

그리고 비 LOB 페이지를 읽는 데 걸리는 시간을 고려하고 싶기 때문에 다음 쿼리를 실행하여 XML 열을 제외한 모든 열을 선택했습니다 (위에서 제안한 테스트 중 하나). 이것은 1.5 초 안에 상당히 일관되게 돌아옵니다.

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 ID, Col2, Col3, Col4 FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,    physical reads 1,     read-ahead reads 4436,
              lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1666 ms.
*/

결론 (현재)
시나리오를 재현하려는 시도를 바탕으로 SATA 드라이브 또는 비 순차 I / O를 20-25 초의 주요 원인으로 지적 할 수 있다고 생각하지 않습니다. XML 열을 포함하지 않을 때 쿼리가 얼마나 빨리 반환되는지 모릅니다. 그리고 나는 당신이 보여주는 많은 수의 논리 읽기 (비 LOB)를 재현 할 수 없었지만 , 그 내용 진술 에 비추어 각 행에 더 많은 데이터를 추가해야한다고 생각합니다 .

테이블 페이지의 ~ 90 %가 LOB_DATA입니다.

내 테이블에는 백만 개의 행이 있으며 각 행에는 15k 이상의 XML 데이터 sys.dm_db_index_physical_stats가 있으며 LOB_DATA 페이지는 2 백만입니다. 나머지 10 %는 222k IN_ROW 데이터 페이지이지만 11,630 개만 있습니다. 다시 한 번, 실제 테이블 스키마 및 실제 데이터에 대한 추가 정보가 필요합니다.


이 토론은 채팅 으로 이동 되었습니다 .
Paul White는 GoFundMonica가

10

LOB_DATA 페이지는 크기 때문에뿐만 아니라 SQL Server가 클러스터형 인덱스를 효과적으로 스캔 할 수 없기 때문에 느린 스캔을 일으킬 수 있다고 생각하고 있습니까?

예, 행에 저장되지 않은 LOB 데이터를 읽으면 순차적 IO 대신 임의 IO가 발생합니다. 속도가 빠르거나 느린 이유를 이해하기 위해 여기에서 사용하는 디스크 성능 지표는 Random Read IOPS입니다.

LOB 데이터는 클러스터 된 인덱스의 데이터 페이지가 실제 LOB 데이터를 가리키는 LOB 루트 구조가있는 LOB 데이터 페이지를 가리키는 트리 구조에 저장됩니다 . 클러스터형 인덱스에서 루트 노드를 통과 할 때 SQL Server는 순차적 읽기를 통해서만 행 내 데이터를 가져올 수 있습니다. LOB 데이터를 얻으려면 SQL Server가 디스크의 다른 곳으로 이동해야합니다.

SSD 디스크로 변경하면 SSD의 임의 IOPS가 회전 디스크보다 훨씬 높기 때문에 그다지 큰 어려움을 겪지 않을 것입니다.

그러한 테이블 구조 / 데이터 패턴을 갖는 것이 합리적인 것으로 간주됩니까?

그렇습니다. 이 테이블이 당신을 위해 무엇을하고 있는지에 달려 있습니다.

일반적으로 SQL Server에서 XML의 성능 문제는 T-SQL을 사용하여 XML에 쿼리하려고 할 때와 where 절 또는 조인의 조건 자에서 XML의 값을 사용하려는 경우에 발생합니다. 이 경우 속성 승격 또는 선택적 XML 인덱스를 보거나 대신 XML을 테이블로 파쇄하는 테이블 구조를 다시 디자인 할 수 있습니다.

나는 압축을 시도

10 년 전에는 제품에서 한 번 해봤고 그 이후로 후회했습니다. T-SQL을 사용하여 데이터를 사용할 수 없다는 점을 정말로 놓쳤으므로 피할 수 있다면 누구에게도 권장하지 않습니다.


답변 주셔서 감사합니다. 압축과 관련하여 : T-SQL에서 해당 데이터를 실제로 쿼리해야 할 필요성은 분명히 저장된 데이터의 특성에 따라 다르기 때문에 이러한 엄격한 권장 사항이 정당한지 확실하지 않습니다. 내 경우에는 지금 압축을 사용하기로 결정했습니다.
Alexander Shelemin 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.