테이블의 데이터 공간이 원시 데이터 크기의 4 배를 차지하는 이유는 무엇입니까?


18

490M 행과 55GB의 테이블 스페이스가있는 테이블이 있으므로 행당 약 167 바이트입니다. 이 테이블에는 a VARCHAR(100), a DATETIME2(0)및 a의 세 가지 열 이 SMALLINT있습니다. 텍스트의 평균 길이 VARCHAR22 +를위한 2 원 데이터는 행 당 약 32 바이트이어야하므로 필드 21.5 관한 VARCHAR대 6 DATETIME216 비트 정수, 2.

위의 공간은 인덱스가 아니라 데이터 일뿐입니다. 속성 | 아래에보고 된 값을 사용하고 있습니다. 저장 | 일반 | 데이터 공간.

물론 약간의 오버 헤드 가 있어야 하지만 행 당 135 바이트는 특히 큰 테이블의 경우 많이 보입니다. 왜 이것이 될 수 있습니까? 다른 사람이 비슷한 승수를 보았습니까? 필요한 추가 공간의 양에 영향을 줄 수있는 요인은 무엇입니까?

비교를 위해 두 INT필드와 1M 행 으로 테이블을 만들려고했습니다 . 필요한 데이터 공간은 8 바이트의 원시 데이터와 비교하여 행당 17 바이트 인 16.4MB입니다. 실제 테이블과 동일한 텍스트로 채워진 INT및 다른 테스트 테이블 VARCHAR(100)은 행 당 39 바이트 (44 K 행)를 사용하며 28 + 조금 더 기대합니다.

따라서 생산 테이블에는 상당히 많은 오버 헤드가 있습니다. 더 크니까요? 인덱스 크기가 대략 N * log (N) 일 것으로 예상하지만 실제 데이터에 필요한 공간이 비선형 인 이유는 알 수 없습니다.

모든 포인터에 미리 감사드립니다!

편집하다:

나열된 모든 필드는 NOT NULL입니다. 실제 테이블에는 VARCHAR필드와 DATETIME2필드에 순서대로 클러스터 된 PK가 있습니다. 두 테스트에서 첫 번째 INT는 (클러스터 된) PK입니다.

중요한 경우 : 테이블은 핑 결과 레코드입니다. 필드는 URL, 핑 날짜 / 시간 및 대기 시간 (밀리 초)입니다. 데이터가 지속적으로 추가되고 업데이트되지는 않지만 데이터는 주기적으로 삭제되어 URL 당 시간당 몇 개의 레코드로 줄어 듭니다.

편집하다:

여기 에서 매우 흥미로운 답변 은 많은 읽기와 쓰기가있는 인덱스의 경우 재구성이 유리하지 않을 수 있음을 나타냅니다. 필자의 경우 소비되는 공간이 문제이지만 쓰기 성능이 더 중요하면 연약한 인덱스를 사용하는 것이 좋습니다.

답변:


11

원래 질문에 대한 의견에 대한 토론 후에이 경우 손실 된 공간은 클러스터 된 키의 선택으로 인해 발생하여 막대한 조각화가 발생합니다.

이러한 상황에서는 항상 sys.dm_db_index_physical_stats를 통해 조각화 상태를 확인할 가치가 있습니다.

편집 : 의견 업데이트 후

평균 페이지 밀도 (클러스터형 인덱스를 다시 작성하기 전)는 24 %로 원래 질문과 완벽하게 일치합니다. 페이지는 1/4로 가득 차서 전체 크기는 원시 데이터 크기의 4 배였습니다.


7

온 디스크 구조에는 오버 헤드가 있습니다.

  • 행 헤더
  • 널 비트 맵 + 포인터
  • 가변 길이 열 오프셋
  • 행 버전 포인터 (선택 사항)
  • ...

2 x 4 바이트 int 열을 취하면

  • 4 바이트 행 헤더
  • NULL 비트 맵에 대한 2 바이트 포인터
  • 2 int 열의 경우 8 바이트
  • 3 바이트 NULL 비트 맵

와우 17 바이트!

원래 테스트보다 오버 헤드가 더 높은 두 번째 테스트 테이블에 대해서도 동일 할 수 있습니다.

  • 가변 길이 열 수의 경우 2 바이트
  • 가변 길이 열당 2 바이트

