Bagikan melalui


Partisi tabel dalam SQL pool yang didedikasikan

Rekomendasi dan contoh untuk menggunakan partisi tabel di kumpulan SQL khusus.

Apa itu partisi tabel?

Partisi tabel memungkinkan Anda membagi data menjadi grup data yang lebih kecil. Dalam kebanyakan kasus, partisi tabel dibuat pada kolom tanggal. Partisi didukung di semua jenis tabel kumpulan SQL khusus; termasuk penyimpan kolom berkluster, indeks berkluster, dan tumpukan. Partisi juga didukung untuk semua jenis distribusi, termasuk distribusi hash atau round robin.

Pemartisian dapat menguntungkan pemeliharaan data dan performa kueri. Apakah hal ini menguntungkan keduanya atau hanya satu tergantung pada bagaimana data dimuat dan apakah kolom yang sama dapat digunakan untuk kedua tujuan itu, karena partisi hanya dapat dilakukan pada satu kolom.

Manfaat bagi beban

Keuntungan utama partisi dalam kumpulan SQL khusus adalah untuk meningkatkan efisiensi dan performa pemuatan data menggunakan penghapusan, pengalihan, dan penggabungan partisi. Dalam kebanyakan kasus, data dipartisi pada kolom tanggal yang terkait erat dengan urutan data dimuat ke dalam kumpulan SQL. Salah satu keuntungan terbesar menggunakan partisi untuk mempertahankan data adalah dapat menghindarkan dari pencatatan transaksi. Meskipun menyisipkan, memperbarui, atau menghapus data saja dapat menjadi pendekatan yang paling mudah, dengan sedikit pemikiran dan upaya, menggunakan partisi selama proses pemuatan Anda dapat meningkatkan performa secara signifikan.

Pengalihan partisi dapat digunakan untuk menghapus atau mengganti bagian tabel dengan cepat. Misalnya, tabel fakta penjualan mungkin hanya berisi data selama 36 bulan terakhir. Pada akhir setiap bulan, bulan terlama data penjualan dihapus dari tabel. Data ini dapat dihapus menggunakan perintah hapus untuk menghapus data dari bulan terlama.

Namun, menghapus sejumlah besar data baris demi baris dengan pernyataan penghapusan dapat memakan waktu terlalu banyak, dan membuat risiko transaksi besar yang membutuhkan waktu lama untuk memutar kembali jika ada yang salah. Pendekatan yang lebih optimal adalah menghilangkan partisi data terlama. Meskipun menghapus setiap baris bisa memakan waktu berjam-jam, menghapus seluruh partisi bisa memakan waktu beberapa detik.

Keuntungan dari kueri

Partisi juga dapat digunakan untuk meningkatkan performa kueri. Kueri yang menerapkan filter ke data yang dipartisi dapat membatasi pemindaian hanya ke partisi yang memenuhi syarat. Metode pemfilteran ini dapat menghindari pemindaian tabel penuh dan hanya memindai subset data yang lebih kecil. Dengan diperkenalkannya indeks penyimpanan kolom berkluster, performa eliminasi predikat kurang bermanfaat, tetapi dalam beberapa kasus dapat memberikan keuntungan untuk kueri.

Misalnya, jika tabel fakta penjualan dipartisi menjadi 36 bulan menggunakan bidang tanggal penjualan, maka kueri yang memfilter pada tanggal penjualan dapat melewati pencarian di partisi yang tidak cocok dengan filter.

Ukuran partisi

Sementara partisi dapat digunakan untuk meningkatkan performa beberapa skenario, membuat tabel dengan terlalu banyak partisi dapat memperburuk performa dalam beberapa situasi. Kekhawatiran ini terutama berlaku untuk tabel penyimpanan kolom berkluster.

Agar partisi bermanfaat, penting untuk memahami kapan harus menggunakan partisi dan jumlah partisi yang akan dibuat. Tidak ada aturan cepat yang keras tentang berapa banyak partisi yang terlalu banyak, itu tergantung pada data Anda dan berapa banyak partisi yang Anda muat secara bersamaan. Skema partisi yang sukses biasanya memiliki puluhan hingga ratusan partisi, bukan ribuan.

Saat membuat partisi pada tabel penyimpan kolom berkluster, penting untuk mempertimbangkan berapa banyak baris yang termasuk dalam setiap partisi. Untuk kompresi optimal dan performa tabel penyimpanan kolom berkluster, diperlukan minimal 1 juta baris per distribusi dan partisi. Sebelum partisi dibuat, kumpulan SQL khusus sudah membagi setiap tabel menjadi 60 distribusi.

