Lập chỉ mục PK GUID trong SQL Server 2012


13

Các nhà phát triển của tôi đã thiết lập ứng dụng của họ để sử dụng GUID làm PK cho hầu hết các bảng của họ và theo mặc định, SQL Server đã thiết lập chỉ mục được nhóm trên các PK này.

Hệ thống này tương đối trẻ và các bảng lớn nhất của chúng tôi chỉ có hơn một triệu hàng, nhưng chúng tôi đang xem xét lập chỉ mục của chúng tôi và muốn có thể mở rộng nhanh chóng vì có thể cần thiết trong tương lai gần.

Vì vậy, thiên hướng đầu tiên của tôi là di chuyển chỉ mục được nhóm vào trường đã tạo, một đại diện lớn nhất của DateTime. Tuy nhiên, cách duy nhất tôi có thể làm cho CX trở nên độc đáo là đưa cột GUID vào CX này nhưng đặt hàng trước khi tạo.

Điều này có làm cho phím phân cụm quá rộng và nó có tăng hiệu năng cho việc ghi không? Đọc cũng quan trọng, nhưng viết có lẽ là một mối quan tâm lớn hơn vào thời điểm này.


1
GUID được tạo như thế nào? NEWID hay NEWSEQUENTIALID?
swasheck

6
Hướng dẫn phân cụm và hiệu suất chèn chỉ nên ở trong một câu nếu từ ngay trước "hiệu suất" được giảm thiểu
billinkc

2
Đưa các nhà phát triển ra ngoài ăn trưa và giải thích với họ rằng nếu họ sử dụng lại NEWID () làm khóa chính, bạn sẽ đổ lỗi cho hiệu suất kém đối với họ. Họ sẽ rất nhanh chóng hỏi bạn phải làm gì để ngăn chặn điều đó. Tại thời điểm đó, bạn nói sử dụng IDENTITY (1,1) thay thế. (có thể là một sự đơn giản hóa nhẹ nhưng 9 lần trong số 10 sẽ hoạt động).
Max Vernon

3
Lý do cho sự ghét bỏ hướng dẫn của chúng tôi là chúng rộng (16 byte) và khi không được tạo ra newsequentialidlà ngẫu nhiên. Các khóa cụm là tốt nhất khi chúng hẹp và tăng. Một GUID thì ngược lại: béo và ngẫu nhiên. Hãy tưởng tượng một kệ sách gần đầy sách. Đến với OED và vì tính ngẫu nhiên của các hướng dẫn, nó chèn vào giữa kệ. Để giữ mọi thứ theo trật tự, một nửa số sách bên phải phải được đưa vào một vị trí mới, đây là một nhiệm vụ đòi hỏi nhiều thời gian. Đó là những gì GUID đang làm với cơ sở dữ liệu của bạn và giết chết hiệu suất.
billinkc

7
Cách khắc phục vấn đề sử dụng định danh duy nhất là quay lại bảng vẽ và không sử dụng định danh duy nhất . Chúng không tệ nếu hệ thống nhỏ, nhưng nếu bạn có ít nhất vài triệu bảng + hàng (hoặc bất kỳ bảng nào lớn hơn thế), bạn sẽ không bị nghiền nát khi sử dụng bộ nhận dạng duy nhất cho các phím.
Jon Seigel

Câu trả lời:


20

Các vấn đề chính với GUID, đặc biệt là các vấn đề không tuần tự, là:

  • Kích thước của khóa (16 byte so với 4 byte cho INT): Điều này có nghĩa là bạn đang lưu trữ gấp 4 lần lượng dữ liệu trong khóa cùng với không gian bổ sung đó cho bất kỳ chỉ mục nào nếu đây là chỉ mục được nhóm của bạn.
  • Phân mảnh chỉ mục: Hầu như không thể giữ phân đoạn cột GUID không tuần tự vì tính chất hoàn toàn ngẫu nhiên của các giá trị chính.

