Khi một bảng có một chỉ mục được nhóm, chỉ mục là dữ liệu bảng (nếu không, bạn có một bảng loại heap). Việc xây dựng lại chỉ mục được nhóm (thực tế là bất kỳ chỉ mục nào, nhưng không gian sẽ không được tính là "dữ liệu" cho chỉ mục không được phân cụm) sẽ dẫn đến các trang được sử dụng một phần được hợp nhất thành một dạng đầy đủ hơn.
Khi bạn chèn dữ liệu vào một chỉ mục (được nhóm hoặc theo cách khác) trong các trang lá thứ tự chỉ mục được tạo khi cần thiết và bạn sẽ chỉ có một trang một phần: trang cuối cùng. Khi bạn nhập dữ liệu ra khỏi chỉ mục, một trang cần được phân chia để dữ liệu nằm đúng vị trí: bạn kết thúc với hai trang gần đầy một nửa và hàng mới đi vào một trong số đó. Theo thời gian, điều này có thể xảy ra rất nhiều, tiêu tốn một lượng không gian thừa, mặc dù trong một chừng mực nào đó, các phần chèn trong tương lai sẽ lấp đầy một số khoảng trống. Các trang không có lá cũng sẽ thấy một hiệu ứng tương tự, nhưng các trang dữ liệu thực tế có ý nghĩa lớn hơn nhiều về kích thước.
Ngoài ra xóa có thể dẫn đến một phần trang. Nếu bạn xóa tất cả các hàng trong một trang, nó sẽ được tính là "không sử dụng" nhưng nếu nó còn một hoặc nhiều hàng dữ liệu thì nó vẫn được tính là đang sử dụng. Ngay cả khi chỉ có một hàng sử dụng 10 byte trong một trang, trang đó vẫn được tính là 8192 byte trong số lượng không gian được sử dụng. Một lần nữa chèn tương lai có thể lấp đầy một số khoảng cách.
Đối với các hàng có độ dài thay đổi, các bản cập nhật cũng có thể có tác dụng tương tự: vì một hàng trở nên nhỏ hơn, nó có thể để lại không gian trong trang của nó mà sau này không dễ sử dụng lại và nếu một hàng trong một trang gần đầy sẽ phát triển lâu hơn, nó có thể buộc chia trang .
SQL Server không dành thời gian cố gắng bình thường hóa dữ liệu bằng cách sắp xếp lại cách sử dụng các trang, cho đến khi được thông báo rõ ràng như thứ tự xây dựng lại chỉ mục của bạn, vì các bài tập thu gom rác như vậy có thể là một cơn ác mộng hiệu suất.
Tôi nghi ngờ đây là những gì bạn đang thấy, mặc dù tôi nói rằng có đủ không gian được phân bổ cho ~ 2,7 lần số lượng dữ liệu hoàn toàn cần là một trường hợp đặc biệt xấu. Điều này có thể ám chỉ rằng bạn có một cái gì đó ngẫu nhiên là một trong những khóa quan trọng trong chỉ mục (có lẽ là cột UUID) có nghĩa là các hàng mới dường như không được thêm vào theo thứ tự chỉ mục và / hoặc một số lần xóa đáng kể đã xảy ra gần đây.
Ví dụ chia trang
Chèn theo thứ tự chỉ mục với các hàng có độ dài cố định trong đó bốn hàng vừa với một trang:
Start with one empty page:
[__|__|__|__]
Add the first item in index order:
[00|__|__|__]
Add the next three
[00|02|04|06]
Adding the next will result in a new page:
[00|02|04|06] [08|__|__|__]
And so on...
[00|02|04|06] [08|10|12|14] [16|18|__|__]
Bây giờ để thêm các hàng ngoài thứ tự chỉ mục (đây là lý do tại sao tôi chỉ sử dụng các số chẵn ở trên): Thêm 11
sẽ có nghĩa là mở rộng trang thứ hai đó (không thể vì chúng có kích thước cố định), di chuyển mọi thứ trên 11 lên một (quá đắt một chỉ mục lớn) hoặc chia trang như vậy:
[00|02|04|06] [08|10|11|__] [12|14|__|__] [16|18|__|__]
Từ đây, việc thêm 13
và 17
sẽ không dẫn đến sự phân tách vì hiện tại có chỗ trong các trang có liên quan:
[00|02|04|06] [08|10|11|__] [12|13|14|__] [16|17|18|__]
nhưng thêm 03 sẽ:
[00|02|03|__] [04|06|__|__] [08|10|11|__] [12|13|14|__] [16|17|18|__]
Như bạn có thể thấy, sau những thao tác chèn đó, chúng tôi hiện có 5 trang dữ liệu được phân bổ có thể phù hợp với tổng số 20 hàng, nhưng chúng tôi chỉ có 14 hàng ở đó ("lãng phí" 30% dung lượng).
Việc xây dựng lại với các tùy chọn mặc định (xem bên dưới về "hệ số lấp đầy") sẽ dẫn đến:
[00|02|03|04] [06|08|10|11] [12|13|14|16] [17|18|__|__]
lưu một trang trong ví dụ đơn giản này. Thật dễ dàng để xem làm thế nào xóa có thể có tác động tương tự như chèn ngoài chỉ mục.
Giảm nhẹ
Nếu bạn đang mong đợi dữ liệu đến theo thứ tự khá ngẫu nhiên liên quan đến thứ tự chỉ mục, bạn có thể sử dụng FILLFACTOR
tùy chọn khi tạo hoặc xây dựng lại một chỉ mục để báo cho SQL Server thoát khỏi các khoảng trống để sau đó điền vào - giảm phân tách trang trong thời gian dài nhưng chiếm nhiều không gian ban đầu. Tất nhiên nhận sai giá trị này có thể làm cho mọi thứ tồi tệ hơn thay vì làm cho tình hình tốt hơn, vì vậy hãy xử lý cẩn thận.
Việc chia trang, đặc biệt là trên chỉ mục được nhóm, có thể có hàm ý hiệu suất cho việc chèn / cập nhật, do đó FILLFACTOR
đôi khi được điều chỉnh vì lý do đó thay vì vấn đề sử dụng không gian trong cơ sở dữ liệu có nhiều hoạt động ghi (nhưng đối với hầu hết các ứng dụng, trong đó đọc vượt quá ghi theo một số đơn đặt hàng lớn, bạn thường tốt hơn hết là để lại hệ số lấp đầy ở mức 100% trừ các trường hợp cụ thể như nơi bạn có các chỉ mục trên các cột có nội dung ngẫu nhiên hiệu quả).
Tôi giả sử các DB tên lớn khác có một tùy chọn tương tự, nếu bạn cũng cần mức độ kiểm soát này.
Cập nhật
Về ALTER INDEX
câu lệnh được thêm vào câu hỏi sau khi tôi bắt đầu gõ ở trên: Tôi giả sử rằng các tùy chọn giống như khi chỉ mục được xây dựng lần đầu (hoặc được xây dựng lại lần cuối) nhưng nếu không thì tùy chọn nén có thể rất quan trọng nếu được thêm vào thời gian xung quanh Cũng trong tuyên bố đó, fillfactor được đặt thành 85% chứ không phải 100%, vì vậy mỗi trang lá sẽ trống ~ 15% ngay sau khi xây dựng lại.