SQL Server 2012에서 PK GUID 인덱싱


13

내 개발자는 거의 모든 테이블에 GUID를 PK로 사용하도록 응용 프로그램을 설정했으며 기본적으로 SQL Server는 이러한 PK에 클러스터 된 인덱스를 설정했습니다.

시스템은 비교적 젊고 가장 큰 테이블은 백만 행이 넘지 만 색인 작성을 검토하고 있으며 가까운 미래에 필요할 때 빠르게 확장 할 수 있기를 원합니다.

그래서 첫 번째 성향은 클러스터 된 인덱스를 DateTime의 가장 큰 표현 인 생성 된 필드로 옮기는 것이 었습니다. 그러나 CX를 고유하게 만들 수있는 유일한 방법은이 CX에 GUID 열을 포함시키는 것이지만 먼저 생성 된 순서입니다.

이렇게하면 클러스터링 키가 너무 넓어지고 쓰기 성능이 향상됩니까? 읽기도 중요하지만이 시점에서 쓰기가 더 큰 관심사 일 수 있습니다.


1
GUID는 어떻게 생성됩니까? NEWID 또는 NEWSEQUENTIALID?
swasheck

6
clustered guid 및 insert 성능은 "performance"바로 앞에 나오는 단어가 최소화 된 경우에만 한 문장이어야합니다.
billinkc

2
개발자들에게 점심을 꺼내고 NEWID ()를 기본 키로 다시 사용하면 성능이 저하 될 것이라고 설명합니다. 그들은 그것을 막기 위해 무엇을해야하는지 매우 빨리 물어볼 것입니다. 이때 IDENTITY (1,1)을 대신 사용한다고합니다. (아마도 약간 단순화되었지만 10 중 9 번 작동합니다).
Max Vernon

3
guid에 대한 우리의 증오의 이유는 그것들이 넓고 (16 바이트), 생성되지 않을 때 newsequentialid무작위 적이라는 것입니다. 클러스터 키는 좁고 증가 할 때 가장 좋습니다. GUID는 뚱뚱하고 무작위입니다. 책장이 거의 책으로 가득 차 있다고 상상해보십시오. OED는 길드의 무작위성으로 인해 선반 중앙에 삽입됩니다. 물건을 주문 상태로 유지하려면 책의 오른쪽 절반이 시간이 많이 걸리는 새로운 위치에 구멍을 뚫어야합니다. 이것이 GUID가 데이터베이스에서 수행하는 작업이며 성능을 저하시킵니다.
billinkc

7
uniqueidentifiers 사용의 문제점을 해결하는 방법은 드로잉 보드로 돌아가서 uniqueidentifiers를 사용하지 않는 것입니다 . 그들은없는 끔찍한 시스템이 작은 경우,하지만 당신 만 적어도 몇 + 행 테이블 (또는 테이블보다 큰) 키에 대한 uniqueidentifiers를 사용하여 분쇄 얻을 것 밖으로, 당신이있는 거 평면이있는 경우.
Jon Seigel

답변:


20

GUID, 특히 비 순차적 인 GUID의 주요 문제는 다음과 같습니다.

  • 키 크기 (INT의 경우 16 바이트 대 4 바이트) : 이는 클러스터 인덱스 인 경우 인덱스에 대한 추가 공간과 함께 키에 4 배의 데이터 양을 저장한다는 의미입니다.
  • 인덱스 조각화 : 키 값의 무작위 특성으로 인해 비 순차적 GUID 열 조각 모음을 유지하는 것은 사실상 불가능합니다.

이것이 당신의 상황에 어떤 의미가 있습니까? 그것은 당신의 디자인에 달려 있습니다. 시스템이 단순히 쓰기에 관한 것이며 데이터 검색에 대해 걱정하지 않으면 Thomas K가 설명한 접근 방식이 정확합니다. 그러나이 전략을 추구하면 해당 데이터를 읽고 저장하는 데 많은 잠재적 인 문제가 발생한다는 점을 명심해야합니다. 로 존 Seigel는 지적, 당신은 또한 더 많은 공간을 점유 할 본질적으로 메모리 팽창을 가진 것입니다.

GUID에 대한 주요 질문은 이들이 얼마나 필요한지입니다. 개발자는 글로벌 고유성을 보장하기 때문에 좋아하지만 이러한 종류의 고유성이 필요한 경우는 드 rare니다. 그러나 최대 값 수가 2,147,483,647 (4 바이트 부호있는 정수의 최대 값)보다 작 으면 키에 적절한 데이터 유형을 사용하지 않는 것입니다. BIGINT (8 바이트)를 사용하더라도 최대 값은 9,223,372,036,854,775,807입니다. 고유 키에 대해 자동 증분 값이 필요한 경우 일반적으로 비전 역 데이터베이스 (및 많은 전역 데이터베이스)에 충분합니다.

