Supera MERGE JOIN (INDEX SCAN) con un valore KEY singolo esplicito su un KEY ESTERO


9

Aggiunto il 7/11 Il problema è che si verificano deadlock a causa della scansione dell'indice durante MERGE JOIN. In questo caso una transazione che tenta di ottenere il blocco S sull'intero indice nella tabella padre FK, ma in precedenza un'altra transazione inserisce il blocco X su un valore chiave dell'indice.

Vorrei iniziare con un piccolo esempio (utilizzato TSQL2012 DB da 70-461 Cource):

CREATE TABLE [Sales].[Orders](
[orderid] [int] IDENTITY(1,1) NOT NULL,
[custid] [int] NULL,
[empid] [int] NOT NULL,
[shipperid] [int] NOT NULL,
... )

Le colonne [custid], [empid], [shipperid]sono parametri corelati di [Sales].[Customers], [HR].[Employees], [Sales].[Shippers]conseguenza. In ogni caso abbiamo un indice cluster su una colonna di riferimento in una tabella parrent.

ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([custid]) REFERENCES [Sales].[Customers] ([custid])
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([empid]) REFERENCES [HR].[Employees] ([empid])
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Shippers] FOREIGN KEY([shipperid])REFERENCES [Sales].[Shippers] ([shipperid])

Sto provando ad INSERT [Sales].[Orders] SELECT ... FROMun altro tavolo chiamato [Sales].[OrdersCache]che ha la stessa struttura [Sales].[Orders]delle chiavi esterne tranne. Un'altra cosa potrebbe essere importante menzionare la tabella [Sales].[OrdersCache]è un indice cluster.

CREATE CLUSTERED INDEX idx_c_OrdersCache ON Sales.OrdersCache ( custid, empid )

Come previsto, quando sto cercando di inserire un volume ridotto di dati, LOOP JOIN funziona bene facendo cercare l'indice sulle chiavi esterne.

Con elevati volumi di dati MERGE JOIN viene utilizzato da Query Optimizer come un modo più efficiente per mantenere la chiave foregn nella query.

E non c'è nulla a che fare con questo, tranne OPTION (LOOP JOIN) nel nostro caso con chiavi esterne o INNER LOOP JOIN nel caso esplicito JOIN.

Di seguito è la query che sto cercando di eseguire nel mio ambiente:

INSERT Sales.Orders (
        custid, empid, shipperid, ... )
SELECT  custid, empid, 2, ...
FROM Sales.OrdersCache

Osservando il piano, possiamo vedere che tutte e 3 le chiavi di inserimento sono state validate con MERGE JOIN. Per me non è un modo appropriato poiché utilizza SCANSIONE INDICE con blocco dell'intero indice. MERGE JOIN durante la convalida delle chiavi esterne

L'uso di OPTION (LOOP JOIN) non è adatto poiché costa quasi il 15% in più rispetto a MERGE JOIN (penso che la regressione sarà maggiore con l'aumento dei volumi di dati).

Nell'istruzione SELECT è possibile visualizzare un valore singolo per l' shipperidattributo per l'intero set inserito. A mio avviso, ci deve essere un modo per accelerare la fase di convalida per l'insieme inserito almeno per l'attributo immutabile. Qualcosa di simile a:

  • crea LOOP JOIN, MERGE JOIN, HASH JOIN se abbiamo un sottoinsieme indefinito per la validazione JOIN
  • se esiste un solo valore esplicito della colonna convalidata, effettuiamo la convalida una sola volta (INDICE SEEK).

Esiste un modello comune per superare la situazione precedente utilizzando strutture di codice, oggetti DDL aggiuntivi, ecc.?

Aggiunto il 20/07. Soluzione. Query Optimizer effettua già un'ottimizzazione di convalida "chiave singola - chiave esterna" utilizzando MERGE JOIN. E rende solo per la tabella Sales.Shippers, lasciando LOOP JOIN per altri join nella query contemporaneamente. Dal momento che ho alcune righe nella tabella padre, lo Strumento per ottimizzare le query utilizza l'algoritmo di join Ordinamento-unione e confronta ogni riga della tabella interna con la tabella padre una sola volta. Questa è la risposta alla mia domanda se esiste un meccanismo particolare per elaborare efficacemente singoli valori in un set durante la convalida della chiave singola. Non è una decisione così perfetta, ma è così che SQL Server ottimizza il caso.

L'indagine sull'incidenza delle prestazioni ha rivelato che nel mio caso le istruzioni di inserimento MERGE JOIN e LOOP JOIN sono diventate approssimativamente uguali con 750 righe inserite simultaneamente con la seguente superiorità di MERGE JOIN (nella risorsa tempo CPU). Quindi l'utilizzo di OPTION (LOOP JOIN) è una soluzione appropriata per il mio processo aziendale.

Risposte:


8

L'uso di OPTION (LOOP JOIN) non è adatto poiché costa quasi il 15% in più rispetto a MERGE JOIN

Le percentuali di costo visualizzate nell'output dello showplan sono sempre stime del modello di ottimizzatore, anche in un piano post-esecuzione (effettivo). Questi costi probabilmente non riflettono le effettive prestazioni di runtime sul tuo particolare hardware. L'unico modo per essere sicuri è testare le alternative con il carico di lavoro, misurando qualsiasi metrica sia più importante per te (tempo trascorso, utilizzo della CPU e così via).

Nella mia esperienza, l'ottimizzatore passa da loop ad join per unire join per la convalida della chiave esterna troppo presto. In tutte le operazioni tranne le più grandi, ho trovato che i loop si uniscono per essere preferibili. E per "grande" intendo almeno decine di milioni di righe, certamente non le migliaia o così indicate nei commenti alla tua domanda.

Secondo me ci deve essere un modo per rendere più veloce la fase di validazione per l'insieme inserito almeno per l'attributo immutabile.

Ciò ha senso in linea di principio, ma oggi non esiste tale logica all'interno della logica di costruzione del piano che la convalida della chiave esterna utilizza. La logica attuale è volutamente molto generica e ortogonale alla domanda più ampia; ottimizzazioni specifiche complicano i test e aumentano la probabilità di bug per i casi limite.

Esiste un modello comune per superare la situazione precedente utilizzando strutture di codice, oggetti DDL aggiuntivi, ecc.?

Non che ne sia a conoscenza. Il rischio di deadlock con la convalida della chiave esterna di unione unione è ben noto , con la soluzione più utilizzata è il OPTION (LOOP JOIN)suggerimento. Non è possibile evitare blocchi condivisi durante la convalida della chiave esterna, poiché sono necessari per la correttezza , anche con livelli di isolamento di versione delle righe.

Non esiste una risposta generale migliore (rispetto al suggerimento di join di loop) se si desidera conservare la capacità per più processi simultanei di aggiungere righe alle tabelle padre e figlio in modo transazionale. Ma se sei disposto a serializzare le modifiche ai dati (non le letture), usare sp_getapplock è una tecnica affidabile e semplice.

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.