Leistungsoptimierung der .WRITE-Klausel


7

Ich habe festgestellt, dass Sie mit der Klausel ( Beispiel hier und hier ) high performance updatesfür varchar(max),nvarchar(max),varbinary(max)Spalten arbeiten können . Leider wird in der offiziellen Dokumentation nichts über Leistungsoptimierung erwähnt ..WRITE

Kann jemand sagen, dass es überhaupt eine Optimierung gibt? Hat jemand einen Leistungstest gemacht? Auch wenn es eine Optimierung gibt, wird dies vermutlich auch nur bestimmte Situationen betreffen.


1
Eine mögliche Leistungsverbesserung wäre, dass Teilaktualisierungen mit .WRITEnur minimal protokolliert werden. Wenn Sie eine große Anzahl von Zeilen aktualisieren, führt dies zu einer geringeren E / A für die Protokolldatei, was möglicherweise zu einer höheren Leistung führt.
Max Vernon

1
Außerdem .WRITEmuss der Inhalt der Spalte nicht neu geschrieben werden, sondern es werden lediglich die bereits in der Spalte enthaltenen Daten geändert. Dies zeigt an, dass in der Tabelle selbst weniger E / A erforderlich sind.
Max Vernon

1
Es gibt einen signifikanten Leistungsunterschied, wenn Ihre Lob-Spalte mehr als 8000 Bytes speichert. Bei In-Row-Daten gibt es kaum einen Unterschied. Wenn Sie testen, können Sie set statistics io onden Unterschied in den logischen Lesevorgängen verwenden und einen Blick darauf werfen.
Mikael Eriksson

@MikaelEriksson Danke für die Vorschläge - sehr interessant - ich beobachte überhaupt keine lob read-ahead reads.
Gotqn

@ MaxVernon Könnten Sie eine offizielle Quelle angeben, die dies bestätigt? Diese Klausel scheint sehr mächtig zu sein, ich kann nicht verstehen, warum es keine weiteren Informationen darüber gibt.
Gotqn

Antworten:


2

Die WRITEMethode ist minimal protokolliert. Wenn Sie eine reguläre UPDATEAnweisung verwenden, wird die gesamte Zeichenfolge mit überschrieben FULL LOGGING. Dies würde bei großen Updates ineffizient werden.

Um die Aktualisierung für Datentypen mit großem Wert zu UPDATEunterstützen .WRITE, unterstützt die Syntax die Methode. Dies führt aufgrund der minimalen Protokollierung zu weniger Transaktionsprotokoll - einschließlich des Einfügens oder Anhängens neuer Daten .

Hinweis: Die UPDATEusing- WRITEMethode schlägt fehl, wenn das Ziel LOBist NULL.

Unten finden Sie eine schnelle und schmutzige Repro, die das Transaktionsprotokoll zeigt, das mit REGULAR UPDATEund UPDATEmit der .WRITEMethode erstellt wurde.

--- create table

create table dbo.testWRITEUpdate (
    ID int identity(1, 1)
    ,COMMENTS varchar(MAX) not null
    )
go

insert into dbo.testWRITEUpdate (COMMENTS)
values (REPLICATE(CAST('KIN' as varchar(max)), 9000))

checkpoint
go

--- Regelmäßiges Update:

set statistics io on
 begin tran
update dbo.testWRITEUpdate
set COMMENTS =  STUFF(COMMENTS, 9, 55, '$$$$$(((())))))))))))))____GarbageData____ Entered ____')
where ID  =1

set statistics io off
go

commit tran

- Statistik E / A-Ausgabe

 Table 'testWRITEUpdate'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 15, lob physical reads 0, lob read-ahead reads 6.

- Transaktionsprotokoll generiert -> 6 Protokollsätze mit 54580 Bytes :

Geben Sie hier die Bildbeschreibung ein

Update mit der WRITEMethode:

begin tran
set statistics io on
update dbo.testWRITEUpdate
set COMMENTS.WRITE('$$$$$(((())))))))))))))____GarbageData____ Entered ____',9,55)
where ID  =1
set statistics io off

commit tran
go

- Statistik IO

Table 'testWRITEUpdate'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 1, lob physical reads 0, lob read-ahead reads 0.

- Transaktionsprotokoll generiert -> 3 Protokollsätze mit 464 Bytes :

