Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Khóa và hiệu suất

Loại và số lượng khóa có được và phát hành trong quá trình thực thi truy vấn có thể có tác động đáng ngạc nhiên đến hiệu suất (khi sử dụng mức cách ly khóa như cam kết đọc mặc định) ngay cả khi không có chờ đợi hoặc chặn xảy ra. Không có thông tin nào trong kế hoạch thực thi để chỉ ra số lượng hoạt động khóa trong khi thực thi, điều này khiến việc phát hiện khó hơn khi khóa quá nhiều gây ra sự cố hiệu suất.

Để khám phá một số hành vi khóa ít được biết đến trong SQL Server, tôi sẽ sử dụng lại các truy vấn và dữ liệu mẫu từ bài đăng cuối cùng của tôi về tính toán trung bình. Trong bài đăng đó, tôi đã đề cập rằng OFFSET giải pháp trung bình được nhóm lại cần một PAGLOCK rõ ràng khóa gợi ý để tránh bị mất nặng vào con trỏ lồng nhau giải pháp, vì vậy chúng ta hãy bắt đầu bằng cách xem xét chi tiết lý do cho điều đó.

Giải pháp trung bình được nhóm OFFSET

Thử nghiệm trung vị được nhóm đã sử dụng lại dữ liệu mẫu từ bài báo trước đó của Aaron Bertrand. Tập lệnh dưới đây tạo lại thiết lập hàng triệu hàng này, bao gồm mười nghìn bản ghi cho mỗi trăm người bán hàng tưởng tượng:

CREATE TABLE dbo.Sales
(
    SalesPerson integer NOT NULL, 
    Amount integer NOT NULL
);
 
WITH X AS
(
    SELECT TOP (100) 
        V.number
    FROM master.dbo.spt_values AS V
    GROUP BY 
        V.number
)
INSERT dbo.Sales WITH (TABLOCKX) 
(
    SalesPerson, 
    Amount
)
SELECT 
    X.number,
    ABS(CHECKSUM(NEWID())) % 99
FROM X 
CROSS JOIN X AS X2 
CROSS JOIN X AS X3;
 
CREATE CLUSTERED INDEX cx 
ON dbo.Sales
    (SalesPerson, Amount);

SQL Server 2012 (và mới hơn) OFFSET giải pháp do Peter Larsson tạo ra như sau (không có bất kỳ gợi ý khóa nào):

DECLARE @s datetime2 = SYSUTCDATETIME();
 
DECLARE @Result AS table 
(
    SalesPerson integer PRIMARY KEY, 
    Median float NOT NULL
);
 
INSERT @Result
    (SalesPerson, Median)
SELECT	
    d.SalesPerson, 
    w.Median
FROM
(
    SELECT SalesPerson, COUNT(*) AS y
    FROM dbo.Sales
    GROUP BY SalesPerson
) AS d
CROSS APPLY
(
    SELECT AVG(0E + Amount)
    FROM
    (
        SELECT z.Amount
        FROM dbo.Sales AS z
        WHERE z.SalesPerson = d.SalesPerson
        ORDER BY z.Amount
        OFFSET (d.y - 1) / 2 ROWS
        FETCH NEXT 2 - d.y % 2 ROWS ONLY
    ) AS f
) AS w (Median);
 
SELECT Peso = DATEDIFF(MILLISECOND, @s, SYSUTCDATETIME());

Các phần quan trọng của kế hoạch sau khi thực hiện được trình bày dưới đây:

Với tất cả dữ liệu bắt buộc trong bộ nhớ, truy vấn này thực thi trong 580 mili giây trung bình trên máy tính xách tay của tôi (chạy SQL Server 2014 Gói Dịch vụ 1). Hiệu suất của truy vấn này có thể được cải thiện thành 320 mili giây chỉ bằng cách thêm gợi ý khóa mức độ chi tiết của trang vào bảng Bán hàng trong truy vấn con áp dụng:

DECLARE @s datetime2 = SYSUTCDATETIME();
 
DECLARE @Result AS table 
(
    SalesPerson integer PRIMARY KEY, 
    Median float NOT NULL
);
 
INSERT @Result
    (SalesPerson, Median)
SELECT	
    d.SalesPerson, 
    w.Median
FROM
(
    SELECT SalesPerson, COUNT(*) AS y
    FROM dbo.Sales
    GROUP BY SalesPerson
) AS d
CROSS APPLY
(
    SELECT AVG(0E + Amount)
    FROM
    (
        SELECT z.Amount
        FROM dbo.Sales AS z WITH (PAGLOCK) -- NEW!
        WHERE z.SalesPerson = d.SalesPerson
        ORDER BY z.Amount
        OFFSET (d.y - 1) / 2 ROWS
        FETCH NEXT 2 - d.y % 2 ROWS ONLY
    ) AS f
) AS w (Median);
 