마지막으로 힙 인덱스와 클러스터형 인덱스를 사용하는 한 순수하게 데이터를 작성하는 경우 인서트에 대한 오버 헤드를 최소화하므로 힙이 가장 효율적입니다. 그러나 SQL Server의 힙은 데이터 검색에 매우 비효율적입니다. 내 경험에 따르면 클러스터 인덱스를 선언 할 기회가 있으면 클러스터형 인덱스가 항상 바람직합니다. 클러스터 인덱스를 테이블에 추가하면 (40 억 개 이상의 레코드) 전체 선택 성능이 6 배 향상됩니다.

추가 정보:


13

OLTP 시스템에서 GUID를 키 및 클러스터로 잘못 사용하는 것은 아무 문제가 없습니다 (클러스터의 크기가 커지는 테이블에 많은 인덱스가없는 경우 제외). 실제로 IDENTITY 열보다 확장 성이 훨씬 뛰어납니다.

GUID가 SQL Server에서 큰 문제라고 널리 퍼져 있습니다. 대체로 이것은 잘못된 것입니다. 실제로 GUID는 약 8 개 이상의 코어가있는 박스에서 훨씬 확장 성이 뛰어납니다.

죄송하지만 개발자가 옳습니다. GUID에 대해 걱정하기 전에 다른 것들에 대해 걱정하십시오.

아, 그리고 마지막으로 : 왜 클러스터 인덱스를 먼저 원하십니까? 작은 인덱스가 많은 OLTP 시스템이 우려되는 경우 힙을 사용하는 것이 좋습니다.

이제 GUID가 소개 할 조각화가 읽은 내용에 대해 고려해 보겠습니다. 조각화에는 세 가지 주요 문제가 있습니다.

  1. 페이지 분할 비용 디스크 I / O
  2. 전체 페이지 절반은 전체 페이지만큼 메모리 효율적이지 않습니다
  3. 페이지가 순서대로 저장되지 않으므로 순차적 I / O 가능성이 줄어 듭니다.

이 문제에 대한 우려는 확장성에 관한 것이므로 "하드웨어를 더 추가하면 시스템 속도가 빨라진다"고 정의 할 수 있습니다. 차례로 하나씩 해결하기 위해

광고 1) 규모를 원한다면 I / O를 구매할 여유가 있습니다. 저렴한 삼성 / 인텔 512GB SSD (USD / GB)는 100K IOPS 이상을 제공합니다. 당신은 2 소켓 시스템에서 곧 그것을 소비하지 않을 것입니다. 그리고 당신이 그것에 부딪 치면, 하나 더 사면 설정됩니다

광고 2) 당신이 당신의 테이블에서 삭제하면, 당신은 어쨌든 전체 페이지 절반이됩니다. 그렇지 않은 경우에도 메모리는 저렴하고 가장 큰 OLTP 시스템을 제외하고는 모두 핫 데이터가 적합해야합니다. 더 많은 데이터를 페이지에 넣는 것은 스케일을 찾을 때 하위 최적화입니다.

Ad 3) 페이지 분할 빈도가 높고 조각난 데이터로 구성된 테이블은 순차적으로 채워진 테이블과 동일한 속도로 임의 I / O를 수행합니다.

조인과 관련하여 워크로드와 같은 OLTP에서 볼 수있는 두 가지 주요 조인 유형은 해시 및 루프입니다. 각각을 차례로 살펴 보자.

해시 조인 : 해시 조인은 작은 테이블이 검색되고 더 큰 테이블이 일반적으로 검색된다고 가정합니다. 작은 테이블은 메모리에있을 가능성이 높으므로 여기서 I / O는 걱정하지 않아도됩니다. 우리는 이미 추구하는 것이 조각화되지 않은 인덱스와 같은 조각화 된 인덱스의 비용이라는 사실에 대해 이미 언급했습니다.

루프 조인 : 외부 테이블을 찾습니다. 같은 비용

잘못된 테이블 스캔이 많이 발생할 수도 있지만 GUID는 다시 걱정할 필요가 없으며 적절한 인덱싱입니다.

이제 합법적 인 범위 스캔이 진행될 수 있으며 (특히 외래 키에 참여할 때) 조각화 된 데이터가 조각화되지 않은 데이터와 비교하여 덜 "포장 된"것입니다. 그러나 3NF 데이터가 잘 색인화되어있는 조인은 다음과 같습니다.

  1. 참조하는 테이블의 기본 키에 대한 외래 키 참조가있는 테이블의 조인

  2. 다른 방법으로

광고 1)이 경우 기본 키에 대한 단일 탐색-n에 1을 결합)-조각화 여부와 동일한 비용 (1 회 탐색)

