Transacties optimaliseren in toegewezen SQL-pool in Azure Synapse Analytics

Meer informatie over het optimaliseren van de prestaties van uw transactionele code in een toegewezen SQL-pool terwijl het risico op lange terugdraaiacties wordt geminimaliseerd.

Transacties en logboekregistratie

Transacties zijn een belangrijk onderdeel van een relationele SQL-poolengine. Transacties worden gebruikt tijdens het wijzigen van gegevens. Deze transacties kunnen expliciet of impliciet zijn. Enkele INSERT-, UPDATE- en DELETE-instructies zijn allemaal voorbeelden van impliciete transacties. Expliciete transacties maken gebruik van BEGIN TRAN, COMMIT TRAN of ROLLBACK TRAN. Expliciete transacties worden doorgaans gebruikt wanneer meerdere wijzigingsinstructies aan elkaar moeten worden gekoppeld in één atomische eenheid.

Wijzigingen in de SQL-pool worden bijgehouden met behulp van transactielogboeken. Elke distributie heeft een eigen transactielogboek. Schrijfbewerkingen in transactielogboeken worden automatisch uitgevoerd. Er is geen configuratie vereist. Hoewel dit proces de schrijfbewerking garandeert, leidt het wel tot overhead in het systeem. U kunt deze impact minimaliseren door transactioneel efficiënte code te schrijven. Transactioneel efficiënte code valt in grote lijnen in twee categorieën.

  • Gebruik waar mogelijk minimale logboekconstructies
  • Gegevens verwerken met behulp van scoped batches om langlopende transacties te voorkomen
  • Een partitiewisselpatroon gebruiken voor grote wijzigingen in een bepaalde partitie

Minimale versus volledige logboekregistratie

In tegenstelling tot volledig geregistreerde bewerkingen, die het transactielogboek gebruiken om elke rijwijziging bij te houden, houden minimaal geregistreerde bewerkingen alleen omvangtoewijzingen en metagegevenswijzigingen bij. Daarom omvat minimale logboekregistratie alleen de informatie die nodig is om de transactie terug te draaien na een fout of voor een expliciete aanvraag (ROLLBACK TRAN). Omdat er veel minder informatie wordt bijgehouden in het transactielogboek, presteert een minimaal geregistreerde bewerking beter dan een volledig geregistreerde bewerking van vergelijkbare grootte. Omdat er minder schrijfbewerkingen naar het transactielogboek gaan, wordt bovendien een veel kleinere hoeveelheid logboekgegevens gegenereerd en is de I/O-efficiëntie dus efficiënter.

De veiligheidslimieten voor transacties zijn alleen van toepassing op volledig geregistreerde bewerkingen.

Notitie

Minimaal geregistreerde bewerkingen kunnen deelnemen aan expliciete transacties. Omdat alle wijzigingen in toewijzingsstructuren worden bijgehouden, is het mogelijk om minimaal vastgelegde bewerkingen terug te draaien.

Minimaal geregistreerde bewerkingen

De volgende bewerkingen kunnen minimaal worden geregistreerd:

  • CREATE TABLE AS SELECT (CTAS)
  • INVOEGEN.. SELECTEER
  • CREATE INDEX
  • ALTER INDEX REBUILD
  • DROP INDEX
  • TRUNCATE TABLE
  • DROP TABLE
  • ALTER TABLE SWITCH PARTITION

Notitie

Interne gegevensverplaatsingsbewerkingen (zoals BROADCAST en SHUFFLE) worden niet beïnvloed door de veiligheidslimiet voor transacties.

Minimale logboekregistratie met bulksgewijs laden

CTAS en INSERT... SELECT zijn beide bewerkingen voor bulksgewijs laden. Beide worden echter beïnvloed door de definitie van de doeltabel en zijn afhankelijk van het laadscenario. In de volgende tabel wordt uitgelegd wanneer bulkbewerkingen volledig of minimaal worden geregistreerd:

Primaire index Scenario laden Modus voor logboekregistratie
Heap Alle Minimaal
Geclusterde index Lege doeltabel Minimaal
Geclusterde index Geladen rijen overlappen niet met bestaande pagina's in het doel Minimaal
Geclusterde index Geladen rijen overlappen met bestaande pagina's in het doel Volledig
Geclusterde columnstore-index Batchgrootte >= 102.400 per uitgelijnde partitieverdeling Minimaal
Geclusterde columnstore-index Batchgrootte < 102.400 per partitie uitgelijnde distributie Volledig

Het is vermeldenswaardig dat schrijfbewerkingen voor het bijwerken van secundaire of niet-geclusterde indexen altijd volledig geregistreerde bewerkingen zijn.

Belangrijk

Een toegewezen SQL-pool heeft 60 distributies. Daarom moet uw batch 6.144.000 rijen of groter bevatten om minimaal te kunnen worden geregistreerd wanneer u naar een geclusterde columnstore-index schrijft. Als de tabel is gepartitioneerd en de rijen die worden ingevoegd partitiegrenzen omvatten, hebt u 6.144.000 rijen per partitiegrens nodig, uitgaande van een gelijkmatige gegevensdistributie. Elke partitie in elke distributie moet onafhankelijk de drempelwaarde van 102.400 rijen overschrijden om de insert minimaal te laten aanmelden bij de distributie.

