BLOB를 별도의 SQL Server 테이블에 저장하는 것이 왜 권장됩니까?


28

이 고도로 찬성 된 SO 답변 은 다른 테이블과 1 : 1 관계가 있더라도 이미지를 별도의 테이블에 배치하는 것이 좋습니다.

그림을 SQL Server 테이블에 저장하기로 결정한 경우 해당 사진을 저장하기 위해 별도의 테이블을 사용하는 것이 좋습니다. 직원 테이블에 직원 사진을 저장하지 말고 별도의 테이블에 보관하십시오. 이렇게하면 직원 테이블을 쿼리의 일부로 항상 직원 사진을 선택할 필요가 없다고 가정 할 때 직원 테이블을 간결하고 의미 있고 효율적으로 유지할 수 있습니다.

왜? SQL Server 가 테이블에 전용 BLOB 데이터 구조대한 포인터 만 저장 한다는 인상을 받았습니다 . 따라서 다른 간접 계층을 수동으로 만드는 것이 왜 귀찮습니까? 실제로 성능이 크게 향상됩니까? 그렇다면 왜 그렇습니까?

답변:


15

BLOB이 다른 테이블에 있어야한다는 데는 동의하지 않지만 데이터베이스에 있으면 안됩니다 . 디스크에 파일이있는 위치에 대한 포인터를 저장 한 다음 데이터베이스에서 가져옵니다.

그들이 (나를 위해) 일으키는 주요 문제는 색인 작성입니다. 쿼리 계획에 XML을 사용하면 모든 사람들이 참여할 수 있으므로 테이블을 만들어 보겠습니다

SELECT TOP 1000
ID = IDENTITY(INT,1,1),
deq.query_plan
INTO dbo.index_test
FROM sys.dm_exec_cached_plans AS dec
CROSS APPLY sys.dm_exec_query_plan(dec.plan_handle) AS deq

ALTER TABLE dbo.index_test ADD CONSTRAINT pk_id PRIMARY KEY CLUSTERED (ID)

1000 행이지만 크기를 확인하는 중입니다 ...

sp_BlitzIndex @DatabaseName = 'StackOverflow', @SchemaName = 'dbo', @TableName = 'index_test'

단 1000 행에 40MB가 넘습니다. 1000 행마다 40MB를 추가한다고 가정하면 꽤 빨리 추악해질 수 있습니다. 백만 행에 도달하면 어떻게됩니까? 그것은 약 1TB의 데이터입니다.

견과류

클러스터형 인덱스를 사용해야하는 모든 쿼리는 이제 BLOB 데이터 열이 참조 될 때 해당 BLOB 데이터를 모두 메모리 설명 으로 읽어야합니다 .

BLOB를 저장하는 것보다 SQL Server 메모리를 사용하는 더 좋은 방법을 생각할 수 있습니까? 확실히 할 수 있기 때문입니다.

비 클러스터형 인덱스로 확장 :

CREATE INDEX ix_noblob ON dbo.index_test (ID)

CREATE INDEX ix_returnoftheblob ON dbo.index_test (ID) INCLUDE (query_plan)

BLOB 열을 크게 피하도록 비 클러스터형 인덱스를 디자인 할 수 있으므로 일반 쿼리는 클러스터형 인덱스를 피할 수 있지만 해당 BLOB 열이 필요할 때 클러스터형 인덱스가 필요합니다.

INCLUDED키 조회 시나리오를 피하기 위해 비 클러스터형 인덱스에 열로 추가하면 거대한 비 클러스터형 인덱스가 생깁니다.여기에 이미지 설명을 입력하십시오

더 많은 문제가 발생합니다.

  • 누구나 SELECT *쿼리를 실행 하면 모든 BLOB 데이터를 얻습니다.
  • 백업 및 복원에서 공간을 차지하여 속도가 느려집니다.
  • DBCC CHECKDB부패를 확인하고 있다는 것을 알고 있기 때문에 속도가 느려집니다 .
  • 그리고 인덱스 유지 관리를 수행하면 속도가 느려집니다.

