Truy vấn đối với sys.schemas và sys.syn từ chạy rất chậm đối với một người dùng


8

Kịch bản: SQL Server 2014 (v12.0.4100.1)

Dịch vụ .NET chạy truy vấn này:

SELECT name, base_object_name 
FROM sys.synonyms 
WHERE schema_id IN (SELECT schema_id 
                    FROM sys.schemas 
                    WHERE name = N'XXXX')
ORDER BY name

... Trả về khoảng 6500 hàng nhưng thường hết sau 3+ phút. Ở XXXXtrên không phải là 'dbo'.

Nếu tôi chạy truy vấn này trong SSMS dưới dạng UserA, truy vấn sẽ trả về sau chưa đầy một giây.

Khi chạy dưới dạng UserB (đó là cách dịch vụ .NET kết nối), truy vấn mất 3-6 phút và có% CPU ở mức 25% (trong 4 lõi) trong toàn bộ thời gian.

UserA là một Đăng nhập tên miền trong vai trò sysadmin.

UserB là một Đăng nhập SQL với:

EXEC sp_addrolemember N'db_datareader', N'UserB'
EXEC sp_addrolemember N'db_datawriter', N'UserB'
EXEC sp_addrolemember N'db_ddladmin', N'UserB'
GRANT EXECUTE TO [UserB]
GRANT CREATE SCHEMA TO [UserB]
GRANT VIEW DEFINITION TO [UserB]

Tôi có thể nhân đôi điều này trong SSMS bằng cách gói SQL ở trên vào một Execute as...Revertkhối, vì vậy mã .NET nằm ngoài hình ảnh.

Kế hoạch thực hiện trông giống nhau. Tôi khác với XML và chỉ có những khác biệt nhỏ (CompileTime, CompileCPU, CompileMemory).

Chỉ số IO tất cả hiển thị không đọc vật lý:

Bảng 'sysobjvalues'. Quét số 0, đọc logic 19970, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Bảng 'Workfile'. Quét số 0, đọc logic 0, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Bảng 'Bàn làm việc'. Quét số 0, đọc logic 0, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Bảng 'sysschobjs'. Quét số 1, đọc logic 9122, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Bảng 'sysclsobjs'. Quét số 0, đọc logic 2, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.

Trạng thái chờ XEvent (cho truy vấn ~ 3 phút) là:

+ --------------------- + ------------ + -------------- -------- + ------------------------------ + ---------- ------------------- +
| Loại chờ | Đếm chờ | Tổng thời gian chờ (ms) | Tổng thời gian chờ tài nguyên (ms) | Tổng thời gian chờ tín hiệu (ms) |
+ --------------------- + ------------ + -------------- -------- + ------------------------------- + --------- -------------------- +
| SOS_SCHEDULER_YIELD | 37300 | 427 | 20 | 407 |
| MẠNG_IO | 5 | 26 | 26 | 0 |
| IO_COMPLETION | 3 | 1 | 1 | 0 |
+ --------------------- + ------------ + -------------- -------- + ------------------------------- + --------- -------------------- +

Nếu tôi viết lại truy vấn (trong SSMS, tôi không có quyền truy cập vào Mã ứng dụng) để

declare @id int 
SELECT @id=schema_id FROM sys.schemas WHERE name = N'XXXX'
SELECT a.name, base_object_name FROM sys.synonyms a
WHERE schema_id = @id
ORDER BY name

sau đó UserB chạy với tốc độ (nhanh) tương tự như UserA.

Nếu tôi thêm db_ownervào UserB, thì, một lần nữa, truy vấn sẽ chạy <1 giây.

Lược đồ được tạo thông qua mẫu này:

DECLARE @TranName VARCHAR(20)
SELECT @TranName = 'MyTransaction'

BEGIN TRANSACTION @TranName
GO

IF NOT EXISTS (SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
        WHERE SCHEMA_NAME = '{1}')
BEGIN
    EXEC('CREATE SCHEMA [{1}]')
    EXEC sp_addextendedproperty @name='User', @value='{0}', @level0type=N'Schema', @level0name=N'{1}'
END
GO

{2}

COMMIT TRANSACTION MyTransaction;
GO

Và {2}, tôi tin rằng, một danh sách các Từ đồng nghĩa được tạo trong lược đồ đó.

Hồ sơ truy vấn tại hai điểm vào truy vấn:

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

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

Tôi đã mở một vé với Microsoft.

Ngoài ra, chúng tôi đã thử thêm UserB vào db_owner, và sau đó lấy DENYtất cả các đặc quyền mà chúng tôi biết có liên quan db_owner. Kết quả là một truy vấn nhanh. Hoặc là chúng tôi đã bỏ lỡ một cái gì đó (hoàn toàn có thể), hoặc có một kiểm tra đặc biệt cho db_ownervai trò.

Câu trả lời:


5