Het laden van gegevens in een niet-lege tabel met een geclusterde index kan vaak een combinatie van volledig geregistreerde en minimaal geregistreerde rijen bevatten. Een geclusterde index is een evenwichtige structuur (b-structuur) van pagina's. Als de pagina naar wordt geschreven al rijen van een andere transactie bevat, worden deze schrijfbewerkingen volledig geregistreerd. Als de pagina echter leeg is, wordt de schrijfbewerking naar die pagina minimaal geregistreerd.

Verwijderingen optimaliseren

DELETE is een volledig geregistreerde bewerking. Als u een grote hoeveelheid gegevens in een tabel of partitie wilt verwijderen, is het vaak logischer om SELECT de gegevens die u wilt behouden, die kunnen worden uitgevoerd als een minimaal vastgelegde bewerking. Als u de gegevens wilt selecteren, maakt u een nieuwe tabel met CTAS. Nadat u de tabel hebt gemaakt, gebruikt u NAAM WIJZIGEN om de oude tabel te vervangen door de zojuist gemaakte tabel.

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

Updates optimaliseren

UPDATE is een volledig geregistreerde bewerking. Als u een groot aantal rijen in een tabel of partitie moet bijwerken, kan het vaak veel efficiënter zijn om een minimaal vastgelegde bewerking zoals CTAS te gebruiken om dit te doen.

In het onderstaande voorbeeld is een volledige tabelupdate geconverteerd naar een CTAS, zodat een minimale logboekregistratie mogelijk is.

In dit geval voegen we met terugwerkende kracht een kortingsbedrag toe aan de verkoop in de 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]

Notitie

Het opnieuw maken van grote tabellen kan baat hebben bij het gebruik van toegewezen workloadbeheerfuncties voor SQL-pools. Zie Resourceklassen voor workloadbeheer voor meer informatie.

Optimaliseren met partitiewisseling

Als u te maken krijgt met grootschalige wijzigingen in een tabelpartitie, is een patroon voor partitiewisseling logisch. Als de gegevenswijziging significant is en meerdere partities omvat, krijgt het herhalen van de partities hetzelfde resultaat.

De stappen voor het uitvoeren van een partitieswitch zijn als volgt:

  1. Een lege partitie maken
  2. De 'update' uitvoeren als een CTAS
  3. De bestaande gegevens naar de out-tabel verplaatsen
  4. Schakelen tussen de nieuwe gegevens
  5. De gegevens opschonen

Als u echter wilt helpen bij het identificeren van de partities om over te schakelen, maakt u de volgende helperprocedure.

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

Met deze procedure wordt het hergebruik van code gemaximaliseerd en blijft het voorbeeld van partitiewisseling compacter.

De volgende code demonstreert de eerder genoemde stappen om een volledige partitiewisselroutine te bereiken.

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

Logboekregistratie minimaliseren met kleine batches

Voor bewerkingen voor het wijzigen van grote gegevens kan het zinvol zijn om de bewerking op te delen in segmenten of batches om het bereik van de werkeenheid te bepalen.

De volgende code is een werkend voorbeeld. De batchgrootte is ingesteld op een klein getal om de techniek te markeren. In werkelijkheid zou de batchgrootte aanzienlijk groter zijn.

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

Richtlijnen voor onderbreken en schalen

Met een toegewezen SQL-pool kunt u uw toegewezen SQL-pool op aanvraag onderbreken, hervatten en schalen . Wanneer u uw toegewezen SQL-pool onderbreekt of schaalt, is het belangrijk om te begrijpen dat alle in-flight transacties onmiddellijk worden beëindigd. waardoor alle openstaande transacties worden teruggedraaid. Als uw workload een langdurige en onvolledige gegevenswijziging heeft uitgevoerd vóór de onderbrekings- of schaalbewerking, moet dit werk ongedaan worden gemaakt. Dit ongedaan maken kan van invloed zijn op de tijd die nodig is om uw toegewezen SQL-pool te onderbreken of te schalen.

Belangrijk

Zowel UPDATE als DELETE zijn volledig geregistreerde bewerkingen, waardoor deze bewerkingen voor ongedaan maken/opnieuw uitvoeren aanzienlijk langer kunnen duren dan equivalente minimaal vastgelegde bewerkingen.

Het beste scenario is om vluchtgegevenswijzigingstransacties te laten voltooien voordat een toegewezen SQL-pool wordt onderbroken of geschaald. Dit scenario is echter niet altijd praktisch. Als u het risico van een lange terugdraaiactie wilt beperken, kunt u een van de volgende opties overwegen:

  • Langlopende bewerkingen herschrijven met CTAS
  • De bewerking opsplitsen in segmenten; werken op een subset van de rijen

Volgende stappen

Zie Transacties in toegewezen SQL-pool voor meer informatie over isolatieniveaus en transactionele limieten. Zie Aanbevolen procedures voor toegewezen SQL-pools voor een overzicht van andere best practices.