Vì vậy, điều này có ý nghĩa gì với tình huống của bạn? Nó đi xuống thiết kế của bạn. Nếu hệ thống của bạn chỉ đơn giản là về ghi và bạn không quan tâm đến việc truy xuất dữ liệu, thì cách tiếp cận được Thomas K vạch ra là chính xác. Tuy nhiên, bạn phải nhớ rằng bằng cách theo đuổi chiến lược này, bạn đang tạo ra nhiều vấn đề tiềm ẩn để đọc dữ liệu đó và lưu trữ nó. Như Jon Seigel chỉ ra, bạn cũng sẽ chiếm nhiều không gian hơn và về cơ bản có sự phình to bộ nhớ.

Câu hỏi chính xung quanh GUID là chúng cần thiết như thế nào. Các nhà phát triển thích chúng bởi vì chúng đảm bảo tính độc đáo toàn cầu, nhưng đây là dịp hiếm hoi mà loại hình duy nhất này là cần thiết. Nhưng hãy xem xét rằng nếu số lượng giá trị tối đa của bạn ít hơn 2.147.483.647 (giá trị tối đa của số nguyên có chữ ký 4 byte), thì có lẽ bạn không sử dụng loại dữ liệu phù hợp cho khóa của mình. Ngay cả khi sử dụng BIGINT (8 byte), giá trị tối đa của bạn là 9,223,372,036,854,775,807. Điều này thường đủ cho bất kỳ cơ sở dữ liệu phi toàn cầu nào (và nhiều cơ sở dữ liệu toàn cầu) nếu bạn cần một số giá trị tăng tự động cho một khóa duy nhất.

Cuối cùng, theo như sử dụng một heap so với một chỉ mục được nhóm, nếu bạn hoàn toàn viết dữ liệu, một heap sẽ hiệu quả nhất vì bạn đang giảm thiểu chi phí cho các lần chèn. Tuy nhiên, heaps trong SQL Server cực kỳ kém hiệu quả cho việc truy xuất dữ liệu. Kinh nghiệm của tôi là một chỉ số được nhóm luôn mong muốn nếu bạn có cơ hội khai báo. Tôi đã thấy việc thêm một chỉ mục được nhóm vào một bảng (4 tỷ + bản ghi) cải thiện hiệu suất chọn tổng thể theo hệ số 6.

Thông tin thêm:


13

Không có gì sai với GUID vì các khóa và cụm trong hệ thống OLTP (trừ khi bạn có RẤT NHIỀU chỉ mục trên bảng chịu kích thước tăng của cụm). Như một vấn đề thực tế, chúng có khả năng mở rộng hơn nhiều so với các cột IDENTITY.

Có một niềm tin phổ biến rằng GUID là một vấn đề lớn trong SQL Server - phần lớn, điều này khá đơn giản là sai. Thực tế, GUID có thể mở rộng đáng kể trên các hộp có nhiều hơn 8 lõi:

Tôi xin lỗi, nhưng các nhà phát triển của bạn là đúng. Lo lắng về những điều khác trước khi bạn lo lắng về GUID.

Oh, và cuối cùng: tại sao bạn muốn một chỉ số cụm ở vị trí đầu tiên? Nếu mối quan tâm của bạn là một hệ thống OLTP với rất nhiều chỉ mục nhỏ, bạn có khả năng tốt hơn với một đống.

Bây giờ chúng ta hãy xem xét phân mảnh (mà GUID sẽ giới thiệu) làm gì cho bài đọc của bạn. Có ba vấn đề lớn với sự phân mảnh:

  1. Trang chia tách chi phí đĩa I / O
  2. Một nửa trang đầy đủ không hiệu quả như bộ nhớ đầy đủ
  3. Nó khiến các trang được lưu trữ không theo thứ tự, điều này làm cho I / O tuần tự ít có khả năng

Vì mối quan tâm của bạn trong câu hỏi là về khả năng mở rộng, mà chúng tôi có thể định nghĩa là "Thêm nhiều phần cứng giúp hệ thống hoạt động nhanh hơn", đây là những vấn đề ít nhất của bạn. Để giải quyết lần lượt từng người