왜 차이점이 있습니까? 또한 (이것들에 링크하지 않습니다)

  • 조각 모음을 위해 인덱스를 다시 만든 적이 있습니까?
  • 삭제 공간을 확보하지 않습니다
  • 중간에 삽입하면 데이터 페이지가 분할됩니다
  • 업데이트로 인해 앞으로 포인터가 발생할 수 있습니다 (갭이 남음).
  • 행 오버플로
  • 인덱스 다시 작성 또는 DBCC CLEANTABLE없이 varchar 열을 제거했습니다.
  • 힙 또는 테이블 (힙에 클러스터 된 인덱스가 없음 = 레코드가 흩어져 있음)
  • RCSI 격리 수준 (행당 추가 14 바이트)
  • varchar에서 후행 공백 (SET ANSI_PADDING은 기본적으로 ON) LEN이 아닌 DATALENGTH를 사용하여 checl
  • 다음과 같이 sp_spaceused를 실행하십시오. @updateusage = 'true'
  • ...

이 참조 SQL 서버 : 어떻게 8킬로바이트 페이지를 채우는 테이블을 만들려면?

SO에서 :


2x4 바이트 int 열 샘플이 100 % 정확하지 않습니다. 4 바이트 행 헤더가 있습니다 (고정 길이 데이터 크기의 경우 2 개의 상태 바이트 및 2 바이트). 그런 다음 데이터에 2x4 바이트가 있습니다. 두 아닌 제 15 바이트의 총 레코드 길이주는 열의 수와 널 비트 맵에 대한 단일 바이트의 바이트
마크 S. 스무

@Mark S. Rasmussen : "고정 길이 데이터 크기의 경우 2 바이트"를 어디서 얻습니까? MSDN? 그리고 null 비트 맵은 항상 3 바이트입니다. sqlskills.com/blogs/paul/post/… + msdn.microsoft.com/en-us/library/ms178085%28v=sql.90%29.aspx
gbn

와우, 훌륭한 세부 사항! VARCHAR위의 추정 에서 s 의 길이 필드를 설명 했지만 열 수는 설명하지 않았습니다. 이 테이블에는 NULL 입력 가능 필드가 없으며 (이를 언급해야 함) 여전히 바이트를 할당합니까?
모든 거래의 존

인덱스 재 구축이 필요한 공간 의 데이터 부분에 영향을 줍 니까? 아마도 클러스터형 인덱스를 다시 작성했을 것입니다. 삽입은 중간에 많이 발생하지만 클러스터링 필드의 순서를 바꾸면 중지됩니다. 이 경우 나머지 대부분은 적용되지 않아야하지만 일반적인 경우에 대한 훌륭한 참조입니다. 나는 당신의 링크를 확인합니다. 좋은 물건!
모든 거래의 존

1
@gbn 고정 길이 데이터 크기의 2 바이트는 언급 한 4 바이트 행 헤더의 일부입니다. 고정 데이터 길이 부분의 끝 / 열 수 / 널 비트 맵의 ​​시작을 가리키는 포인터입니다. NULL 비트 맵이 항상 3 바이트 인 것은 아닙니다 . 열 수를 포함하면 최소 3 바이트가되지만 더 많을 수 있습니다-설명에서 비트 맵과 열 수를 분할합니다. 또한 NULL 비트 맵은 항상 존재하지는 않지만이 경우에는 존재합니다.
Mark S. Rasmussen

5

시간이 지남에 따라 데이터 유형이 변경 되었습니까? 가변 길이 열이 제거 되었습니까? 색인은 자주 조각 모음되었지만 다시는 작성되지 않았습니까? 많은 행이 삭제되었거나 가변 길이 열이 많이 업데이트 되었습니까? 여기에 좋은 토론이 있습니다 .


데이터 유형을 변경하지 않았거나 필드를 제거하지 않았다고 97 % 확신합니다. 내가 한 경우 테이블에 훨씬 적은 수의 행이 있었을 때 실제로 초기에 있었을 것입니다. 삭제 또는 업데이트가 없으며 데이터 만 추가됩니다.
모든 거래의 존

수정 : 삭제 가 있으며 약간 있습니다. 이 테이블은 순 증가율이 높기 때문에이 공간이 빠르게 재사용 될 것이라고 생각합니다.
모든 거래의 존

삭제가 많으면 데이터가 재사용되거나 재사용되지 않을 수 있습니다. 테이블의 클러스터링 키는 무엇입니까? 테이블 가운데 나 끝에 삽입물이 있습니까?
mrdenny

클러스터 된 키는 VARCHARDATETIME2필드에서 순서대로 복합 입니다. 인서트는 첫 번째 필드에 균등하게 분배됩니다. 두 번째 필드의 경우 새 값이 항상 기존 값보다 큽니다.
모든 거래의 존
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.