광고 2)이 경우 동일한 키에 참여하고 있지만 둘 이상의 행을 검색 할 수 있습니다 (범위 탐색). 이 경우 조인은 1에서 n입니다. 그러나 당신이 찾고있는 외래 테이블, 당신은 SAME 키를 찾고 있습니다.이 키는 조각화되지 않은 것과 같은 페이지에있을 가능성이 높습니다.

외래 키를 잠시 고려하십시오. 기본 키를 "완벽하게"순차적으로 배치 했더라도 해당 키를 가리키는 것은 순차적이지 않습니다.

물론 돈이 저렴하고 프로세스가 많은 일부 은행의 SAN에있는 가상 머신에서 실행 중일 수 있습니다. 그러면이 모든 조언을 잃게됩니다. 그러나 그것이 당신의 세계라면, 확장 성은 아마도 당신이 찾고있는 것이 아닐 것입니다-당신은 성능과 빠른 속도 / 비용을 찾고 있습니다.


1
의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Paul White 9

5

토마스 : 당신의 요점 중 일부는 완전한 의미를 가지고 있으며 나는 그들 모두에 동의합니다. SSD를 사용하는 경우 최적화 대상의 균형이 변경됩니다. 랜덤 대 순차는 회전 디스크와 같은 토론이 아닙니다.

특히 순수한 DB보기를 취하는 것은 끔찍한 일이라는 데 동의합니다. DB 성능 향상시키기 위해 애플리케이션을 느리게 확장 할 수 없게 만드는 것은 상당히 잘못된 길입니다.

IDENTITY (또는 시퀀스 또는 DB에서 생성 된 것) 의 큰 문제 는 키를 만들기 위해 DB 로의 왕복이 필요하기 때문에 엄청나게 느리다는 것입니다. 이로 인해 DB에 자동으로 병목 현상이 발생하여 응용 프로그램이 키를 사용하여 DB 호출을 시작하십시오. GUID를 만들면 응용 프로그램을 사용하여 키를 만들어이 문제를 해결할 수 있으며, 정의에 따라 전역 적으로 고유 한 것으로 보장되므로 응용 프로그램 계층에서 DB 왕복 시간이 발생하기 전에이를 사용하여 레코드를 전달할 수 있습니다.

그러나 GUID에 대한 대안을 사용하는 경향이 있습니다. 여기에서 데이터 유형에 대한 개인적 선호는 앱에서 생성 된 세계적으로 고유 한 BIGINT입니다. 어떻게하면 되나요? 가장 간단한 예에서는 작고 매우 가벼운 함수를 앱에 추가하여 GUID를 해시합니다. 해시 함수가 빠르고 비교적 빠르다고 가정합니다 (예 : Google의 CityHash 참조 : http://google-opensource.blogspot.in/2011/04/introducing-cityhash.html- 모든 컴파일 단계를 올바르게 수행해야합니다. 또는 간단한 코드 의 경우 http://tools.ietf.org/html/draft-eastlake-fnv-03 의 FNV1a 변형 ) 응용 프로그램에서 생성 한 고유 식별자와 CPU가 더 잘 작동하는 64 비트 키 값의 이점을 얻습니다. .

BIGINT를 생성하는 다른 방법이 있으며,이 두 알고리즘 모두 해시 충돌의 가능성이 있습니다. 읽기 및 의식적인 결정을 내리십시오.


2
Thomas의 답변에 대한 답변이 아니라 OP의 질문에 대한 답변으로 답변을 편집하는 것이 좋습니다. Thomas (, MikeFal 's)와 제안의 차이점을 여전히 강조 할 수 있습니다.
ypercubeᵀᴹ

2
질문에 대한 답변을 알려주십시오. 당신이하지 않으면 우리는 당신을 위해 그것을 제거합니다.
JNK

2
의견 마크에 감사드립니다. 답변을 편집하면 (아주 ​​좋은 컨텍스트를 제공한다고 생각합니다) 한 가지만 변경합니다. INSERT에주의를 기울이면 IDENTITY에 서버에 대한 추가 왕복 여행이 필요하지 않습니다. INSERT를 호출하는 배치에서 항상 SCOPE_IDENTITY ()를 반환 할 수 있습니다.
Thomas Kejser

1
"키를 생성하기 위해 DB 로의 왕복이 필요하기 때문에 엄청나게 느리다"와 관련하여-한 번의 왕복 여행에서 필요한만큼을 얻을 수 있습니다.
AK

"한 번의 왕복 여행에서 필요한만큼을 얻을 수 있습니다"에 관하여-IDENTITY 열이나 기본적으로 데이터베이스 수준에서 DEFAULT를 사용하는 다른 방법으로는이 작업을 수행 할 수 없습니다.
Avi Cherry
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.