Le query su sys.schemas e sys.synonym sono molto lente per un utente


8

Scenario: SQL Server 2014 (v12.0.4100.1)

Il servizio .NET esegue questa query:

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

... che restituisce circa 6500 righe ma spesso scade dopo 3+ minuti. Quanto XXXXsopra non è 'dbo'.

Se eseguo questa query in SSMS come UtenteA, la query ritorna in meno di un secondo.

Quando eseguito come UserB (che è il modo in cui si connette il servizio .NET), la query richiede 3-6 minuti e ha la CPU% al 25% (di 4 core) per tutto il tempo.

UtenteA è un accesso al dominio nel ruolo di amministratore di sistema.

UserB è un login SQL con:

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]

Posso duplicare questo in SSMS racchiudendo l'SQL sopra in un Execute as...Revertblocco, quindi il codice .NET è fuori dall'immagine.

Il piano di esecuzione ha lo stesso aspetto. Ho diffuso l'XML e ci sono solo differenze minori (CompileTime, CompileCPU, CompileMemory).

Le statistiche IO non mostrano letture fisiche:

Tabella "sysobjvalues". Conteggio scansioni 0, letture logiche 19970, letture fisiche 0, letture avanti 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.
Tabella "File di lavoro". Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.
Tabella "Tavolo da lavoro". Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.
Tabella "sysschobjs". Conteggio scansione 1, letture logiche 9122, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.
Tabella "sysclsobjs". Conteggio scansione 0, letture logiche 2, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

XEvent attende lo stato (per una query di ~ 3min) sono:

+ --------------------- + ------------ + -------------- -------- + ------------------------------ + ---------- ------------------- +
| Tipo di attesa | Attendi il conteggio | Tempo di attesa totale (ms) | Tempo di attesa totale delle risorse (ms) | Tempo di attesa del segnale totale (ms) |
+ --------------------- + ------------ + -------------- -------- + ------------------------------- + --------- -------------------- +
| SOS_SCHEDULER_YIELD | 37300 | 427 | 20 | 407 |
| NETWORK_IO | 5 | 26 | 26 | 0 |
| IO_COMPLETION | 3 | 1 | 1 | 0 |
+ --------------------- + ------------ + -------------- -------- + ------------------------------- + --------- -------------------- +

Se riscrivo la query (in SSMS, non ho accesso al codice app) a

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

quindi UserB funziona alla stessa velocità (veloce) di UserA.

Se aggiungo db_ownera UserB, quindi, di nuovo, la query viene eseguita <1 sec.

Schema creato tramite questo modello:

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

E {2} è, credo, un elenco di sinonimi creati in quello schema.

Profilo della query in due punti nella query:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Ho aperto un biglietto con Microsoft.

Inoltre, abbiamo provato ad aggiungere UserB db_ownere quindi a DENYtutti i privilegi a cui sappiamo che sono associati db_owner. Il risultato è una query veloce. O abbiamo perso qualcosa (del tutto possibile) o c'è un controllo speciale per il db_ownerruolo.

Risposte:


5

Potresti voler riscrivere la tua query come segue (sto usando dbopiuttosto che in XXXXmodo da trovare alcuni sinonimi sul mio database di test). Questo è simile alla riscrittura che hai trovato più efficiente, ma evita la necessità di dichiarare una variabile e utilizzare due query.

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

Questo produce un piano come il seguente:

inserisci qui la descrizione dell'immagine

Una cosa molto interessante Filterdell'operatore in questo piano è che ha un predicato che esegue un has_access()controllo interno . Questo filtro rimuove qualsiasi oggetto che l'account corrente non disponga delle autorizzazioni sufficienti per vedere. Tuttavia, questo controllo è in cortocircuito (cioè, si completa molto più rapidamente) se si è membri del db_ownerruolo, il che potrebbe spiegare le differenze di prestazione che si stanno vedendo.

inserisci qui la descrizione dell'immagine

Ecco il piano di query per la query originale. Notare che tutti i sinonimi nel database ( 1,126nel mio caso, ma probabilmente molti altri nel tuo caso) passano attraverso il has_access()filtro molto costoso , anche se solo i 2sinonimi corrispondono allo schema. Utilizzando la query semplificata sopra, possiamo garantire che has_access()venga invocato solo per i sinonimi che corrispondono alla tua query anziché per tutti i sinonimi nel database.

inserisci qui la descrizione dell'immagine


Utilizzo di sys.dm_exec_query_profiles per esplorare ulteriormente

Come suggerisce Martin, possiamo confermare che il controllo has_access () è un collo di bottiglia significativo utilizzando sys.dm_exec_query_profilessu SQL Server 2014+. Se eseguo la seguente query utilizzando un db_owneraccount su un database con ~ 700K oggetti, la query richiede ~500ms:

SELECT COUNT(*)
FROM sys.objects

Quando viene eseguito con un account che non è un db_owner, questa stessa query richiede circa otto minuti! Eseguendo il piano effettivo e utilizzando una procedura p_queryProgress che ho scritto per analizzare sys.dm_exec_query_profilespiù facilmente l'output, possiamo vedere che quasi tutto il tempo di elaborazione viene speso per l' Filteroperatore che sta eseguendo il has_access()controllo:

inserisci qui la descrizione dell'immagine


Problema TokenAndPermUserStore? Nel qual caso questo articolo della Knowledge Base potrebbe aiutare support.microsoft.com/en-gb/kb/955644
Martin Smith

@MartinSmith Molto interessante, non ero a conoscenza delle opzioni di configurazione access check cache bucket counte in access check cache quotaprecedenza. Dovrà giocarci un po 'con quelli.
Geoff Patterson,

Non sono sicuro che questa cache sia pertinente al caso qui. Ricordo solo che causava problemi in passato.
Martin Smith,

1
@MartinSmith Sebbene quelle impostazioni non abbiano avuto un impatto, c'è qualcosa di interessante nella cache. Sembra che l'esistenza della cache sia dannosa. Ad esempio, se corro WHILE(1=1) BEGIN DBCC FREESYSTEMCACHE ('TokenAndPermUserStore') WAITFOR DELAY '00:00:05' ENDper sempre, la query si completa in meno di 2 minuti contro 8 minuti in genere.
Geoff Patterson,

1
Grazie a tutti: la risposta di Microsoft fa eco ai commenti precedenti e una riscrittura delle query è la soluzione migliore. Si scopre che has_access () ha un corto circuito all'inizio per testare db_owner o sysadmin e ciò ha comportato una grande differenza di tempo.
James,

0

Se questo è ancora attivo - abbiamo avuto lo stesso problema - sembra che se sei il dbo o un sysadmin, allora qualsiasi accesso a sys.objects (o qualcosa del genere) - allora è istantaneo senza controlli su singoli oggetti.

se è un db_datareader modesto, deve controllare ogni oggetto a sua volta ... è nascosto nel piano di query poiché si comportano più come funzioni che come viste / tabelle

il piano sembra lo stesso, ma sta facendo cose diverse dietro il cofano


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.