SELECT Peso = DATEDIFF(MILLISECOND, @s, SYSUTCDATETIME());

Kế hoạch thực thi không thay đổi (tất nhiên là ngoài văn bản gợi ý khóa trong showplan XML):

Phân tích khóa trung vị được nhóm

Lời giải thích cho sự cải thiện đáng kể về hiệu suất do PAGLOCK gợi ý là khá đơn giản, ít nhất là ban đầu.

Nếu chúng tôi theo dõi hoạt động khóa theo cách thủ công trong khi truy vấn này thực thi, chúng tôi thấy rằng không có gợi ý về mức độ chi tiết khóa trang, SQL Server thu thập và phát hành hơn nửa triệu khóa cấp hàng trong khi tìm kiếm chỉ mục được phân nhóm. Không có sự ngăn chặn để đổ lỗi; chỉ cần có được và giải phóng nhiều khóa này sẽ thêm chi phí đáng kể cho việc thực thi truy vấn này. Yêu cầu khóa cấp độ trang làm giảm đáng kể hoạt động khóa, dẫn đến hiệu suất được cải thiện nhiều.

Vấn đề hiệu suất khóa của kế hoạch cụ thể này được giới hạn trong tìm kiếm chỉ mục theo nhóm trong kế hoạch ở trên. Quá trình quét toàn bộ chỉ mục theo nhóm (được sử dụng để tính toán số lượng hàng hiện có cho mỗi người bán hàng) sử dụng tự động khóa cấp độ trang. Đây là một thông tin thú vị. Hành vi khóa chi tiết của công cụ SQL Server không được ghi lại trong Books Online ở bất kỳ mức độ nào, nhưng các thành viên khác nhau của nhóm SQL Server đã đưa ra một số nhận xét chung trong nhiều năm, bao gồm thực tế là các bản quét không hạn chế có xu hướng bắt đầu lấy trang khóa, trong khi các hoạt động nhỏ hơn có xu hướng bắt đầu bằng khóa hàng.

Trình tối ưu hóa truy vấn cung cấp một số thông tin cho công cụ lưu trữ, bao gồm ước tính số lượng, gợi ý nội bộ cho mức độ cô lập và mức độ chi tiết khóa, mà các tối ưu hóa nội bộ có thể được áp dụng một cách an toàn, v.v. Một lần nữa, những chi tiết này không được ghi lại trong Sách Trực tuyến. Cuối cùng, bộ máy lưu trữ sử dụng nhiều thông tin khác nhau để quyết định khóa nào được yêu cầu tại thời điểm chạy và mức độ chi tiết của chúng.

Một lưu ý nhỏ và hãy nhớ rằng chúng ta đang nói về một truy vấn thực thi ở mức cách ly giao dịch đã cam kết đã đọc khóa mặc định, hãy lưu ý rằng các khóa hàng được thực hiện mà không có gợi ý về mức độ chi tiết sẽ không chuyển sang khóa bảng trong trường hợp này. Điều này là do hành vi bình thường trong đã cam kết đọc là giải phóng khóa trước đó ngay trước khi có được khóa tiếp theo, có nghĩa là chỉ một khóa hàng chia sẻ duy nhất (với các khóa chia sẻ mục đích cấp cao hơn được liên kết) sẽ được giữ tại bất kỳ thời điểm cụ thể nào. Vì số lượng khóa hàng được giữ đồng thời không bao giờ đạt đến ngưỡng, không có khóa nào được cố gắng leo thang.

Giải pháp trung bình đơn OFFSET

Kiểm tra hiệu suất cho một phép tính trung bình duy nhất sử dụng một bộ dữ liệu mẫu khác, được sao chép lại từ bài báo trước đó của Aaron. Tập lệnh bên dưới tạo một bảng với mười triệu hàng dữ liệu giả ngẫu nhiên:

CREATE TABLE dbo.obj
(
    id  integer NOT NULL IDENTITY(1,1), 
    val integer NOT NULL
);
 
INSERT dbo.obj WITH (TABLOCKX) 
    (val)
SELECT TOP (10000000) 
    AO.[object_id]
FROM sys.all_columns AS AC
CROSS JOIN sys.all_objects AS AO
CROSS JOIN sys.all_objects AS AO2
WHERE AO.[object_id] > 0
ORDER BY 
    AC.[object_id];
 
