Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Узнайте, как оптимизировать производительность транзакционного кода в выделенном пуле SQL, минимизируя риск долгих возвратов.
Транзакции и ведение журнала
Transactions are an important component of a relational SQL pool engine. Transactions are used during data modification. Эти транзакции могут быть явными или неявными. Single INSERT, UPDATE, and DELETE statements are all examples of implicit transactions. Явные транзакции используют BEGIN TRAN, COMMIT TRAN или ROLLBACK TRAN. Explicit transactions are typically used when multiple modification statements need to be tied together in a single atomic unit.
Changes to the SQL pool are tracked using transaction logs. Каждый дистрибутив имеет собственный журнал транзакций. Записи журналов транзакций являются автоматическими. Конфигурация не требуется. However, whilst this process guarantees the write it does introduce an overhead in the system. Это влияние можно свести к минимуму, написав эффективный код транзакций. Эффективная транзакционная обработка кода в целом делится на две категории.
- Использование минимальных конструкций ведения журнала по возможности
- Process data using scoped batches to avoid singular long running transactions
- Применяйте шаблон переключения разделов для крупных изменений в заданном разделе.
Minimal vs. full logging
Unlike fully logged operations, which use the transaction log to keep track of every row change, minimally logged operations keep track of extent allocations and meta-data changes only. Therefore, minimal logging involves logging only the information that is required to roll back the transaction after a failure, or for an explicit request (ROLLBACK TRAN). Так как в журнале транзакций отслеживается гораздо меньше сведений, минимально зарегистрированная операция выполняется лучше, чем операция полного ведения журнала. Furthermore, because fewer writes go the transaction log, a much smaller amount of log data is generated and so is more I/O efficient.
Ограничения безопасности транзакций применяются только к полностью зарегистрированным операциям.
Примечание.
Минимально зарегистрированные операции могут участвовать в явных транзакциях. As all changes in allocation structures are tracked, it is possible to roll back minimally logged operations.
Минимально зарегистрированные операции
Следующие операции могут быть минимально зарегистрированы в журнале:
- CREATE TABLE AS SELECT (CTAS)
- INSERT..SELECT
- СОЗДАТЬ ИНДЕКС
- ALTER INDEX REBUILD
- DROP INDEX
- TRUNCATE TABLE
- DROP TABLE
- ALTER TABLE SWITCH PARTITION
Примечание.
Внутренние операции перемещения данных (например, BROADCAST и SHUFFLE) не затрагиваются ограничением безопасности транзакций.
Minimal logging with bulk load
CTAS и INSERT... SELECT — это обе операции массовой загрузки. Однако оба влияют на определение целевой таблицы и зависят от сценария загрузки. В следующей таблице объясняется, когда массовые операции полностью или минимально регистрируются:
Основной индекс | Сценарий загрузки | Logging Mode |
---|---|---|
Heap | Any | Минимальный |
Кластеризованный индекс | Пустая целевая таблица | Минимальный |
Кластеризованный индекс | Loaded rows do not overlap with existing pages in target | Минимальный |
Кластеризованный индекс | Loaded rows overlap with existing pages in target | Full |
Clustered Columnstore Index | Batch size >= 102,400 per partition aligned distribution | Минимальный |
Clustered Columnstore Index | Batch size < 102,400 per partition aligned distribution | Full |
Следует отметить, что все операции записи для обновления вторичных или некластикционных индексов всегда будут полностью зарегистрированы.
Это важно
A dedicated SQL pool has 60 distributions. Therefore, assuming all rows are evenly distributed and landing in a single partition, your batch will need to contain 6,144,000 rows or larger to be minimally logged when writing to a Clustered Columnstore Index. Если таблица секционирована и строки, вставляемые, пересекают границы секционирования, то потребуется 6 144 000 строк на каждую границу секции при условии равномерного распределения данных. Each partition in each distribution must independently exceed the 102,400 row threshold for the insert to be minimally logged into the distribution.
Загрузка данных в непустую таблицу с кластеризованным индексом часто может содержать смесь полностью зарегистрированных и минимально записанных строк. Кластеризованный индекс — это сбалансированное дерево (b-дерево) страниц. Если страница, в которую вносятся изменения, уже содержит строки из другой транзакции, то эти записи будут полностью зафиксированы в журнале. However, if the page is empty then the write to that page will be minimally logged.
Optimizing deletes
DELETE — это полностью зарегистрированная операция. If you need to delete a large amount of data in a table or a partition, it often makes more sense to SELECT
the data you wish to keep, which can be run as a minimally logged operation. Чтобы выбрать данные, создайте новую таблицу с помощью CTAS. После создания используйте ПЕРЕИМЕНОВАТЬ, чтобы заменить старую таблицу на вновь созданную.
-- 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];
Optimizing updates
UPDATE — это полностью зарегистрированная операция. If you need to update a large number of rows in a table or a partition, it can often be far more efficient to use a minimally logged operation such as CTAS to do so.
В приведенном ниже примере полное обновление таблицы было преобразовано в CTAS, чтобы можно было выполнить минимальное ведение журнала.
В этом случае мы ретроспективно добавляем скидку к продажам в таблице:
--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]
Примечание.
Re-creating large tables can benefit from using dedicated SQL pool workload management features. Дополнительные сведения см. в разделе "Классы ресурсов" для управления рабочими нагрузками.
Optimizing with partition switching
If faced with large-scale modifications inside a table partition, then a partition switching pattern makes sense. Если изменение данных является значительным и охватывает несколько секций, то итерирование по секциям достигает одного и того же результата.
The steps to perform a partition switch are as follows:
- Создайте пустой раздел
- Perform the 'update' as a CTAS
- Switch out the existing data to the out table
- Switch in the new data
- Очистка данных
Тем не менее, чтобы определить разделы для переключения, создайте следующую вспомогательную процедуру.
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
Эта процедура позволяет повторно использовать код и сохраняет пример переключения секций более компактным.
В следующем коде показаны шаги, упомянутые ранее для достижения полной процедуры переключения секций.
--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
Minimize logging with small batches
Для операций изменения больших данных может потребоваться разделить операцию на блоки или пакеты, чтобы ограничить единицу работы.
Следующий код — это рабочий пример. The batch size has been set to a trivial number to highlight the technique. In reality, the batch size would be significantly larger.
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
Руководство по приостановке и масштабированию
Dedicated SQL pool lets you pause, resume, and scale your dedicated SQL pool on demand. When you pause or scale your dedicated SQL pool, it is important to understand that any in-flight transactions are terminated immediately; causing any open transactions to be rolled back. If your workload had issued a long running and incomplete data modification prior to the pause or scale operation, then this work will need to be undone. This undoing might impact the time it takes to pause or scale your dedicated SQL pool.
Это важно
Операции UPDATE
и DELETE
являются полностью логгированными, поэтому операции отмены и повтора могут занять значительно больше времени, чем эквивалентные минимально логгированные операции.
The best scenario is to let in flight data modification transactions complete prior to pausing or scaling a dedicated SQL pool. Однако этот сценарий может не всегда быть практическим. To mitigate the risk of a long rollback, consider one of the following options:
- Rewrite long running operations using CTAS
- Разорвать операцию на блоки; работа с подмножеством строк
Дальнейшие действия
См. Транзакции в выделенном пуле SQL, чтобы узнать больше об уровнях изоляции и ограничениях транзакций. For an overview of other Best Practices, see Dedicated SQL pool best practices.