Partisi apa pun yang ditambahkan ke tabel adalah tambahan untuk distribusi yang dibuat di belakang layar. Dengan menggunakan contoh ini, jika tabel fakta penjualan berisi 36 partisi bulanan, dan mengingat bahwa kolam SQL khusus memiliki 60 distribusi, maka tabel fakta penjualan harus berisi 60 juta baris per bulan, atau 2,1 miliar baris ketika semua bulan dihuni. Jika tabel berisi kurang dari jumlah baris minimum yang disarankan per partisi, pertimbangkan untuk menggunakan lebih sedikit partisi guna meningkatkan jumlah baris per partisi.

Untuk informasi selengkapnya, lihat artikel Pengindeksan, yang menyertakan kueri yang dapat menilai kualitas indeks penyimpanan kolom berkluster.

Perbedaan sintaks dari SQL Server

Kumpulan SQL khusus memperkenalkan cara untuk mendefinisikan partisi yang lebih sederhana dari SQL Server. Fungsi dan skema partisi tidak digunakan dalam kumpulan SQL khusus karena berada di SQL Server. Sebaliknya, yang perlu Anda lakukan adalah mengidentifikasi kolom yang dipartisi dan titik batas.

Meskipun sintaks partisi mungkin sedikit berbeda dari SQL Server, konsep dasarnya sama. SQL Server dan kumpulan SQL khusus mendukung satu kolom partisi per tabel, yang dapat berupa partisi rentang. Untuk mempelajari selengkapnya tentang partisi, lihat Tabel dan Indeks Yang Dipartisi.

Contoh berikut menggunakan pernyataan CREATE TABLE untuk mempartisi tabel FactInternetSales pada kolom OrderDateKey:

CREATE TABLE [dbo].[FactInternetSales]
(
    [ProductKey]            int          NOT NULL
,   [OrderDateKey]          int          NOT NULL
,   [CustomerKey]           int          NOT NULL
,   [PromotionKey]          int          NOT NULL
,   [SalesOrderNumber]      nvarchar(20) NOT NULL
,   [OrderQuantity]         smallint     NOT NULL
,   [UnitPrice]             money        NOT NULL
,   [SalesAmount]           money        NOT NULL
)
WITH
(   CLUSTERED COLUMNSTORE INDEX
,   DISTRIBUTION = HASH([ProductKey])
,   PARTITION   (   [OrderDateKey] RANGE RIGHT FOR VALUES
                    (20000101,20010101,20020101
                    ,20030101,20040101,20050101
                    )
                )
);

Migrasi partisi dari SQL Server

Untuk memigrasikan definisi partisi SQL Server ke kumpulan SQL khusus, cukup:

Jika Anda memigrasikan tabel yang dipartisi dari instans SQL Server, SQL berikut ini dapat membantu Anda mengetahui jumlah baris di setiap partisi. Perlu diingat bahwa jika granularitas partisi yang sama digunakan dalam kumpulan SQL khusus, jumlah baris per partisi berkurang dengan faktor 60.

-- Partition information for a SQL Server Database
SELECT      s.[name]                        AS      [schema_name]
,           t.[name]                        AS      [table_name]
,           i.[name]                        AS      [index_name]
,           p.[partition_number]            AS      [partition_number]
,           SUM(a.[used_pages]*8.0)         AS      [partition_size_kb]
,           SUM(a.[used_pages]*8.0)/1024    AS      [partition_size_mb]
,           SUM(a.[used_pages]*8.0)/1048576 AS      [partition_size_gb]
,           p.[rows]                        AS      [partition_row_count]
,           rv.[value]                      AS      [partition_boundary_value]
,           p.[data_compression_desc]       AS      [partition_compression_desc]
FROM        sys.schemas s
JOIN        sys.tables t                    ON      t.[schema_id]         = s.[schema_id]
JOIN        sys.partitions p                ON      p.[object_id]         = t.[object_id]
JOIN        sys.allocation_units a          ON      a.[container_id]      = p.[partition_id]
JOIN        sys.indexes i                   ON      i.[object_id]         = p.[object_id]
                                            AND     i.[index_id]          = p.[index_id]