Quảng cáo 1) Nếu bạn muốn quy mô, thì bạn có thể đủ khả năng để mua I / O. Ngay cả một ổ SSD Samsung / Intel 512GB giá rẻ (với một vài USD / GB) sẽ giúp bạn kiếm được hơn 100 nghìn IOPS. Bạn sẽ không tiêu thụ bất cứ lúc nào sớm trên hệ thống 2 ổ cắm. Và nếu bạn gặp phải điều đó, hãy mua thêm một cái nữa và bạn đã được thiết lập

Quảng cáo 2) Nếu bạn xóa trong bảng của mình, bạn sẽ có một nửa trang đầy đủ. Và ngay cả khi bạn không, bộ nhớ là rẻ và cho tất cả trừ các hệ thống OLTP lớn nhất - dữ liệu nóng sẽ phù hợp ở đó. Tìm cách để đóng gói nhiều dữ liệu hơn vào các trang là tối ưu hóa phụ khi bạn đang tìm kiếm tỷ lệ.

Quảng cáo 3) Một bảng được xây dựng từ việc phân chia trang thường xuyên, dữ liệu bị phân mảnh cao thực hiện I / O ngẫu nhiên với tốc độ chính xác như một bảng được điền tuần tự

Liên quan đến việc tham gia, có hai loại tham gia chính mà bạn có thể thấy trong OLTP như khối lượng công việc: Hash và vòng lặp. Hãy nhìn lần lượt từng cái:

Tham gia băm: Một tham gia băm giả định rằng bảng nhỏ được quét và bảng lớn hơn thường được tìm kiếm. Các bảng nhỏ rất có khả năng nằm trong bộ nhớ, vì vậy I / O không phải là mối quan tâm của bạn ở đây. Chúng tôi đã chạm vào thực tế rằng các tìm kiếm có cùng chi phí trong chỉ mục phân mảnh như trong một chỉ mục không phân mảnh

Vòng lặp tham gia: Bảng bên ngoài sẽ được tìm kiếm. Cùng chi phí

Bạn cũng có thể có nhiều quá trình quét bảng xấu đang diễn ra - nhưng sau đó GUID không phải là mối quan tâm của bạn, lập chỉ mục phù hợp là.

Bây giờ, bạn có thể có một số lần quét phạm vi hợp pháp đang diễn ra (đặc biệt là khi tham gia khóa ngoại) và trong trường hợp này, dữ liệu bị phân mảnh ít bị "đóng gói" hơn so với dữ liệu không bị phân mảnh. Nhưng hãy để chúng tôi xem xét những gì tham gia mà bạn có thể sẽ thấy trong dữ liệu 3NF được lập chỉ mục tốt là:

  1. Tham gia từ một bảng có tham chiếu khóa ngoài đến khóa chính của bảng mà nó tham chiếu

  2. Cách khác xung quanh

Quảng cáo 1) Trong trường hợp này, bạn sẽ tìm kiếm một khóa chính - nối n đến 1. Phân mảnh hoặc không, cùng một chi phí (một tìm kiếm)

Quảng cáo 2) Trong trường hợp này, bạn đang tham gia cùng một khóa, nhưng có thể truy xuất nhiều hơn một hàng (phạm vi tìm kiếm). Tham gia trong trường hợp này là 1 đến n. Tuy nhiên, bảng nước ngoài bạn tìm kiếm, bạn đang tìm kiếm khóa SAME, có khả năng giống như trên cùng một trang trong một chỉ mục bị phân mảnh như trên một bảng không bị phân mảnh.

Hãy xem xét các khóa ngoại trong một thời điểm. Ngay cả khi bạn có tuần tự "hoàn hảo" đã đặt các khóa chính của chúng tôi - mọi thứ chỉ vào khóa đó vẫn sẽ không tuần tự.

Tất nhiên, bạn có thể đang chạy trên một máy ảo trong một số SAN ở một số ngân hàng rẻ tiền và có quy trình cao. Sau đó tất cả lời khuyên này sẽ bị mất. Nhưng nếu đó là thế giới của bạn, khả năng mở rộng có thể không phải là thứ bạn đang tìm kiếm - bạn đang tìm kiếm hiệu suất và tốc độ / chi phí cao - đó là cả hai điều khác nhau.


1
Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Paul White 9

5