CREATE UNIQUE CLUSTERED INDEX cx 
ON dbo.obj(val, id);

OFFSET giải pháp là:

DECLARE @Start datetime2 = SYSUTCDATETIME();
 
DECLARE @Count bigint = 10000000
--(
--    SELECT COUNT_BIG(*) 
--    FROM dbo.obj AS O
--);
 
SELECT 
    Median = AVG(1.0 * SQ1.val)
FROM 
(
    SELECT O.val 
    FROM dbo.obj AS O
    ORDER BY O.val
    OFFSET (@Count - 1) / 2 ROWS
    FETCH NEXT 1 + (1 - @Count % 2) ROWS ONLY
) AS SQ1;
 
SELECT Peso = DATEDIFF(MILLISECOND, @Start, SYSUTCDATETIME());

Kế hoạch sau khi thực hiện là:

Truy vấn này thực thi trong 910 mili giây trung bình trên máy thử nghiệm của tôi. Hiệu suất không thay đổi nếu một PAGLOCK gợi ý được thêm vào, nhưng lý do cho điều đó không phải như những gì bạn có thể nghĩ…

Phân tích khóa trung vị đơn

Bạn có thể mong đợi công cụ lưu trữ vẫn chọn các khóa chia sẻ ở cấp độ trang, do quá trình quét chỉ mục theo nhóm, giải thích tại sao lại có PAGLOCK gợi ý không có hiệu lực. Trên thực tế, việc theo dõi các khóa được thực hiện trong khi truy vấn này thực thi cho thấy rằng không có khóa dùng chung (S) nào được thực hiện, ở bất kỳ mức độ chi tiết nào . Các khóa duy nhất được thực hiện là chia sẻ mục đích (IS) ở cấp đối tượng và cấp trang.

Lời giải thích cho hành vi này có hai phần. Điều đầu tiên cần chú ý là Quét chỉ mục theo cụm nằm bên dưới một toán tử Hàng đầu trong kế hoạch thực thi. Điều này có ảnh hưởng quan trọng đến các ước tính về số lượng, như được thể hiện trong kế hoạch trước khi thực hiện (ước tính):

OFFSETFETCH các mệnh đề trong truy vấn tham chiếu một biểu thức và một biến, do đó, trình tối ưu hóa truy vấn đoán số hàng sẽ cần trong thời gian chạy. Dự đoán tiêu chuẩn cho Top là một trăm hàng. Tất nhiên, đây là một dự đoán khủng khiếp, nhưng nó đủ để thuyết phục công cụ lưu trữ khóa ở mức độ chi tiết của hàng thay vì ở cấp độ trang.

Nếu chúng tôi vô hiệu hóa hiệu ứng "mục tiêu hàng" của toán tử Hàng đầu bằng cách sử dụng cờ theo dõi được lập thành văn bản 4138, thì số hàng ước tính khi quét sẽ thay đổi thành mười triệu (vẫn sai, nhưng theo hướng khác). Điều này đủ để thay đổi quyết định về mức độ chi tiết khóa của công cụ lưu trữ, do đó, các khóa được chia sẻ ở cấp độ trang (lưu ý, không phải khóa được chia sẻ có mục đích) được thực hiện:

DECLARE @Start datetime2 = SYSUTCDATETIME();
 
DECLARE @Count bigint = 10000000
--(
--    SELECT COUNT_BIG(*) 
--    FROM dbo.obj AS O
--);
 
SELECT 
    Median = AVG(1.0 * SQ1.val)
FROM 
(
    SELECT O.val 
    FROM dbo.obj AS O
    ORDER BY O.val
    OFFSET (@Count - 1) / 2 ROWS
    FETCH NEXT 1 + (1 - @Count % 2) ROWS ONLY
) AS SQ1
OPTION (QUERYTRACEON 4138); -- NEW!
 
SELECT Peso = DATEDIFF(MILLISECOND, @Start, SYSUTCDATETIME());

Kế hoạch thực hiện ước tính được đưa ra dưới cờ theo dõi 4138 là:

Quay trở lại ví dụ chính, ước tính hàng trăm do mục tiêu hàng được đoán có nghĩa là công cụ lưu trữ chọn khóa ở cấp hàng. Tuy nhiên, chúng tôi chỉ quan sát các khóa chia sẻ mục đích (IS) ở cấp bảng và trang. Các khóa cấp cao hơn này sẽ khá bình thường nếu chúng ta thấy các khóa chia sẻ (S) cấp hàng, vậy chúng đã đi đâu?