Bạn có thể muốn viết lại truy vấn của mình như sau (Tôi đang sử dụng dbochứ không phải XXXXđể tôi tìm thấy một số từ đồng nghĩa trên cơ sở dữ liệu thử nghiệm của mình). Điều này tương tự như việc viết lại mà bạn thấy là hiệu quả hơn, nhưng tránh sự cần thiết phải khai báo một biến và sử dụng hai truy vấn.

SELECT name, base_object_name 
FROM sys.synonyms 
WHERE schema_id = SCHEMA_ID(N'dbo')
ORDER BY name

Điều này mang lại một kế hoạch như sau:

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

Một điều rất thú vị về người Filtervận hành trong kế hoạch này là nó có một vị từ thực hiện has_access()kiểm tra nội bộ . Bộ lọc này sẽ xóa mọi đối tượng mà tài khoản hiện tại không có đủ quyền để xem. Tuy nhiên, kiểm tra này được ngắn mạch (nghĩa là hoàn thành nhanh hơn nhiều) nếu bạn là thành viên của db_ownervai trò, điều này có thể giải thích sự khác biệt về hiệu suất bạn đang thấy.

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

Đây là kế hoạch truy vấn cho truy vấn ban đầu của bạn. Lưu ý rằng tất cả các từ đồng nghĩa trên cơ sở dữ liệu ( 1,126trong trường hợp của tôi, nhưng có thể nhiều từ khác trong trường hợp của bạn) đi qua has_access()bộ lọc rất đắt tiền , mặc dù chỉ các 2từ đồng nghĩa phù hợp với lược đồ. Bằng cách sử dụng truy vấn đơn giản hóa ở trên, chúng tôi có thể đảm bảo rằng has_access()chỉ được gọi cho các từ đồng nghĩa phù hợp với truy vấn của bạn chứ không phải cho tất cả các từ đồng nghĩa trong cơ sở dữ liệu.

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


Sử dụng sys.dm_exec_query_profiles để khám phá thêm

Như Martin gợi ý, chúng tôi có thể xác nhận rằng kiểm tra has_access () là một nút cổ chai đáng kể bằng cách sử dụng sys.dm_exec_query_profilestrên SQL Server 2014+. Nếu tôi chạy truy vấn sau bằng db_ownertài khoản trên cơ sở dữ liệu có ~ 700K đối tượng, truy vấn sẽ ~500ms:

SELECT COUNT(*)
FROM sys.objects

Khi chạy với một tài khoản không phải là một db_owner, truy vấn tương tự này mất khoảng tám phút! Chạy với kế hoạch thực tế và sử dụng thủ tục p_queryProTHER mà tôi đã viết để giúp phân tích cú pháp sys.dm_exec_query_profilesdễ dàng hơn, chúng ta có thể thấy rằng hầu như toàn bộ thời gian xử lý được dành cho Filtertoán tử đang thực hiện has_access()kiểm tra:

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


Vấn đề TokenAndPermUserStore? Trong trường hợp này, bài viết KB này có thể giúp support.microsoft.com/en-gb/kb/955644
Martin Smith

@MartinSmith Rất thú vị, tôi không biết các tùy chọn access check cache bucket countaccess check cache quotacấu hình trước đây. Sẽ phải chơi xung quanh với những người một chút.
Geoff Patterson

Tôi không chắc chắn rằng bộ đệm này có liên quan đến trường hợp ở đây. Tôi chỉ nhớ nó gây ra vấn đề trong quá khứ.
Martin Smith

1
@MartinSmith Trong khi các cài đặt đó không có tác động, có một điều thú vị đang diễn ra với bộ đệm. Có vẻ như sự tồn tại của bộ đệm là bất lợi. Ví dụ: nếu tôi chạy WHILE(1=1) BEGIN DBCC FREESYSTEMCACHE ('TokenAndPermUserStore') WAITFOR DELAY '00:00:05' ENDtrong một vòng lặp mãi mãi, truy vấn sẽ hoàn thành trong vòng dưới 2 phút so với 8 phút.
Geoff Patterson

1
Cảm ơn tất cả mọi người - phản hồi từ Microsoft lặp lại các bình luận ở trên và viết lại truy vấn là giải pháp tốt nhất. Hóa ra has_access () có tính năng ngắn mạch khi bắt đầu kiểm tra db_owner hoặc sysadmin và điều đó dẫn đến sự khác biệt lớn về thời gian.
James

0

Nếu điều này vẫn còn tồn tại - chúng tôi đã có cùng một vấn đề - có vẻ như nếu bạn là dbo hoặc sysadmin thì mọi quyền truy cập vào sys.objects (hoặc bất cứ điều gì tương tự) - ngay lập tức không có kiểm tra đối với từng đối tượng.

nếu đó là một db_datareader thấp thì nó phải lần lượt kiểm tra từng đối tượng ... nó bị ẩn trong kế hoạch truy vấn vì chúng hoạt động giống như các hàm hơn là các khung nhìn / bảng

kế hoạch trông giống nhau, nhưng nó làm những việc khác nhau đằng sau mui xe


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.