Trong thế giới SQL Server, có hai kiểu người:những người thích tất cả các đối tượng của họ được đặt tiền tố và những người không thích. Nhóm trước đây được chia thành hai loại:những người đặt trước các thủ tục được lưu trữ bằng sp_
và những người chọn các tiền tố khác (chẳng hạn như usp_
hoặc proc_
). Một khuyến nghị lâu đời là tránh sp_
tiền tố, cả vì lý do hiệu suất và để tránh sự mơ hồ hoặc va chạm nếu bạn tình cờ chọn một tên đã tồn tại trong danh mục hệ thống. Xung đột chắc chắn vẫn là một vấn đề, nhưng giả sử bạn đã kiểm tra tên đối tượng của mình, thì đó có phải là vấn đề về hiệu suất không?
TL; Phiên bản DR:CÓ.
Tiền tố sp_ vẫn là không. Nhưng trong bài đăng này, tôi sẽ giải thích lý do tại sao, cách SQL Server 2012 có thể khiến bạn tin rằng lời khuyên cảnh báo này không còn áp dụng nữa và một số tác dụng phụ tiềm ẩn khác của việc chọn quy ước đặt tên này.
Vấn đề với sp_ là gì?
sp_
tiền tố không có nghĩa như bạn nghĩ:hầu hết mọi người nghĩ sp
là viết tắt của "thủ tục được lưu trữ" trong khi thực tế nó có nghĩa là "đặc biệt." Các thủ tục đã lưu trữ (cũng như các bảng và dạng xem) được lưu trữ trong bản gốc với một sp_
tiền tố có thể truy cập từ bất kỳ cơ sở dữ liệu nào mà không có tham chiếu thích hợp (giả sử phiên bản cục bộ không tồn tại). Nếu quy trình được đánh dấu là đối tượng hệ thống (sử dụng sp_MS_marksystemobject
(một thủ tục hệ thống không có tài liệu và không được hỗ trợ đặt is_ms_shipped
đến 1), sau đó thủ tục trong tổng thể sẽ thực thi trong ngữ cảnh của cơ sở dữ liệu đang gọi. Hãy xem một ví dụ đơn giản:
CREATE DATABASE sp_test; GO USE sp_test; GO CREATE TABLE dbo.foo(id INT); GO USE master; GO CREATE PROCEDURE dbo.sp_checktable AS SELECT DB_NAME(), name FROM sys.tables WHERE name = N'foo'; GO USE sp_test; GO EXEC dbo.sp_checktable; -- runs but returns 0 results GO EXEC master..sp_MS_marksystemobject N'dbo.sp_checktable'; GO EXEC dbo.sp_checktable; -- runs and returns results GO
Kết quả:
(0 row(s) affected) sp_test foo (1 row(s) affected)
Vấn đề về hiệu suất xuất phát từ thực tế là bản gốc có thể được kiểm tra đối với một thủ tục được lưu trữ tương đương, tùy thuộc vào việc có phiên bản cục bộ của thủ tục hay không và trên thực tế có một đối tượng tương đương trong bản gốc hay không. Điều này có thể dẫn đến chi phí siêu dữ liệu bổ sung cũng như thêm SP:CacheMiss
biến cố. Câu hỏi đặt ra là liệu chi phí này có hữu hình hay không.
Vì vậy, hãy xem xét một thủ tục rất đơn giản trong cơ sở dữ liệu thử nghiệm:
CREATE DATABASE sp_prefix; GO USE sp_prefix; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Và các thủ tục tương đương trong tổng thể:
USE master; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'master', DB_NAME(); END GO EXEC sp_MS_marksystemobject N'sp_something';
CacheMiss:Sự thật hay hư cấu?
Nếu chúng tôi chạy kiểm tra nhanh từ cơ sở dữ liệu thử nghiệm của mình, chúng tôi thấy rằng việc thực thi các thủ tục được lưu trữ này sẽ không bao giờ thực sự gọi các phiên bản từ cái chính, bất kể chúng tôi có đủ điều kiện cơ sở dữ liệu hoặc lược đồ đúng thủ tục hay không (một quan niệm sai lầm phổ biến) hoặc nếu chúng tôi đánh dấu phiên bản chính như một đối tượng hệ thống:
USE sp_prefix; GO EXEC sp_prefix.dbo.sp_something; GO EXEC dbo.sp_something; GO EXEC sp_something;
Kết quả:
sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix
Cũng hãy chạy Quick Trace SP:CacheMiss
nào không sự kiện:
Chúng tôi thấy CacheMiss
các sự kiện cho lô đặc biệt gọi thủ tục được lưu trữ (vì SQL Server thường sẽ không bận tâm vào bộ nhớ đệm một lô chủ yếu bao gồm các lệnh gọi thủ tục), nhưng không phải cho chính thủ tục được lưu trữ. Cả có và không có sp_something
thủ tục tồn tại trong chính (và khi nó tồn tại, cả khi có và không có nó được đánh dấu là đối tượng hệ thống), các lệnh gọi đến sp_something
trong cơ sở dữ liệu người dùng không bao giờ "vô tình" gọi thủ tục trong chính và không bao giờ tạo bất kỳ CacheMiss
nào sự kiện cho thủ tục.
Điều này xảy ra trên SQL Server 2012. Tôi đã lặp lại các thử nghiệm tương tự ở trên trên SQL Server 2008 R2 và nhận thấy kết quả hơi khác:
Vì vậy, trên SQL Server 2008 R2, chúng tôi thấy một CacheMiss
bổ sung sự kiện không xảy ra trong SQL Server 2012. Điều này xảy ra trong tất cả các tình huống (không có đối tượng chính tương đương, một đối tượng trong cái chính được đánh dấu là đối tượng hệ thống và một đối tượng trong cái chính không được đánh dấu là đối tượng hệ thống). Ngay lập tức tôi tò mò liệu sự kiện bổ sung này sẽ có tác động đáng chú ý nào đến hiệu suất hay không.
Vấn đề về hiệu suất:Sự thật hay hư cấu?
Tôi đã thực hiện một quy trình bổ sung mà không có sp_
tiền tố để so sánh hiệu suất thô, CacheMiss
sang một bên:
USE sp_prefix; GO CREATE PROCEDURE dbo.proc_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Vì vậy, sự khác biệt duy nhất giữa sp_something
và proc_something
. Sau đó, tôi đã tạo các thủ tục trình bao bọc để thực thi chúng 1000 lần mỗi thủ tục, sử dụng EXEC sp_prefix.dbo.<procname>
, EXEC dbo.<procname>
và EXEC <procname>
cú pháp, với các thủ tục được lưu trữ tương đương ở chế độ chính và được đánh dấu là đối tượng hệ thống, đang ở trong chế độ chính nhưng không được đánh dấu là đối tượng hệ thống và hoàn toàn không tồn tại trong chế độ chính.
USE sp_prefix; GO CREATE PROCEDURE dbo.wrap_sp_3part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC sp_prefix.dbo.sp_something; SET @i += 1; END END GO CREATE PROCEDURE dbo.wrap_sp_2part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC dbo.sp_something; SET @i += 1; END END GO CREATE PROCEDURE dbo.wrap_sp_1part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC sp_something; SET @i += 1; END END GO -- repeat for proc_something
Đo thời gian chạy của mỗi thủ tục trình bao bọc bằng SQL Sentry Plan Explorer, kết quả cho thấy rằng bằng cách sử dụng sp_
tiền tố có tác động đáng kể đến thời lượng trung bình trong hầu hết các trường hợp (và chắc chắn là trung bình):
Chúng tôi cũng thấy rằng hiệu suất của SQL Server 2012 có xu hướng tốt hơn nhiều so với hiệu suất trên SQL Sevrer 2008 R2 - không có biến nào khác khác. Cả hai phiên bản đều nằm trên cùng một máy chủ và không bị ảnh hưởng bởi bộ nhớ hay áp lực nào khác. Đây có thể là sự kết hợp của CacheMiss
bổ sung sự kiện và những cải tiến minh bạch mà bạn nhận được từ các cải tiến được thực hiện cho công cụ cơ sở dữ liệu giữa các phiên bản.
Một tác dụng phụ khác:Mơ hồ
Nếu bạn tạo một thủ tục được lưu trữ tham chiếu đến một đối tượng bạn đã tạo, hãy nói dbo.sp_helptext
và bạn đã không nhận ra (hoặc không quan tâm) rằng tên này va chạm với tên thủ tục hệ thống, sau đó có thể có sự mơ hồ khi ai đó đang xem xét thủ tục đã lưu trữ của bạn. Họ rất có thể sẽ cho rằng bạn muốn nói đến thủ tục hệ thống, chứ không phải một thủ tục khác mà bạn đã tạo ra để chia sẻ tên của nó.
Một điều thú vị khác xảy ra khi bạn tạo một thủ tục được lưu trữ tham chiếu đến một thủ tục được lưu trữ có tiền tố là sp_
điều đó chỉ xảy ra cũng tồn tại trong tổng thể. Hãy chọn một thủ tục hiện có mà bạn có thể không quen thuộc ngay lập tức (và do đó có thể là một đại diện có nhiều khả năng hơn cho kịch bản mà tôi đang mô tả):sp_resyncuniquetable
.
CREATE PROCEDURE dbo.test1 AS BEGIN EXEC dbo.sp_resyncuniquetable; END GO
Trong Management Studio, IntelliSense không gạch dưới tên thủ tục được lưu trữ là không hợp lệ, vì có một thủ tục hợp lệ với tên đó trong tổng thể. Vì vậy, nếu không nhìn thấy một dòng nguệch ngoạc bên dưới, bạn có thể cho rằng thủ tục đã ở đó (và giả sử thủ tục trong bản chính có thể được thực thi mà không có lỗi, điều này cũng có thể vượt qua QA / testing). Nếu bạn chọn một tên khác cho quy trình đồng bộ lại của mình, giả sử proc_resyncuniquetable
, hoàn toàn không có cơ hội cho sự mơ hồ này (trừ khi ai đó tự tạo thủ tục đó ở chế độ chính, điều mà tôi đoán có thể xảy ra). Nếu quy trình chưa tồn tại, trình gọi sẽ vẫn được tạo thành công (do phân giải tên bị trì hoãn), nhưng bạn sẽ nhận được cảnh báo sau:
The module 'test1' depends on the missing object 'dbo.proc_resyncuniquetable'. The module will still be created; however, it cannot run successfully until the object exists.
Thêm một nguồn mơ hồ nữa có thể xảy ra trong trường hợp này. Chuỗi sự kiện sau đây là hoàn toàn hợp lý:
- Bạn tạo phiên bản ban đầu của một thủ tục, chẳng hạn,
sp_foo
. - Người triển khai vô tình tạo một phiên bản chính (và có thể thông báo hoặc có thể không, nhưng trong cả hai trường hợp đều không xóa).
- Người triển khai (hoặc người khác) tạo quy trình, lần này là trong cơ sở dữ liệu phù hợp.
- Theo thời gian, bạn thực hiện nhiều sửa đổi đối với
your_database.dbo.sp_foo
. - Bạn thay thế
sp_foo
vớisp_superfoo
và xóasp_foo
từ cơ sở dữ liệu người dùng. - Khi cập nhật (các) ứng dụng để tham chiếu quy trình mới được lưu trữ, bạn có thể bỏ lỡ một hoặc hai thay thế vì nhiều lý do khác nhau.
Vì vậy, trong trường hợp này, ứng dụng vẫn đang gọi sp_foo
và nó không thất bại - ngay cả khi bạn đã xóa bản sao cục bộ - vì nó tìm thấy những gì nó cho là bản chính tương đương. Không những thủ tục được lưu trữ này trong bản chính không tương đương với sp_superfoo
, nó thậm chí không tương đương với phiên bản mới nhất của sp_foo
.
"Không tìm thấy thủ tục" là một vấn đề dễ khắc phục hơn nhiều so với "Thủ tục không tồn tại - nhưng mã gọi nó hoạt động và không hoàn toàn trả lại kết quả mong đợi."
Kết luận
Tôi vẫn nghĩ rằng, mặc dù hành vi đã thay đổi một chút trong SQL Server 2012, bạn không nên sử dụng sp_
tiền tố bất kỳ lúc nào, trừ khi ý định của bạn là tạo một thủ tục được lưu trữ trong bản gốc * và * đánh dấu nó là một đối tượng hệ thống. Nếu không, bạn sẽ gặp phải những vấn đề về hiệu suất này cũng như tiềm ẩn sự mơ hồ trên nhiều phương diện.
Và cá nhân tôi, tôi không nghĩ rằng các thủ tục được lưu trữ cần phải có bất kỳ tiền tố nào - nhưng tôi có ít bằng chứng hữu hình hơn để thuyết phục bạn về điều đó, ngoài việc hỏi bạn rằng nó có thể là loại đối tượng nào? Bạn không thể thực thi một dạng xem, một hàm hoặc một bảng…
Như tôi thường đề xuất, tôi không thực sự quan tâm quy ước đặt tên của bạn là gì, miễn là bạn nhất quán. Nhưng tôi nghĩ bạn nên tránh các tiền tố có thể gây hại như sp_
.