JOIN        sys.data_spaces ds              ON      ds.[data_space_id]    = i.[data_space_id]
LEFT JOIN   sys.partition_schemes ps        ON      ps.[data_space_id]    = ds.[data_space_id]
LEFT JOIN   sys.partition_functions pf      ON      pf.[function_id]      = ps.[function_id]
LEFT JOIN   sys.partition_range_values rv   ON      rv.[function_id]      = pf.[function_id]
                                            AND     rv.[boundary_id]      = p.[partition_number]
WHERE       p.[index_id] <=1
GROUP BY    s.[name]
,           t.[name]
,           i.[name]
,           p.[partition_number]
,           p.[rows]
,           rv.[value]
,           p.[data_compression_desc];

Peralihan partisi

Kumpulan SQL khusus mendukung pemisahan, penggabungan, dan peralihan partisi. Masing-masing fungsi ini dijalankan menggunakan pernyataan ALTER TABLE.

Untuk beralih partisi di antara dua tabel, Anda harus memastikan bahwa partisi sejajar pada batas masing-masing dan definisi tabel cocok. Karena batasan pemeriksaan tidak tersedia untuk memberlakukan rentang nilai dalam tabel, tabel sumber harus berisi batas partisi yang sama dengan tabel target. Jika batas partisi tidak sama, maka sakelar partisi akan gagal karena metadata partisi tidak akan disinkronkan.

Pembagian partisi membutuhkan agar partisi yang bersangkutan kosong jika tabel memiliki indeks columnstore terkelompok (CCI). Partisi lain dalam tabel yang sama dapat berisi data. Partisi yang berisi data tidak dapat dipisahkan, itu akan mengakibatkan kesalahan: ALTER PARTITION statement failed because the partition is not empty. Only empty partitions can be split in when a columnstore index exists on the table. Consider disabling the columnstore index before issuing the ALTER PARTITION statement, then rebuilding the columnstore index after ALTER PARTITION is complete. Sebagai solusi untuk membagi partisi yang berisi data, lihat Cara membagi partisi yang berisi data.

Cara membagi partisi yang berisi data

Metode paling efisien untuk membagi partisi yang sudah berisi data adalah dengan menggunakan pernyataan CTAS. Jika tabel yang dipartisi adalah penyimpanan kolom berkluster, maka partisi tabel harus kosong sebelum dapat dipisahkan.

Contoh berikut membuat tabel penyimpanan kolom yang dipartisi. Satu baris disisipkan ke dalam setiap partisi:

CREATE TABLE [dbo].[FactInternetSales]
(
        [ProductKey]            int          NOT NULL
    ,   [OrderDateKey]          int          NOT NULL
    ,   [CustomerKey]           int          NOT NULL
    ,   [PromotionKey]          int          NOT NULL
    ,   [SalesOrderNumber]      nvarchar(20) NOT NULL
    ,   [OrderQuantity]         smallint     NOT NULL
    ,   [UnitPrice]             money        NOT NULL
    ,   [SalesAmount]           money        NOT NULL
)
WITH
(   CLUSTERED COLUMNSTORE INDEX
,   DISTRIBUTION = HASH([ProductKey])
,   PARTITION   (   [OrderDateKey] RANGE RIGHT FOR VALUES
                    (20000101
                    )
                )
);

INSERT INTO dbo.FactInternetSales
VALUES (1,19990101,1,1,1,1,1,1);

INSERT INTO dbo.FactInternetSales
VALUES (1,20000101,1,1,1,1,1,1);

Kueri berikut menemukan jumlah baris menggunakan tampilan katalog sys.partitions:

SELECT  QUOTENAME(s.[name])+'.'+QUOTENAME(t.[name]) as Table_name
,       i.[name] as Index_name
,       p.partition_number as Partition_nmbr
,       p.[rows] as Row_count
,       p.[data_compression_desc] as Data_Compression_desc
FROM    sys.partitions p
JOIN    sys.tables     t    ON    p.[object_id]   = t.[object_id]
JOIN    sys.schemas    s    ON    t.[schema_id]   = s.[schema_id]
JOIN    sys.indexes    i    ON    p.[object_id]   = i.[object_Id]
                            AND   p.[index_Id]    = i.[index_Id]
WHERE t.[name] = 'FactInternetSales';

Perintah pemisahan berikut menerima pesan kesalahan:

ALTER TABLE FactInternetSales SPLIT RANGE (20010101);
Msg 35346, Level 15, State 1, Line 44
SPLIT clause of ALTER PARTITION statement failed because the partition is not empty. Only empty partitions can be split in when a columnstore index exists on the table. Consider disabling the columnstore index before issuing the ALTER PARTITION statement, then rebuilding the columnstore index after ALTER PARTITION is complete.