이것이 도움이되기를 바랍니다!


7
사용자는 일반적으로 SELECT *를 입력하므로
브렌트 오자르

나는 당신이 언급 한 단점이 왜 그림을 별도의 테이블에 놓기를 권유했는지의 일부라고 생각합니다. 사용자에 대한 다양한 보고서를 실행하는 경우 해당 사진 파일이 필요하지 않습니다. 단일 사용자의 프로필 페이지를로드하는 경우 Blob 테이블에 가입 할 때입니까? 여기에 뭔가 빠진 것이 있습니까 (예 :이 시나리오에서도 실제로 단점이 있습니까?)
BVernon

11

이 이미지는 얼마나 크며 얼마나 많이 기대하십니까? 나는 @sp_BlitzErik에 대부분 동의하지만 ,이 작업을 수행하는 것이 좋은 시나리오가 있다고 생각하므로 실제로 여기에서 요청되는 사항을 더 명확하게 이해하는 것이 도움이 될 것입니다.

Erik이 지적한 대부분의 부정적인 측면을 완화시키는 것으로 고려해야 할 몇 가지 옵션은 다음과 같습니다.

이 두 옵션은 BLOB를 SQL Server에 완전히 저장하거나 완전히 외부에 저장하는 중간 단계로 설계되었습니다 (경로를 유지하기 위해 문자열 colun 제외). 이를 통해 BLOB가 데이터 모델의 일부가되고 버퍼 풀 (예 : 메모리)의 공간을 낭비하지 않으면 서 트랜잭션에 참여할 수 있습니다. BLOB 데이터는 여전히 백업에 포함되므로 더 많은 공간을 차지하고 백업 복원합니다. 그러나 응용 프로그램의 일부 인 경우 어떻게 든 백업해야하며 경로가 포함 된 문자열 열 만 완전히 분리되어 BLOB 파일을 가져올 수 있다는 점을 감안할 때이 사실을 부정적인 것으로 간주하는 데 어려움을 겪고 있습니다. DB에서 그 표시를 나타내지 않고 삭제되었습니다 (즉, 잘못된 포인터 / 누락 된 파일). 또한 DB 내에서 파일을 "삭제"할 수는 있지만 결국 정리해야하는 파일 시스템 (예 : 두통)에 여전히 존재합니다. 그러나 파일이 HUGE 인 경우 경로 열을 제외하고 SQL Server 외부에 완전히 두는 것이 가장 좋습니다.

이는 "내부 또는 외부"질문에 도움이되지만 단일 테이블 대 다중 테이블 질문에는 영향을주지 않습니다. 이 특정 질문 이외에도 사용 패턴을 기반으로 테이블을 열 그룹으로 분할하는 데 유효한 사례가 있다고 말할 수 있습니다. 하나의 열이 50 개 이상인 경우 자주 액세스하는 열과 그렇지 않은 열이있는 경우가 종종 있습니다. 일부 열은 자주 쓰지만 일부 열은 대부분 읽습니다. 자주 액세스하는 열과 자주 액세스하지 않는 열을 1 : 1 관계를 갖는 여러 테이블로 분리하면 사용하지 않는 데이터에 대해 버퍼 풀의 공간을 낭비하는 이유 (큰 이미지를 정기적으로 저장하는 이유와 유사)VARBINARY(MAX)열이 문제입니까)? 또한 행 크기를 줄이고 데이터 페이지에 더 많은 행을 맞추면 읽기 (물리적 및 논리적)를보다 효율적으로 수행하여 자주 액세스하는 열의 성능을 향상시킵니다. 물론 PK를 복제해야하므로 약간의 비 효율성이 발생하기도합니다. 이제 두 테이블을 조인해야 할 경우도 있습니다.

따라서 취할 수있는 몇 가지 접근 방식이 있으며 가장 좋은 방법은 환경과 달성하려는 대상에 따라 다릅니다.


