Tối ưu hóa hiệu suất mệnh đề .WRITE


7

Tôi đã thấy rằng bạn có thể làm high performance updatestrên varchar(max),nvarchar(max),varbinary(max)các cột bằng cách sử dụng .WRITEmệnh đề ( ví dụ ở đâyở đây ). Thật không may, không có gì được đề cập về tối ưu hóa hiệu suất trong tài liệu chính thức .

Bất cứ ai có thể nói là có bất kỳ tối ưu hóa nào cả? Có ai đã thực hiện một số thử nghiệm hiệu suất? Ngoài ra nếu có tối ưu hóa tôi đoán nó cũng sẽ chỉ ảnh hưởng đến các tình huống cụ thể.


1
Một cải tiến hiệu suất tiềm năng sẽ là các bản cập nhật một phần .WRITEchỉ được ghi lại tối thiểu. Nếu bạn đang cập nhật một lượng lớn hàng, điều này sẽ dẫn đến IO thấp hơn vào tệp nhật ký, có thể dẫn đến hiệu suất cao hơn.
Max Vernon

1
Ngoài ra, .WRITEkhông cần phải viết lại nội dung của cột, nó chỉ cần sửa đổi dữ liệu đã có trong cột. Điều này cho thấy ít IO là cần thiết trên chính bảng.
Max Vernon

1
Có một sự khác biệt đáng kể về hiệu suất nếu cột lob của bạn lưu trữ hơn 8000 byte. Đối với dữ liệu liên tiếp, có rất ít nếu có sự khác biệt. Khi bạn kiểm tra, bạn có thể sử dụng set statistics io onvà xem xét sự khác biệt về số lần đọc logic.
Mikael Eriksson

@MikaelEriksson Cảm ơn những lời đề nghị - rất thú vị - Tôi hoàn toàn không quan sát lob read-ahead reads.
gotqn

@MaxVernon Bạn có thể cung cấp nguồn chính thức xác nhận điều này? Điều khoản này dường như rất mạnh mẽ, tôi không thể hiểu tại sao không có thêm thông tin về nó.
gotqn

Câu trả lời:


2

Các WRITEphương pháp được tối thiểu đăng nhập. Nếu bạn sử dụng UPDATEcâu lệnh thông thường , nó sẽ dẫn đến ghi đè toàn bộ chuỗi bằng cách sử dụng FULL LOGGING. Điều này sẽ trở nên không hiệu quả khi xử lý các bản cập nhật lớn.

Để hỗ trợ cập nhật cho các loại dữ liệu giá trị lớn, UPDATEcú pháp hỗ trợ .WRITEphương thức. Điều này sẽ dẫn đến nhật ký giao dịch ít hơn do bản chất của việc ghi nhật ký tối thiểu - bao gồm chèn hoặc nối thêm dữ liệu mới .

Lưu ý: Phương thức UPDATEsử dụng WRITEsẽ thất bại nếu mục tiêu LOBNULL.

Dưới đây là một repro nhanh và bẩn hiển thị nhật ký giao dịch được tạo bằng phương thức REGULAR UPDATEUPDATEsử dụng .WRITE.

--- 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

--- Cập nhật thường xuyên:

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

- thống kê đầu ra IO

 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.

- Nhật ký giao dịch được tạo -> 6 Bản ghi nhật ký với 54580 byte :

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

Cập nhật bằng WRITEphương pháp:

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

- Chỉ số 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.

- Nhật ký giao dịch được tạo -> 3 bản ghi nhật ký với 464 byte :

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

Thử nghiệm trên chứng minh rằng WRITEphương pháp này mang lại nhiều lợi ích hơn về mặt Nhật ký giao dịch được tạo vì nó là hoạt động được ghi nhật ký tối thiểu + phát sinh các lần đọc logic ít hơn.

Lưu ý : Đây chỉ là trầy xước bề mặt, có thể có nhiều bài kiểm tra thực hiện để thực sự chứng minh về hiệu suất nếu phương pháp VIẾT được thực hiện tốt hơn so với CẬP NHẬT thường xuyên đặc biệt là khi đối phó với VARCHAR, kiểu dữ liệu nvarchar OR VARBINARY.


Cập nhật (Theo yêu cầu của OP):

Dưới đây là truy vấn để tìm 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

--- hoặc một phiên bản chi tiết hơn:

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;

Cảm ơn thật thú vị. Ngoài ra, bạn có thể chỉ ra điều đó If the statement inserts or appends new data, the operation is minimally-logged if the database recovery model is simple or bulk-loggedUpdates to existing data are not minimally-logged- vì vậy tôi đoán trong FULLmô hình khôi phục, việc ghi nhật ký không phải là một yếu tố ( beyondrelational.com/modules/2/bloss/77/Posts/17835/ Lỗi )
gotqn

1
Cảm ơn. Tôi cũng đã kiểm tra mã của bạn trên FULLcơ sở dữ liệu mô hình phục hồi và thực sự có ít đăng nhập khi .WRITEđược sử dụng. Tôi đoán các liên kết ở trên là sai.
gotqn
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.