Thomas: một số điểm của bạn hoàn toàn có ý nghĩa và tôi đồng ý với tất cả chúng. Nếu bạn đang sử dụng SSD, sự cân bằng của những gì bạn tối ưu hóa sẽ thay đổi. Random vs sequential không giống như thảo luận với đĩa quay.

Tôi đặc biệt đồng ý rằng việc xem DB thuần túy là sai lầm khủng khiếp. Làm ứng dụng của bạn chậm chạp và không thể leo để cải thiện chỉ việc thực hiện DB có thể được khá nhầm lẫn.

Vấn đề lớn với IDENTITY (hoặc chuỗi hoặc bất cứ thứ gì được tạo ra trong DB) là nó chậm khủng khiếp vì nó yêu cầu một chuyến đi khứ hồi đến DB để tạo khóa và điều này tự động tạo ra một nút cổ chai trong DB của bạn, nó buộc các ứng dụng phải thực hiện cuộc gọi DB để bắt đầu sử dụng phím. Tạo GUID giải quyết điều này bằng cách sử dụng ứng dụng để tạo khóa, nó được đảm bảo là duy nhất trên toàn cầu (theo định nghĩa) và do đó, các lớp ứng dụng có thể sử dụng nó để vượt qua bản ghi xung quanh TRƯỚC KHI phát sinh một chuyến đi khứ hồi DB.

Nhưng tôi có xu hướng sử dụng một giải pháp thay thế cho GUID Sở thích cá nhân của tôi cho một kiểu dữ liệu ở đây là một BIGINT duy nhất trên toàn cầu được tạo bởi ứng dụng. Làm thế nào để một người đi về làm điều này? Trong ví dụ tầm thường nhất, bạn thêm một hàm nhỏ, RẤT nhẹ vào ứng dụng của mình để băm GUID. Giả sử chức năng băm của bạn nhanh và tương đối nhanh (xem CityHash từ Google để biết ví dụ: http://google-opensource.blogspot.in/2011/04/int sinhing-cityhash.html - đảm bảo bạn có được tất cả các bước biên dịch đúng, hoặc biến thể FNV1a của http://tools.ietf.org/html/draft-eastlake-fnv-03 cho mã đơn giản) điều này mang lại cho bạn lợi ích của cả hai định danh duy nhất của ứng dụng và giá trị khóa 64 bit mà CPU hoạt động tốt hơn .

Có nhiều cách khác để tạo ra BIGINT, và trong cả hai thuật toán này đều có cơ hội va chạm băm - đọc và đưa ra quyết định có ý thức.


2
Tôi đề nghị bạn chỉnh sửa câu trả lời của mình dưới dạng câu trả lời cho câu hỏi của OP chứ không phải (như bây giờ) là câu trả lời cho câu trả lời của Thomas. Bạn vẫn có thể nêu bật sự khác biệt giữa Thomas (, của MikeFal) và đề xuất của bạn.
ypercubeᵀᴹ

2
Hãy giải đáp câu trả lời của bạn cho câu hỏi. Nếu bạn không xóa chúng tôi.
JNK

2
Cảm ơn các ý kiến ​​Mark. Khi bạn chỉnh sửa câu trả lời của mình (mà tôi nghĩ cung cấp một số ngữ cảnh rất tốt) tôi sẽ thay đổi một điều: IDENTITY không yêu cầu một chuyến đi khứ hồi bổ sung đến máy chủ nếu bạn cẩn thận với INSERT. Bạn luôn có thể trả về SCOPE_IDENTITY () trong đợt gọi INSERT ..
Thomas Kejser

1
Về "nó chậm kinh khủng vì nó yêu cầu một chuyến đi khứ hồi đến DB để tạo khóa" - bạn có thể lấy bao nhiêu tùy ý trong một chuyến đi khứ hồi.
AK

Về "bạn có thể lấy bao nhiêu tùy ý trong một chuyến đi khứ hồi" - Bạn không thể làm điều này với các cột IDENTITY hoặc bất kỳ phương pháp nào khác mà về cơ bản bạn đang sử dụng DEFAULT ở cấp cơ sở dữ liệu.
Avi Cherry
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.