SQL Server가 테이블에 전용 BLOB 데이터 구조에 대한 포인터 만 저장한다는 인상을 받았습니다.

그렇게 간단하지 않습니다. 여기서 좋은 정보를 찾을 수 있습니다. Varchar, Varbinary 등과 같은 (MAX) 유형의 LOB 포인터 크기는 얼마입니까? 기본 사항은 다음과 같습니다.

  • TEXT, NTEXTIMAGE데이터 유형 (기본값) : 16 바이트 포인터
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(기본적으로) :
    • 데이터가 행에 맞으면 거기에 배치됩니다
    • 데이터가 약보다 작은 경우. 40,000 바이트 (링크 된 블로그 게시물 쇼 상한 40,000하지만 내 테스트는 약간 높은 값을 보였다) 이 구조에 대한 행에 공간이있는 경우, 다음에서 시작, LOB 페이지에 1 개, 5 직접 링크 사이에있을 것입니다 첫 번째 8000 바이트에 대한 첫 번째 링크의 경우 24 바이트이며, 8000 바이트의 추가 세트 각각에 대해 각 추가 링크 당 12 바이트 씩 최대 72 바이트입니다.
    • 데이터가 약 이상인 경우. 40,000 바이트 또는 적절한 수의 직접 링크를 저장할 공간이 충분하지 않습니다 (예 : 행에 40 바이트 만 남았고 20,000 바이트 값은 첫 번째에 24 바이트, 48 바이트에 대한 두 개의 추가 링크에 대해 12 바이트 인 3 개의 링크가 필요함) 필요한 전체 행 공간), LOB 페이지에 대한 링크가 포함 된 텍스트 트리 페이지에 대한 24 바이트 포인터 만 있습니다.

7

어떤 이유로 든 데이터를 SQL Server에 저장 해야하는 경우 별도의 테이블에 저장하면 몇 가지 이점을 생각할 수 있습니다. 일부는 다른 것보다 더 설득력이 있습니다.

  1. 데이터를 별도의 테이블에두면 별도의 데이터베이스에 저장할 수 있습니다. 이는 예정된 유지 보수에 이점이있을 수 있습니다. 예를 들어, DBCC CHECKDBBLOB 데이터가 포함 된 데이터베이스에서만 실행할 수 있습니다 .

  2. BLOB에 항상 8000 바이트 이상을 넣지 않으면 일부 행에 대해 저장 될 수 있습니다 . 쿼리에 열이 필요하지 않더라도 클러스터형 인덱스를 사용하여 데이터에 액세스하는 쿼리 속도가 느려지므로 원하지 않을 수 있습니다. 데이터를 별도의 테이블에두면 이러한 위험이 제거됩니다.

  3. 행 외부에 저장하면 SQL Server는 최대 24 바이트 포인터를 사용하여 새 페이지를 가리 킵니다. 공간을 차지하고 단일 테이블에 추가 할 수있는 총 BLOB 열 수를 제한합니다. 자세한 내용은 srutzky의 답변을 참조하십시오.

  4. BLOB 컬럼을 포함하는 테이블에서 클러스터 된 컬럼 저장소 인덱스를 정의 할 수 없습니다. 이 제한은 SQL Server 2017에서 제거되었습니다.

  5. 데이터를 SQL Server 외부로 이동해야한다고 결정한 경우 데이터가 이미 별도의 테이블에 있으면 변경하기가 더 쉬울 수 있습니다.


1
여기에 좋은 점이 있습니다 (+1). 그러나 # 3 (재 : 행 외부 데이터의 24 바이트 포인터)에 대해 명확히하기 위해 항상 올바른 것은 아닙니다. 내 답변 의 맨 아래에 행 의 데이터 유형, 값 크기 및 여유 공간 크기가 포인터의 크기를 결정하는 방법을 설명합니다.
Solomon Rutzky
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.