Namun, Anda bisa menggunakan CTAS untuk membuat tabel baru untuk menyimpan data.

CREATE TABLE dbo.FactInternetSales_20000101
    WITH    (   DISTRIBUTION = HASH(ProductKey)
            ,   CLUSTERED COLUMNSTORE INDEX              
            ,   PARTITION   (   [OrderDateKey] RANGE RIGHT FOR VALUES
                                (20000101
                                )
                            )
)
AS
SELECT *
FROM    FactInternetSales
WHERE   1=2;

Ketika batas partisi sudah selaras, peralihan diizinkan. Hal ini akan menjadikan tabel sumber dengan partisi kosong yang kemudian dapat Anda pisahkan.

ALTER TABLE FactInternetSales SWITCH PARTITION 2 TO FactInternetSales_20000101 PARTITION 2;

ALTER TABLE FactInternetSales SPLIT RANGE (20010101);

Yang tersisa hanyalah menyelaraskan data ke batas partisi baru menggunakan CTAS, lalu mengalihkan data kembali ke tabel utama.

CREATE TABLE [dbo].[FactInternetSales_20000101_20010101]
    WITH    (   DISTRIBUTION = HASH([ProductKey])
            ,   CLUSTERED COLUMNSTORE INDEX
            ,   PARTITION   (   [OrderDateKey] RANGE RIGHT FOR VALUES
                                (20000101,20010101
                                )
                            )
            )
AS
SELECT  *
FROM    [dbo].[FactInternetSales_20000101]
WHERE   [OrderDateKey] >= 20000101
AND     [OrderDateKey] <  20010101;

ALTER TABLE dbo.FactInternetSales_20000101_20010101 SWITCH PARTITION 2 TO dbo.FactInternetSales PARTITION 2;

Setelah Anda menyelesaikan pemindahan data, sebaiknya segarkan kembali statistik pada tabel target. Memperbarui statistik memastikan bahwa statistik secara akurat mencerminkan distribusi data baru di partisi masing-masing.

UPDATE STATISTICS [dbo].[FactInternetSales];

Akhirnya, dalam kasus satu kali penggunaan sakelar partisi untuk memindahkan data, Anda dapat menghapus tabel yang dibuat untuk sakelar partisi, FactInternetSales_20000101_20010101 dan FactInternetSales_20000101. Atau, Anda mungkin ingin menyimpan tabel kosong untuk pergantian partisi secara otomatisasi yang teratur.

Muat data baru ke dalam partisi yang berisi data dalam satu langkah

Memuat data ke dalam partisi dengan pengalihan partisi adalah cara mudah untuk menggelar data baru dalam tabel yang tidak terlihat oleh pengguna. Dapat menjadi tantangan pada sistem yang sibuk untuk menangani kontensi penguncian yang terkait dengan perpindahan partisi.

Untuk menghapus data yang ada dalam partisi, dulu ALTER TABLE diperlukan untuk menggantikan data. Kemudian, ALTER TABLE yang lain diharuskan untuk mengganti data dengan yang baru.

Di kumpulan SQL khusus, opsi TRUNCATE_TARGET didukung dalam perintah ALTER TABLE. Dengan TRUNCATE_TARGET, perintah ALTER TABLE menimpa data yang ada di partisi dengan data baru. Di bawah ini adalah contoh yang menggunakan CTAS untuk membuat tabel baru dengan data yang sudah ada, menyisipkan data baru, lalu memindahkan semua data kembali ke tabel target, menggantikan data yang sudah ada.

CREATE TABLE [dbo].[FactInternetSales_NewSales]
    WITH    (   DISTRIBUTION = HASH([ProductKey])
            ,   CLUSTERED COLUMNSTORE INDEX
            ,   PARTITION   (   [OrderDateKey] RANGE RIGHT FOR VALUES
                                (20000101,20010101
                                )
                            )
            )
AS
SELECT  *
FROM    [dbo].[FactInternetSales]
WHERE   [OrderDateKey] >= 20000101
AND     [OrderDateKey] <  20010101
;

INSERT INTO dbo.FactInternetSales_NewSales
VALUES (1,20000101,2,2,2,2,2,2);

ALTER TABLE dbo.FactInternetSales_NewSales SWITCH PARTITION 2 TO dbo.FactInternetSales PARTITION 2 WITH (TRUNCATE_TARGET = ON);  

