Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Pelajari cara mengoptimalkan performa kode transaksi di kumpulan SQL khusus sambil meminimalkan risiko untuk pemutaran kembali yang panjang.
Transaksi dan pengelogan
Transaksi adalah komponen penting dari mesin kumpulan SQL relasional. Transaksi digunakan selama modifikasi data. Transaksi ini bisa eksplisit atau implisit. Pernyataan INSERT, UPDATE, dan DELETE tunggal adalah semua contoh transaksi implisit. Transaksi eksplisit menggunakan BEGIN TRAN, COMMIT TRAN, atau ROLLBACK TRAN. Transaksi eksplisit biasanya digunakan ketika beberapa pernyataan modifikasi perlu diikat bersama-sama dalam satu unit atomik.
Perubahan pada kumpulan SQL dilacak menggunakan log transaksi. Setiap distribusi memiliki log transaksinya sendiri. Penulisan log transaksi bersifat otomatis. Tidak diperlukan konfigurasi. Meskipun proses ini menjamin penulisan, hal ini memang memperkenalkan beban tambahan dalam sistem. Anda dapat meminimalkan dampak ini dengan menulis kode yang efisien secara transaksional. Kode yang efisien secara transaksional secara luas termasuk dalam dua kategori.
- Gunakan konstruksi pengelogan minimal jika memungkinkan
- Memproses data menggunakan batch yang terdefinisi untuk menghindari transaksi tunggal yang berjalan lama
- Gunakan pola pergantian partisi untuk perubahan besar pada partisi tertentu
Pencatatan minimal vs. penuh
Tidak seperti operasi yang dicatat sepenuhnya, yang menggunakan log transaksi untuk melacak setiap perubahan baris, operasi yang dicatat secara minimal hanya melacak alokasi ekstensi dan perubahan metadata. Oleh karena itu, pengelogan minimal hanya melibatkan pengelogan informasi yang diperlukan untuk mengembalikan transaksi setelah kegagalan, atau untuk permintaan eksplisit (ROLLBACK TRAN). Karena jauh lebih sedikit informasi yang dilacak dalam log transaksi, operasi dengan pencatatan minimal berkinerja lebih efisien daripada operasi serupa yang dicatat sepenuhnya. Selain itu, karena lebih sedikit penulisan masuk ke log transaksi, jumlah data log yang jauh lebih kecil dihasilkan dan lebih efisien I/O.
Batas keamanan transaksi hanya berlaku untuk operasi yang dicatat sepenuhnya.
Nota
Operasi yang dicatat secara minimal dapat berpartisipasi dalam transaksi eksplisit. Karena semua perubahan struktur alokasi dilacak, dimungkinkan untuk mengembalikan operasi yang pencatatannya minimal.
Operasi dengan pencatatan minimal
Operasi berikut ini mampu dicatat secara minimal:
- BUAT TABEL DENGAN MEMILIH (CTAS)
- INSERT..SELECT
- BUAT INDEKS
- MEMBANGUN ULANG INDEKS
- HAPUS INDEKS
- TRUNCATE TABEL
- HAPUS TABEL (DROP TABLE)
- MENGUBAH TABEL PEMINDAHAN PARTISI
Nota
Operasi pergerakan data internal (seperti BROADCAST dan SHUFFLE) tidak terpengaruh oleh batas keamanan transaksi.
Pengelogan minimal dengan beban massal
CTAS dan INSERT... SELECT keduanya adalah operasi pemuatan massal. Namun, keduanya dipengaruhi oleh definisi tabel target dan bergantung pada skenario beban. Tabel berikut menjelaskan kapan operasi massal dicatat sepenuhnya atau minimal:
| Indeks Utama | Skenario Beban | Mode Pengelogan |
|---|---|---|
| Tumpukan | Apa saja | Minimal |
| Indeks Terkelompok | Tabel target kosong | Minimal |
| Indeks Terkelompok | Baris yang dimuat tidak tumpang tindih dengan halaman yang ada di target | Minimal |
| Indeks Terkelompok | Baris yang dimuat tumpang tindih dengan halaman yang ada dalam target | Penuh |
| Indeks Penyimpan Kolom Berkluster | Ukuran batch >= 102.400 per partisi dalam distribusi yang sejajar | Minimal |
| Indeks Penyimpanan Kolom Berkluster | Ukuran batch < 102,400 per distribusi sesuai partisi | Penuh |
Perlu dicatat bahwa setiap penulisan data untuk memperbarui indeks sekunder atau non-kluster akan selalu menjadi operasi yang dicatat sepenuhnya.
Penting
Kumpulan SQL khusus memiliki 60 distribusi. Oleh karena itu, dengan asumsi semua baris didistribusikan secara merata dan masuk dalam satu partisi, batch Anda harus berisi 6.144.000 baris atau lebih besar untuk dicatat seminimal mungkin saat menulis ke Indeks Penyimpan Kolom Berkluster. Jika tabel dipartisi dan baris yang dimasukkan mencakup perbatasan partisi, maka Anda akan memerlukan 6.144.000 baris per perbatasan partisi dengan asumsi distribusi data merata. Setiap partisi dalam setiap distribusi harus secara independen melebihi ambang batas 102.400 baris agar entri dicatat secara minimal ke dalam distribusi.
Memuat data ke dalam tabel yang tidak kosong dengan indeks berkluster sering kali dapat berisi campuran baris yang dicatat sepenuhnya dan dicatat secara minimal. Indeks berkluster adalah sebuah pohon seimbang (balanced tree atau b-tree) dari halaman. Jika halaman yang ditulis sudah berisi baris dari transaksi lain, maka penulisan ini akan dicatat secara lengkap. Namun, jika halaman kosong, maka penulisan ke halaman tersebut akan dicatat secara minimal.
Mengoptimalkan penghapusan
DELETE adalah operasi yang dicatat sepenuhnya. Jika Anda perlu menghapus jumlah data yang besar pada tabel atau partisi, sering kali lebih masuk akal untuk SELECT data yang ingin Anda simpan, yang dapat dijalankan dengan pencatatan minimal. Untuk memilih data, buat tabel baru dengan CTAS. Setelah dibuat, gunakan RENAME untuk menukar tabel lama Anda dengan tabel yang baru dibuat.
-- Delete all sales transactions for Promotions except PromotionKey 2.
--Step 01. Create a new table select only the records we want to kep (PromotionKey 2)
CREATE TABLE [dbo].[FactInternetSales_d]
WITH
( CLUSTERED COLUMNSTORE INDEX
, DISTRIBUTION = HASH([ProductKey])
, PARTITION ( [OrderDateKey] RANGE RIGHT
FOR VALUES ( 20000101, 20010101, 20020101, 20030101, 20040101, 20050101
, 20060101, 20070101, 20080101, 20090101, 20100101, 20110101
, 20120101, 20130101, 20140101, 20150101, 20160101, 20170101
, 20180101, 20190101, 20200101, 20210101, 20220101, 20230101
, 20240101, 20250101, 20260101, 20270101, 20280101, 20290101
)
)
AS
SELECT *
FROM [dbo].[FactInternetSales]
WHERE [PromotionKey] = 2
OPTION (LABEL = 'CTAS : Delete')
;
--Step 02. Rename the Tables to replace the
RENAME OBJECT [dbo].[FactInternetSales] TO [FactInternetSales_old];
RENAME OBJECT [dbo].[FactInternetSales_d] TO [FactInternetSales];
Mengoptimalkan pembaruan
UPDATE adalah operasi yang dicatat sepenuhnya. Jika Anda perlu memperbarui sejumlah besar baris dalam tabel atau partisi, sering kali bisa jauh lebih efisien untuk menggunakan operasi yang dicatat secara minimal seperti CTAS untuk melakukannya.
Dalam contoh di bawah, pembaruan tabel lengkap telah dikonversi ke CTAS sehingga pencatatan minimal dimungkinkan.
Dalam hal ini, kami secara retrospektif menambahkan jumlah diskon ke penjualan dalam tabel:
--Step 01. Create a new table containing the "Update".
CREATE TABLE [dbo].[FactInternetSales_u]
WITH
( CLUSTERED INDEX
, DISTRIBUTION = HASH([ProductKey])
, PARTITION ( [OrderDateKey] RANGE RIGHT
FOR VALUES ( 20000101, 20010101, 20020101, 20030101, 20040101, 20050101
, 20060101, 20070101, 20080101, 20090101, 20100101, 20110101
, 20120101, 20130101, 20140101, 20150101, 20160101, 20170101
, 20180101, 20190101, 20200101, 20210101, 20220101, 20230101
, 20240101, 20250101, 20260101, 20270101, 20280101, 20290101
)
)
)
AS
SELECT
[ProductKey]
, [OrderDateKey]
, [DueDateKey]
, [ShipDateKey]
, [CustomerKey]
, [PromotionKey]
, [CurrencyKey]
, [SalesTerritoryKey]
, [SalesOrderNumber]
, [SalesOrderLineNumber]
, [RevisionNumber]
, [OrderQuantity]
, [UnitPrice]
, [ExtendedAmount]
, [UnitPriceDiscountPct]
, ISNULL(CAST(5 as float),0) AS [DiscountAmount]
, [ProductStandardCost]
, [TotalProductCost]
, ISNULL(CAST(CASE WHEN [SalesAmount] <=5 THEN 0
ELSE [SalesAmount] - 5
END AS MONEY),0) AS [SalesAmount]
, [TaxAmt]
, [Freight]
, [CarrierTrackingNumber]
, [CustomerPONumber]
FROM [dbo].[FactInternetSales]
OPTION (LABEL = 'CTAS : Update')
;
--Step 02. Rename the tables
RENAME OBJECT [dbo].[FactInternetSales] TO [FactInternetSales_old];
RENAME OBJECT [dbo].[FactInternetSales_u] TO [FactInternetSales];
--Step 03. Drop the old table
DROP TABLE [dbo].[FactInternetSales_old]
Nota
Membuat ulang tabel besar dapat memperoleh manfaat dari menggunakan fitur manajemen beban kerja kumpulan SQL khusus. Untuk informasi selengkapnya, lihat Kelas sumber daya untuk manajemen beban kerja.
Mengoptimalkan dengan pengalihan partisi
Jika dihadapkan dengan modifikasi skala besar di dalam partisi tabel, maka pola pengalihan partisi masuk akal. Jika modifikasi data signifikan dan mencakup beberapa partisi, maka iterasi atas partisi mencapai hasil yang sama.
Langkah-langkah untuk melakukan sakelar partisi adalah sebagai berikut:
- Membuat partisi kosong
- Laksanakan 'pembaruan' sebagai CTAS
- Memindahkan data yang ada ke tabel keluaran
- Beralih ke data baru
- Membersihkan data tersebut
Namun, untuk membantu mengidentifikasi partisi yang akan dialihkan, buat prosedur pembantu berikut.
CREATE PROCEDURE dbo.partition_data_get
@schema_name NVARCHAR(128)
, @table_name NVARCHAR(128)
, @boundary_value INT
AS
IF OBJECT_ID('tempdb..#ptn_data') IS NOT NULL
BEGIN
DROP TABLE #ptn_data
END
CREATE TABLE #ptn_data
WITH ( DISTRIBUTION = ROUND_ROBIN
, HEAP
)
AS
WITH CTE
AS
(
SELECT s.name AS [schema_name]
, t.name AS [table_name]
, p.partition_number AS [ptn_nmbr]
, p.[rows] AS [ptn_rows]
, CAST(r.[value] AS INT) AS [boundary_value]
FROM sys.schemas AS s
JOIN sys.tables AS t ON s.[schema_id] = t.[schema_id]
JOIN sys.indexes AS i ON t.[object_id] = i.[object_id]
JOIN sys.partitions AS p ON i.[object_id] = p.[object_id]
AND i.[index_id] = p.[index_id]
JOIN sys.partition_schemes AS h ON i.[data_space_id] = h.[data_space_id]
JOIN sys.partition_functions AS f ON h.[function_id] = f.[function_id]
LEFT JOIN sys.partition_range_values AS r ON f.[function_id] = r.[function_id]
AND r.[boundary_id] = p.[partition_number]
WHERE i.[index_id] <= 1
)
SELECT *
FROM CTE
WHERE [schema_name] = @schema_name
AND [table_name] = @table_name
AND [boundary_value] = @boundary_value
OPTION (LABEL = 'dbo.partition_data_get : CTAS : #ptn_data')
;
GO
Prosedur ini memaksimalkan penggunaan kembali kode dan menjaga contoh pengalihan partisi lebih ringkas.
Kode berikut menunjukkan langkah-langkah yang disebutkan sebelumnya untuk mencapai rutinitas pengalihan partisi penuh.
--Create a partitioned aligned empty table to switch out the data
IF OBJECT_ID('[dbo].[FactInternetSales_out]') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FactInternetSales_out]
END
CREATE TABLE [dbo].[FactInternetSales_out]
WITH
( DISTRIBUTION = HASH([ProductKey])
, CLUSTERED COLUMNSTORE INDEX
, PARTITION ( [OrderDateKey] RANGE RIGHT
FOR VALUES ( 20020101, 20030101
)
)
)
AS
SELECT *
FROM [dbo].[FactInternetSales]
WHERE 1=2
OPTION (LABEL = 'CTAS : Partition Switch IN : UPDATE')
;
--Create a partitioned aligned table and update the data in the select portion of the CTAS
IF OBJECT_ID('[dbo].[FactInternetSales_in]') IS NOT NULL
BEGIN
DROP TABLE [dbo].[FactInternetSales_in]
END
CREATE TABLE [dbo].[FactInternetSales_in]
WITH
( DISTRIBUTION = HASH([ProductKey])
, CLUSTERED COLUMNSTORE INDEX
, PARTITION ( [OrderDateKey] RANGE RIGHT
FOR VALUES ( 20020101, 20030101
)
)
)
AS
SELECT
[ProductKey]
, [OrderDateKey]
, [DueDateKey]
, [ShipDateKey]
, [CustomerKey]
, [PromotionKey]
, [CurrencyKey]
, [SalesTerritoryKey]
, [SalesOrderNumber]
, [SalesOrderLineNumber]
, [RevisionNumber]
, [OrderQuantity]
, [UnitPrice]
, [ExtendedAmount]
, [UnitPriceDiscountPct]
, ISNULL(CAST(5 as float),0) AS [DiscountAmount]
, [ProductStandardCost]
, [TotalProductCost]
, ISNULL(CAST(CASE WHEN [SalesAmount] <=5 THEN 0
ELSE [SalesAmount] - 5
END AS MONEY),0) AS [SalesAmount]
, [TaxAmt]
, [Freight]
, [CarrierTrackingNumber]
, [CustomerPONumber]
FROM [dbo].[FactInternetSales]
WHERE OrderDateKey BETWEEN 20020101 AND 20021231
OPTION (LABEL = 'CTAS : Partition Switch IN : UPDATE')
;
--Use the helper procedure to identify the partitions
--The source table
EXEC dbo.partition_data_get 'dbo','FactInternetSales',20030101
DECLARE @ptn_nmbr_src INT = (SELECT ptn_nmbr FROM #ptn_data)
SELECT @ptn_nmbr_src
--The "in" table
EXEC dbo.partition_data_get 'dbo','FactInternetSales_in',20030101
DECLARE @ptn_nmbr_in INT = (SELECT ptn_nmbr FROM #ptn_data)
SELECT @ptn_nmbr_in
--The "out" table
EXEC dbo.partition_data_get 'dbo','FactInternetSales_out',20030101
DECLARE @ptn_nmbr_out INT = (SELECT ptn_nmbr FROM #ptn_data)
SELECT @ptn_nmbr_out
--Switch the partitions over
DECLARE @SQL NVARCHAR(4000) = '
ALTER TABLE [dbo].[FactInternetSales] SWITCH PARTITION '+CAST(@ptn_nmbr_src AS VARCHAR(20)) +' TO [dbo].[FactInternetSales_out] PARTITION ' +CAST(@ptn_nmbr_out AS VARCHAR(20))+';
ALTER TABLE [dbo].[FactInternetSales_in] SWITCH PARTITION '+CAST(@ptn_nmbr_in AS VARCHAR(20)) +' TO [dbo].[FactInternetSales] PARTITION ' +CAST(@ptn_nmbr_src AS VARCHAR(20))+';'
EXEC sp_executesql @SQL
--Perform the clean-up
TRUNCATE TABLE dbo.FactInternetSales_out;
TRUNCATE TABLE dbo.FactInternetSales_in;
DROP TABLE dbo.FactInternetSales_out
DROP TABLE dbo.FactInternetSales_in
DROP TABLE #ptn_data
Meminimalkan pencatatan dengan kelompok kecil
Untuk operasi modifikasi data besar, mungkin masuk akal untuk membagi operasi menjadi gugus atau batch untuk mencakup unit kerja.
Kode berikut adalah contoh yang berfungsi. Ukuran batch telah diatur ke angka sepele untuk menyoroti teknik. Pada kenyataannya, ukuran batch akan jauh lebih besar.
SET NO_COUNT ON;
IF OBJECT_ID('tempdb..#t') IS NOT NULL
BEGIN
DROP TABLE #t;
PRINT '#t dropped';
END
CREATE TABLE #t
WITH ( DISTRIBUTION = ROUND_ROBIN
, HEAP
)
AS
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS seq_nmbr
, SalesOrderNumber
, SalesOrderLineNumber
FROM dbo.FactInternetSales
WHERE [OrderDateKey] BETWEEN 20010101 and 20011231
;
DECLARE @seq_start INT = 1
, @batch_iterator INT = 1
, @batch_size INT = 50
, @max_seq_nmbr INT = (SELECT MAX(seq_nmbr) FROM dbo.#t)
;
DECLARE @batch_count INT = (SELECT CEILING((@max_seq_nmbr*1.0)/@batch_size))
, @seq_end INT = @batch_size
;
SELECT COUNT(*)
FROM dbo.FactInternetSales f
PRINT 'MAX_seq_nmbr '+CAST(@max_seq_nmbr AS VARCHAR(20))
PRINT 'MAX_Batch_count '+CAST(@batch_count AS VARCHAR(20))
WHILE @batch_iterator <= @batch_count
BEGIN
DELETE
FROM dbo.FactInternetSales
WHERE EXISTS
(
SELECT 1
FROM #t t
WHERE seq_nmbr BETWEEN @seq_start AND @seq_end
AND FactInternetSales.SalesOrderNumber = t.SalesOrderNumber
AND FactInternetSales.SalesOrderLineNumber = t.SalesOrderLineNumber
)
;
SET @seq_start = @seq_end
SET @seq_end = (@seq_start+@batch_size);
SET @batch_iterator +=1;
END
Panduan jeda dan penskalaan
Kumpulan SQL khusus memungkinkan Anda menjeda, melanjutkan, dan menskalakan kumpulan SQL khusus sesuai permintaan. Ketika Anda menjeda atau menskalakan kumpulan SQL khusus Anda, penting untuk dipahami bahwa setiap transaksi dalam penerbangan segera dihentikan; menyebabkan transaksi terbuka digulung balik. Jika tugas sistem Anda telah memulai modifikasi data yang berlangsung lama dan belum selesai sebelum operasi jeda atau penyesuaian skala, maka pekerjaan ini perlu dibatalkan. Perubahan ini dapat berdampak pada waktu yang diperlukan untuk menjeda atau menskalakan kumpulan SQL khusus Anda.
Penting
Keduanya UPDATE dan DELETE merupakan operasi yang sepenuhnya dicatat dan sehingga operasi pembatalan/pengulangan ini dapat memakan waktu lebih lama secara signifikan daripada operasi yang dicatat secara minimal yang setara.
Skenario terbaik adalah membiarkan transaksi modifikasi data penerbangan selesai sebelum menjeda atau menskalakan kumpulan SQL khusus. Namun, skenario ini mungkin tidak selalu praktis. Untuk mengurangi risiko pembatalan yang memakan waktu lama, pertimbangkan salah satu opsi berikut:
- Menulis ulang operasi jangka panjang menggunakan CTAS
- Bagi operasi menjadi beberapa bagian dan operasikan pada subset baris.
Langkah berikutnya
Lihat Transaksi di kumpulan SQL khusus untuk mempelajari selengkapnya tentang tingkat isolasi dan batas transaksi. Untuk gambaran umum Praktik Terbaik lainnya, lihat Praktik terbaik kumpulan SQL Khusus.