Chỉ số không bao gồm nhanh hơn chỉ số cụm?


9

Cả hai bảng có cùng cấu trúc và 19972 hàng trong mỗi bảng. để thực hành lập chỉ mục, tôi đã tạo cả hai bảng có cùng cấu trúc và được tạo

clustered index on persontb(BusinessEntityID)

nonclustered index on Persontb_NC(BusinessEntityId)

và cấu trúc bảng

BusinessEntityID int
FirstName varchar(100)
LastName  varchar(100)                                                                                                                       

 -- Nonclusted key on businessentityid takes 38%
SELECT  BusinessEntityId from Persontb_NC
WHERE businessentityid BETWEEN 400 AND 4000

-- CLustered key businessentityid takes 62%
SELECT BusinessEntityId  from persontb 
WHERE businessentityid BETWEEN 400 AND 4000

nhập mô tả hình ảnh ở đây

Tại sao chỉ số cụm chiếm 62% và không phân cụm 38%?


1
Tại sao bỏ phiếu cho đóng?

Câu trả lời:


10

Có, chỉ mục được phân cụm có ít hàng trên mỗi trang hơn chỉ mục không được phân cụm vì các trang lá của chỉ mục được phân cụm phải lưu trữ các giá trị cho hai cột khác ( FirstNameLastName).

Các trang lá của NCI chỉ lưu trữ các BusinessEntityIdgiá trị và bộ định vị hàng (RID nếu bảng là một đống hoặc khóa CI khác).

Vì vậy, chi phí ước tính phản ánh số lượng đọc và yêu cầu IO lớn hơn.

Nếu bạn đã tuyên bố NCI là

nonclustered index on Persontb_NC(BusinessEntityId) INCLUDE (FirstName, LastName)

sau đó nó sẽ tương tự như chỉ số cụm.


5

Chỉ mục cụm không chỉ chứa dữ liệu từ chỉ mục cột mà còn dữ liệu từ tất cả các cột khác. (Chỉ có thể có một chỉ mục được nhóm trên mỗi bảng)

Chỉ mục không chứa chỉ chứa dữ liệu từ (các) cột được lập chỉ mục và một con trỏ row_id đến nơi còn lại của dữ liệu.

Do đó, chỉ mục không bao gồm cụ thể này nhẹ hơn và cần đọc ít hơn để quét / tìm kiếm thông qua nó và truy vấn cụ thể này sẽ hoạt động nhanh hơn.

Tuy nhiên, bạn đã cố gắng truy xuất FirstName và LastName chưa, nó sẽ khác và chỉ mục được nhóm sẽ hoạt động tốt hơn.


2

Tỷ lệ phần trăm giữa các kế hoạch truy vấn là vô nghĩa để so sánh hoàn toàn. Bạn phải điểm chuẩn các truy vấn để có một so sánh hợp lệ. Ngoài ra, số lượng hàng nhỏ có xu hướng che giấu sự khác biệt về hiệu suất giữa các chiến lược lập chỉ mục. Bằng cách tăng số lượng hàng lên 10 triệu, bạn có thể có được một bức tranh rõ ràng hơn về sự khác biệt hiệu suất.

Có một tập lệnh mẫu tạo 3 bảng, hai bảng của bạn ở trên và một bảng thứ ba có cả chỉ mục được phân cụm và không phân cụm.

USE [tempdb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[t1](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [c1] [varchar](200) NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[t2](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [c1] [varchar](200) NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[t3](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [c1] [varchar](200) NULL
) ON [PRIMARY]

GO

CREATE CLUSTERED INDEX CIX_t1 ON t1(id)

CREATE NONCLUSTERED INDEX IX_t2 ON t2(id)

CREATE CLUSTERED INDEX CIX_t3 ON t3(id)
CREATE NONCLUSTERED INDEX IX_t3 ON t3(id)

Điền vào các bảng với 10 triệu hàng

DECLARE @i INT
DECLARE @j int
DECLARE @t DATETIME
SET NOCOUNT ON
SET @t = CURRENT_TIMESTAMP
SET @i = 0
WHILE @i < 10000000
BEGIN
--populate with strings with a length between 100 and 200 
INSERT INTO t1 (c1) VALUES (REPLICATE('x', 101+ CAST(RAND(@i) * 100 AS INT)))
SET @i = @i + 1
END

PRINT 'Time to populate t1: '+ CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR(10)) + ' ms'
SET @t = CURRENT_TIMESTAMP


SET @i = 0
WHILE @i < 10000000
BEGIN
--populate with strings with a length between 100 and 200 
INSERT INTO t2 (c1) VALUES (REPLICATE('x', 101+ CAST(RAND(@i) * 100 AS INT)))
SET @i = @i + 1
END

PRINT 'Time to populate t3: '+ CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR(10)) + ' ms'
SET @t = CURRENT_TIMESTAMP

SET @i = 0
WHILE @i < 10000000
BEGIN
--populate with strings with a length between 100 and 200 
INSERT INTO t3 (c1) VALUES (REPLICATE('x', 101+ CAST(RAND(@i) * 100 AS INT)))
SET @i = @i + 1
END

PRINT 'Time to populate t3: '+ CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR(10)) + ' ms'