Kontrol sumber partisi tabel

Catatan

Jika alat kontrol sumber Anda tidak dikonfigurasi untuk mengabaikan skema partisi, mengubah skema tabel untuk memperbarui partisi dapat menyebabkan tabel dihilangkan dan dibuat ulang sebagai bagian dari penyebaran, yang mungkin tidak layak. Solusi khusus untuk menerapkan perubahan seperti itu, seperti yang dijelaskan di bawah ini, mungkin diperlukan. Periksa apakah alat integrasi/penyebaran berkelanjutan (CI/CD) Anda memungkinkan untuk ini. Di SQL Server Data Tools (SSDT), cari Pengaturan Penerbitan Tingkat Lanjut "Abaikan skema partisi" untuk menghindari skrip yang dihasilkan yang menyebabkan tabel dihilangkan dan dibuat ulang.

Contoh ini berguna saat memperbarui skema partisi tabel kosong. Untuk terus menerapkan perubahan partisi pada tabel dengan data, ikuti langkah-langkah dalam Cara membagi partisi yang berisi data bersamaan dengan pengiriman, untuk memindahkan data secara sementara dari setiap partisi sebelum menerapkan SPLIT RANGE pada partisi. Hal ini diperlukan karena alat CI/CD tidak menyadari partisi mana yang memiliki data.

Untuk menghindarkan definisi tabel Anda dari berkarat di sistem kontrol sumber, Anda mungkin ingin mempertimbangkan pendekatan berikut:

  1. Buat tabel sebagai tabel yang dipartisi tetapi tanpa nilai partisi

    CREATE TABLE [dbo].[FactInternetSales]
    (
        [ProductKey]            int          NOT NULL
    ,   [OrderDateKey]          int          NOT NULL
    ,   [CustomerKey]           int          NOT NULL
    ,   [PromotionKey]          int          NOT NULL
    ,   [SalesOrderNumber]      nvarchar(20) NOT NULL
    ,   [OrderQuantity]         smallint     NOT NULL
    ,   [UnitPrice]             money        NOT NULL
    ,   [SalesAmount]           money        NOT NULL
    )
    WITH
    (   CLUSTERED COLUMNSTORE INDEX
    ,   DISTRIBUTION = HASH([ProductKey])
    ,   PARTITION   (   [OrderDateKey] RANGE RIGHT FOR VALUES () )
    );
    
  2. SPLIT tabel sebagai bagian dari proses penyebaran:

     -- Create a table containing the partition boundaries
    
    CREATE TABLE #partitions
    WITH
    (
        LOCATION = USER_DB
    ,   DISTRIBUTION = HASH(ptn_no)
    )
    AS
    SELECT  ptn_no
    ,       ROW_NUMBER() OVER (ORDER BY (ptn_no)) as seq_no
    FROM    (
        SELECT CAST(20000101 AS INT) ptn_no
        UNION ALL
        SELECT CAST(20010101 AS INT)
        UNION ALL
        SELECT CAST(20020101 AS INT)
        UNION ALL
        SELECT CAST(20030101 AS INT)
        UNION ALL
        SELECT CAST(20040101 AS INT)
    ) a;
    
     -- Iterate over the partition boundaries and split the table
    
    DECLARE @c INT = (SELECT COUNT(*) FROM #partitions)
    ,       @i INT = 1                                 --iterator for while loop
    ,       @q NVARCHAR(4000)                          --query
    ,       @p NVARCHAR(20)     = N''                  --partition_number
    ,       @s NVARCHAR(128)    = N'dbo'               --schema
    ,       @t NVARCHAR(128)    = N'FactInternetSales' --table;
    
    WHILE @i <= @c
    BEGIN
        SET @p = (SELECT ptn_no FROM #partitions WHERE seq_no = @i);
        SET @q = (SELECT N'ALTER TABLE '+@s+N'.'+@t+N' SPLIT RANGE ('+@p+N');');
    
        -- PRINT @q;
        EXECUTE sp_executesql @q;
        SET @i+=1;
    END
    
     -- Code clean-up
    
    DROP TABLE #partitions;
    

Dengan pendekatan ini, kode dalam kontrol sumber tetap statis dan nilai batas partisi diizinkan untuk menjadi dinamis; berkembang dengan kolam SQL dari waktu ke waktu.

Untuk informasi selengkapnya tentang mengembangkan tabel, lihat Gambaran Umum Tabel.