Geben Sie hier die Bildbeschreibung ein

Der obige Test zeigt, dass die WRITEMethode mehr Nutzen in Bezug auf das generierte Transaktionsprotokoll bietet, da es sich um eine minimal protokollierte Operation handelt und weniger logische Lesevorgänge erforderlich sind.

Hinweis : Dies ist nur ein Kratzer auf der Oberfläche. Es können mehr Tests durchgeführt werden, um die Leistung tatsächlich zu beweisen, wenn die WRITE-Methode eine bessere Leistung als das reguläre UPDATE erbringt, insbesondere wenn es sich um VARCHAR-, NVARCHAR- oder VARBINARY-Datentypen handelt.


Update (wie vom OP angefordert):

Unten ist die Abfrage zum Finden der sys.dm_tran_database_transactions

SELECT [database_transaction_log_bytes_used] FROM sys.dm_tran_database_transactions
WHERE [database_id] = DB_ID ('test_kin');
GO

--- oder eine detailliertere Version:

SELECT DTST.[session_id], 
 DES.[login_name] AS [Login Name], 
 DB_NAME (DTDT.database_id) AS [Database], 
 DTDT.[database_transaction_begin_time] AS [Begin Time], 
 -- DATEDIFF(ms,DTDT.[database_transaction_begin_time], GETDATE()) AS [Duration ms], 
 CASE DTAT.transaction_type 
   WHEN 1 THEN 'Read/write' 
    WHEN 2 THEN 'Read-only' 
    WHEN 3 THEN 'System' 
    WHEN 4 THEN 'Distributed' 
  END AS [Transaction Type], 
  CASE DTAT.transaction_state 
    WHEN 0 THEN 'Not fully initialized' 
    WHEN 1 THEN 'Initialized, not started' 
    WHEN 2 THEN 'Active' 
    WHEN 3 THEN 'Ended' 
    WHEN 4 THEN 'Commit initiated' 
    WHEN 5 THEN 'Prepared, awaiting resolution' 
    WHEN 6 THEN 'Committed' 
    WHEN 7 THEN 'Rolling back' 
    WHEN 8 THEN 'Rolled back' 
  END AS [Transaction State], 
 DTDT.[database_transaction_log_record_count] AS [Log Records], 
 DTDT.[database_transaction_log_bytes_used] AS [Log Bytes Used], 
 DTDT.[database_transaction_log_bytes_reserved] AS [Log Bytes RSVPd], 
 DEST.[text] AS [Last Transaction Text], 
 DEQP.[query_plan] AS [Last Query Plan] 
FROM sys.dm_tran_database_transactions DTDT 
 INNER JOIN sys.dm_tran_session_transactions DTST 
   ON DTST.[transaction_id] = DTDT.[transaction_id] 
 INNER JOIN sys.[dm_tran_active_transactions] DTAT 
   ON DTST.[transaction_id] = DTAT.[transaction_id] 
 INNER JOIN sys.[dm_exec_sessions] DES 
   ON DES.[session_id] = DTST.[session_id] 
 INNER JOIN sys.dm_exec_connections DEC 
   ON DEC.[session_id] = DTST.[session_id] 
 LEFT JOIN sys.dm_exec_requests DER 
   ON DER.[session_id] = DTST.[session_id] 
 CROSS APPLY sys.dm_exec_sql_text (DEC.[most_recent_sql_handle]) AS DEST 
 OUTER APPLY sys.dm_exec_query_plan (DER.[plan_handle]) AS DEQP 
ORDER BY DTDT.[database_transaction_log_bytes_used] DESC;
-- ORDER BY [Duration ms] DESC;

Danke das ist interessant. Sie können auch darauf hinweisen, dass - If the statement inserts or appends new data, the operation is minimally-logged if the database recovery model is simple or bulk-loggedund Updates to existing data are not minimally-loggedich denke, im FULLWiederherstellungsmodell ist die Protokollierung kein Faktor ( beyondrelational.com/modules/2/blogs/77/Posts/17835/… )
gotqn

1
Vielen Dank. Ich habe auch Ihren Code in der FULLDatenbank des Wiederherstellungsmodells überprüft und es wird tatsächlich weniger protokolliert, wenn er .WRITEverwendet wird. Ich denke, der obige Link ist falsch.
Gotqn
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.