Chúng ta có thể sử dụng sys.dm_db_index_physical_stats để xem kích thước trên đĩa của các chỉ mục.

SELECT  OBJECT_NAME(OBJECT_ID) table_name, index_id, index_type_desc, 
record_count, page_count, page_count / 128.0 size_in_mb, avg_record_size_in_bytes
FROM    sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('t1'), NULL, NULL, 'detailed')
WHERE   index_level = 0 
UNION ALL
SELECT  OBJECT_NAME(OBJECT_ID) table_name, index_id, index_type_desc, 
record_count, page_count, page_count / 128.0 size_in_mb, avg_record_size_in_bytes
FROM    sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('t2'), NULL, NULL, 'detailed')
WHERE   index_level = 0 
UNION ALL
SELECT  OBJECT_NAME(OBJECT_ID) table_name, index_id, index_type_desc, 
record_count, page_count, page_count / 128.0 size_in_mb, avg_record_size_in_bytes
FROM    sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('t3'), NULL, NULL, 'detailed')
WHERE   index_level = 0 

Và kết quả:

table_name  index_id    page_count  size_in_mb  avg_record_size_in_bytes    index_type_desc
t1  1   211698  1653.890625 167.543 CLUSTERED INDEX
t2  0   209163  1634.085937 165.543 HEAP
t2  2   22272   174.000000  16  NONCLUSTERED INDEX
t3  1   211698  1653.890625 167.543 CLUSTERED INDEX
t3  2   12361   96.570312   8   NONCLUSTERED INDEX

Chỉ số cụm của T1 có kích thước khoảng 1,6 GB. Chỉ số không phân cụm của T2 là 170 MB (tiết kiệm 90% trong IO). Chỉ số không phân cụm của T3 là 97 MB, hoặc ít hơn khoảng 95% IO so với T1.

Vì vậy, dựa trên yêu cầu IO, kế hoạch truy vấn ban đầu nên có nhiều hơn 10% / 90%, chứ không phải 38% / 62%. Ngoài ra, do chỉ mục không được phân cụm có khả năng phù hợp hoàn toàn trong bộ nhớ, sự khác biệt có thể vẫn lớn hơn, vì IO đĩa rất đắt.


1
Đó là một bước nhảy vọt để suy ra rằng 10%/90%con số của bạn chính xác hơn 38%/62%. Các chuỗi có độ dài từ 100 đến 200 chắc chắn sẽ là sự đánh giá quá cao về yêu cầu không gian cho cặp tên / họ, do đó bạn sẽ có mật độ trang thấp hơn OP. Khi tôi cố gắng chống lại dữ liệu mẫu của bạn, chi phí ước tính sẽ hiển thị là 87% / 13% .
Martin Smith

1
SQL Server không đề cập đến data_pagestrong sys.allocation_units. Bạn có thể thấy điều này từ CREATE TABLE T1(C INT);CREATE TABLE T2(C INT);UPDATE STATISTICS T1 WITH PAGECOUNT = 1;UPDATE STATISTICS T2 WITH PAGECOUNT = 100đó so sánh các chi phí ước tínhSELECT * FROM T1;SELECT * FROM T2;
Martin Smith

Xin vui lòng đọc lại câu đầu tiên trong câu trả lời của tôi. So sánh chi phí trực tiếp là vô nghĩa. Đối với sự khác biệt về hiệu suất giữa các truy vấn của OP, một ước tính tốt hơn có thể được rút ra theo kinh nghiệm bằng cách tính toán giảm kích thước của các chỉ mục (và do đó là số lượng IO), chứ không phải bằng chi phí từ trình tối ưu hóa.
StrayCatDBA

1
Nói chung là có nhưng trong trường hợp này, lý do tại sao trình tối ưu hóa truy vấn tốn chi phí cho chỉ mục được phân cụm nhiều hơn chỉ mục không được phân cụm (chủ đề của câu hỏi này) chính xác là do số lượng trang khác nhau.
Martin Smith

1
Theo http://www.qdpma.com/ppt/CostFormulas2.ppt Công thức sử dụng để chi phí một Index Scan hoặc Index Seek mà không cần tra cứu là (phụ thuộc phiên bản) IO (0,003125 + 0,00074074 mỗi trang) và CPU (0,0001581 0,0000011 + mỗi hàng). Các chi phí và hàng cố định bằng nhau cho CI và NCI nên biến duy nhất là các trang.
Martin Smith
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.