Câu trả lời là công cụ lưu trữ chứa một tối ưu hóa khác có thể bỏ qua các khóa chia sẻ cấp hàng trong một số trường hợp nhất định. Khi tối ưu hóa này được áp dụng, các khóa chia sẻ ý định cấp cao hơn vẫn được nhận.

Tóm lại, đối với truy vấn trung bình đơn:

  1. Việc sử dụng một biến và biểu thức trong OFFSET mệnh đề có nghĩa là trình tối ưu hóa đoán số lượng.
  2. Ước tính thấp có nghĩa là bộ máy lưu trữ quyết định chiến lược khóa ở cấp độ hàng.
  3. Tối ưu hóa nội bộ có nghĩa là các khóa S cấp hàng được bỏ qua trong thời gian chạy, chỉ để lại các khóa IS ở cấp trang và đối tượng.

Truy vấn trung bình duy nhất sẽ có cùng một vấn đề về hiệu suất khóa hàng như trung bình được nhóm (do ước tính không chính xác của trình tối ưu hóa truy vấn) nhưng nó được lưu bằng một tối ưu hóa công cụ lưu trữ riêng biệt dẫn đến chỉ thực hiện các khóa trang và bảng được chia sẻ ý định trong thời gian chạy.

Kiểm tra trung vị được nhóm được xem lại

Bạn có thể tự hỏi tại sao Tìm kiếm chỉ mục theo cụm trong thử nghiệm trung vị được nhóm lại không tận dụng tối ưu hóa công cụ lưu trữ giống nhau để bỏ qua các khóa chia sẻ cấp hàng. Tại sao nhiều khóa hàng chia sẻ được sử dụng, làm cho PAGLOCK gợi ý cần thiết?

Câu trả lời ngắn gọn là tối ưu hóa này không khả dụng cho INSERT...SELECT truy vấn. Nếu chúng ta chạy SELECT của riêng nó (tức là không ghi kết quả vào bảng) và không có PAGLOCK gợi ý, tối ưu hóa bỏ qua khóa hàng đã áp dụng:

DECLARE @s datetime2 = SYSUTCDATETIME();
 
--DECLARE @Result AS table 
--(
--    SalesPerson integer PRIMARY KEY, 
--    Median float NOT NULL
--);
 
--INSERT @Result
--    (SalesPerson, Median)
SELECT	
    d.SalesPerson, 
    w.Median
FROM
(
    SELECT SalesPerson, COUNT(*) AS y
    FROM dbo.Sales
    GROUP BY SalesPerson
) AS d
CROSS APPLY
(
    SELECT AVG(0E + Amount)
    FROM
    (
        SELECT z.Amount
        FROM dbo.Sales AS z
        WHERE z.SalesPerson = d.SalesPerson
        ORDER BY z.Amount
        OFFSET (d.y - 1) / 2 ROWS
        FETCH NEXT 2 - d.y % 2 ROWS ONLY
    ) AS f
) AS w (Median);
 
SELECT Peso = DATEDIFF(MILLISECOND, @s, SYSUTCDATETIME());

Chỉ các khóa chia sẻ mục đích (IS) cấp bảng và cấp trang được sử dụng và hiệu suất tăng lên cùng mức như khi chúng tôi sử dụng PAGLOCK gợi ý. Tất nhiên, bạn sẽ không tìm thấy hành vi này trong tài liệu và nó có thể thay đổi bất cứ lúc nào. Tuy nhiên, điều tốt là nên biết.

Ngoài ra, trong trường hợp bạn đang thắc mắc, cờ theo dõi 4138 không ảnh hưởng đến lựa chọn mức độ chi tiết khóa của công cụ lưu trữ trong trường hợp này vì số lượng hàng ước tính khi tìm kiếm quá thấp (mỗi lần lặp áp dụng) ngay cả khi mục tiêu hàng bị vô hiệu hóa.

Trước khi đưa ra kết luận về hiệu suất của một truy vấn, hãy đảm bảo kiểm tra số lượng và loại khóa mà nó đang sử dụng trong quá trình thực thi. Mặc dù SQL Server thường chọn độ chi tiết 'đúng', nhưng đôi khi nó có thể làm sai, đôi khi có những ảnh hưởng đáng kể đến hiệu suất.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL còn lại tham gia

  2. Lỗi T-SQL, cạm bẫy và các phương pháp hay nhất - tham gia

  3. Xác định các vấn đề đặt hàng trong sự kiện mở rộng

  4. Ký hiệu ERD trong mô hình hóa dữ liệu

  5. Cách tìm giá trị trung bình của một cột số trong SQL