Tôi đã viết nhiều lần về cách sử dụng con trỏ và cách, trong hầu hết các trường hợp, viết lại con trỏ của bạn bằng cách sử dụng logic dựa trên bộ sẽ hiệu quả hơn.
Tuy nhiên, tôi thực tế.
Tôi biết rằng có những trường hợp con trỏ là "bắt buộc" - bạn cần gọi một thủ tục được lưu trữ khác hoặc gửi e-mail cho mọi hàng, bạn đang thực hiện các tác vụ bảo trì đối với từng cơ sở dữ liệu hoặc bạn đang chạy một tác vụ đơn giản không đáng để đầu tư thời gian để chuyển đổi sang dựa trên thiết lập.
Bạn (có thể) làm việc đó hôm nay như thế nào
Bất kể lý do bạn vẫn đang sử dụng con trỏ là gì, ít nhất bạn nên cẩn thận để không sử dụng các tùy chọn mặc định khá đắt tiền. Hầu hết mọi người bắt đầu con trỏ của họ như thế này:
DECLARE c CURSOR FOR SELECT whatever FROM ...
Bây giờ một lần nữa, đối với các nhiệm vụ đặc biệt, một lần, điều này có thể tốt. Nhưng có…
Các cách khác để thực hiện
Tôi muốn chạy một số kiểm tra bằng cách sử dụng các giá trị mặc định và so sánh chúng với các tùy chọn con trỏ khác nhau, chẳng hạn như LOCAL
, STATIC
, READ_ONLY
và FAST_FORWARD
. (Có rất nhiều tùy chọn, nhưng đây là những tùy chọn được sử dụng phổ biến nhất vì chúng có thể áp dụng cho các loại thao tác con trỏ phổ biến nhất mà mọi người sử dụng.) Tôi không chỉ muốn kiểm tra tốc độ thô của một vài kết hợp khác nhau, mà cũng là tác động đến tempdb và bộ nhớ, cả sau khi khởi động lại dịch vụ nguội và với bộ nhớ cache ấm.
Truy vấn mà tôi quyết định cung cấp cho con trỏ là một truy vấn rất đơn giản đối với sys.objects
, trong cơ sở dữ liệu mẫu AdventureWorks2012. Điều này trả về 318.500 hàng trên hệ thống của tôi (một hệ thống 2 lõi rất khiêm tốn với RAM 4GB):
SELECT c1.[object_id] FROM sys.objects AS c1 CROSS JOIN (SELECT TOP 500 name FROM sys.objects) AS c2;
Sau đó, tôi gói truy vấn này trong một con trỏ với các tùy chọn khác nhau (bao gồm cả mặc định) và chạy một số kiểm tra, đo Tổng bộ nhớ máy chủ, các trang được phân bổ cho tempdb (theo sys.dm_db_task_space_usage
và / hoặc sys.dm_db_session_space_usage
) và tổng thời lượng. Tôi cũng cố gắng quan sát sự tranh chấp tempdb bằng cách sử dụng các kịch bản của Glenn Berry và Robert Davis, nhưng trên hệ thống bảng xếp hạng của tôi, tôi không thể phát hiện ra bất kỳ sự tranh chấp nào. Tất nhiên, tôi cũng đang sử dụng SSD và hoàn toàn không có gì khác đang chạy trên hệ thống, vì vậy đây có thể là những thứ bạn muốn thêm vào các thử nghiệm của riêng mình nếu tempdb có nhiều khả năng là nút cổ chai.
Vì vậy, cuối cùng các truy vấn trông giống như thế này, với các truy vấn chẩn đoán được đưa vào ở những điểm thích hợp:
DECLARE @i INT = 1; DECLARE c CURSOR -- LOCAL -- LOCAL STATIC -- LOCAL FAST_FORWARD -- LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT c1.[object_id] FROM sys.objects AS c1 CROSS JOIN (SELECT TOP 500 name FROM sys.objects) AS c2 ORDER BY c1.[object_id]; OPEN c; FETCH c INTO @i; WHILE (@@FETCH_STATUS = 0) BEGIN SET @i += 1; -- meaningless operation FETCH c INTO @i; END CLOSE c; DEALLOCATE c;
Kết quả
Thời lượng
Biện pháp khá được cho là quan trọng và phổ biến nhất là "nó mất bao lâu?" Chà, mất gần năm lần để chạy con trỏ với các tùy chọn mặc định (hoặc chỉ với LOCAL
được chỉ định), so với việc chỉ định STATIC
hoặc FAST_FORWARD
:
Bộ nhớ
Tôi cũng muốn đo bộ nhớ bổ sung mà SQL Server sẽ yêu cầu khi thực hiện từng loại con trỏ. Vì vậy, tôi chỉ cần khởi động lại trước mỗi lần kiểm tra bộ đệm lạnh, đo bộ đếm hiệu suất Total Server Memory (KB)
trước và sau mỗi lần kiểm tra. Sự kết hợp tốt nhất ở đây là LOCAL FAST_FORWARD
:
sử dụng tempdb
Kết quả này khiến tôi rất ngạc nhiên. Vì định nghĩa của con trỏ tĩnh có nghĩa là nó sao chép toàn bộ kết quả sang tempdb và nó thực sự được thể hiện trong sys.dm_exec_cursors
dưới dạng SNAPSHOT
, Tôi mong đợi lượt truy cập trên các trang tempdb sẽ cao hơn với tất cả các biến thể tĩnh của con trỏ. Đây không phải là trường hợp; một lần nữa, chúng tôi thấy mức truy cập gần gấp 5 lần đối với việc sử dụng tempdb với con trỏ mặc định và con trỏ chỉ có LOCAL
chỉ định:
Kết luận
Trong nhiều năm, tôi đã nhấn mạnh rằng tùy chọn sau phải luôn được chỉ định cho các con trỏ của bạn:
LOCAL STATIC READ_ONLY FORWARD_ONLY
Kể từ thời điểm này, cho đến khi tôi có cơ hội kiểm tra các hoán vị khác hoặc tìm thấy bất kỳ trường hợp nào không phải là lựa chọn nhanh nhất, tôi sẽ đề xuất như sau:
LOCAL FAST_FORWARD
(Ngoài ra, tôi cũng chạy thử nghiệm bỏ qua LOCAL
tùy chọn và sự khác biệt là không đáng kể.)
Điều đó nói rằng, điều này không nhất thiết đúng với * tất cả * con trỏ. Trong trường hợp này, tôi chỉ nói về con trỏ nơi bạn chỉ đọc dữ liệu từ con trỏ, chỉ theo hướng về phía trước và bạn không cập nhật dữ liệu cơ bản (bằng khóa hoặc sử dụng WHERE CURRENT OF
). Đó là những bài kiểm tra cho một ngày khác.