Mengapa sekuens Denali seharusnya berkinerja lebih baik daripada kolom identitas?

36

Dalam jawabannya untuk Mana yang lebih baik: kolom identitas atau menghasilkan nilai id unik? mrdenny berkata:

Ketika SQL Denali keluar, itu akan mendukung urutan yang akan lebih efisien daripada identitas, tetapi Anda tidak dapat membuat sesuatu yang lebih efisien sendiri.

Saya tidak yakin. Mengetahui urutan Oracle , saya harus membuat pemicu untuk memasukkan, merangkum setiap insert ke dalam panggilan prosedur tersimpan, atau berdoa agar saya tidak lupa untuk menggunakan urutan dengan benar ketika saya melakukan insert ad-hoc.

Saya ragu bahwa keuntungan dari urutan sangat jelas.

bernd_k
sumber
2
Saya tahu itu tidak menjawab pertanyaan Anda tetapi selain dari perbedaan kinerja, urutan memiliki keuntungan lain. Misalnya, urutan tidak menghentikan Anda memperbarui kolom target, yang merupakan batasan IDENTITY yang sangat tidak nyaman.
nvogel

Jawaban:

37

Saya akan menjawab di sini juga. Itu ada hubungannya dengan internal tentang bagaimana IDENTITYdan SEQUENCEbekerja.

Dengan IDENTITY, SQL Server pre-cache nilai ke dalam memori sehingga mereka tersedia. Lihat jawaban Martin Smith untuk detailnya. Saat nilai digunakan, proses latar belakang menghasilkan lebih banyak nilai. Seperti yang dapat Anda bayangkan, kumpulan ini dapat habis dengan cepat, meninggalkan aplikasi karena proses latar belakang yang menghasilkan nilai.

Dengan SEQUENCE, SQL Server memungkinkan Anda menentukan seberapa besar cache seharusnya. Meskipun SQL Server tidak benar-benar menyimpan nilai dalam cache, itu hanya menyimpan nilai saat ini dan nilai akhir atas, ini akan sangat mengurangi jumlah IO yang diperlukan untuk membuat nilai.

Jangan mengatur cache terlalu tinggi, karena ini akan mengurangi jumlah angka yang dapat digunakan: jika SQL Server crash, nilai apa pun yang ditentukan dalam kisaran cache saat ini yang tidak digunakan akan hilang.

Adapun penyisipan baris, cukup tentukan nilai default untuk kolom, seperti:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),
mrdenny
sumber
21

Sejak artikel Itzik Ben Gan ditulis, ukuran cache 10 hardcoded IDENTITYtampaknya telah diubah. Dari komentar di item hubungkan ini

Ukuran pra-alokasi didasarkan pada ukuran tipe data dari kolom properti identitas didefinisikan. Untuk kolom integer SQL Server, server pra-mengalokasikan identitas dalam rentang 1000 nilai. Untuk tipe data bigint, server mengalokasikan sebelumnya dalam rentang nilai 10.000.

Buku T-SQL Querying berisi tabel berikut tetapi menekankan bahwa nilai-nilai ini tidak didokumentasikan atau dijamin tidak berubah.

+-----------------+-----------+
|    DataType     | CacheSize |
+-----------------+-----------+
| TinyInt         | 10        |
| SmallInt        | 100       |
| Int             | 1,000     |
| BigInt, Numeric | 10,000    |
+-----------------+-----------+

Artikel di sini menguji berbagai ukuran cache urutan dan memasukkan ukuran kumpulan dan muncul dengan hasil berikut.

masukkan deskripsi gambar di sini

Yang tampaknya menunjukkan bahwa untuk sisipan besar IDENTITYberkinerja SEQUENCE. Namun ia tidak menguji ukuran cache 1.000 dan juga hasil tersebut hanya satu tes. Melihat secara khusus pada ukuran cache 1.000 dengan berbagai ukuran bets sisipan, saya mendapatkan hasil berikut (mencoba setiap ukuran bets 50 kali dan menggabungkan hasilnya sebagai di bawah ini - semua kali dalam μs.)

+------------+-----------+-----------+-----------+-----------+-----------+-----------+
|            |             Sequence              |             Identity              |
| Batch Size |    Min    |    Max    |    Avg    |    Min    |    Max    |    Avg    |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+
| 10         | 2,994     | 7,004     | 4,002     | 3,001     | 7,005     | 4,022     |
| 100        | 3,997     | 5,005     | 4,218     | 4,001     | 5,010     | 4,238     |
| 1,000      | 6,001     | 19,013    | 7,221     | 5,982     | 8,006     | 6,709     |
| 10,000     | 26,999    | 33,022    | 28,645    | 24,015    | 34,022    | 26,114    |
| 100,000    | 189,126   | 293,340   | 205,968   | 165,109   | 234,156   | 173,391   |
| 1,000,000  | 2,208,952 | 2,344,689 | 2,269,297 | 2,058,377 | 2,191,465 | 2,098,552 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+

Untuk ukuran batch yang lebih besar, IDENTITYversi umumnya lebih cepat .

Buku Kueri TSQL juga menjelaskan mengapa IDENTITYbisa memiliki keunggulan kinerja dibandingkan urutan.

Ini IDENTITYadalah tabel khusus dan SEQUENCEtidak. Jika bencana terjadi pada pertengahan sisipan sebelum log buffer disiram, tidak masalah jika identitas yang dipulihkan adalah yang sebelumnya karena proses pemulihan juga akan membatalkan sisipan, sehingga SQL Server tidak memaksa pembilasan log buffer pada setiap identitas cache terkait menulis disk. Namun untuk urutan ini adalah ditegakkan sebagai nilai dapat digunakan untuk tujuan apapun - termasuk di luar database. Jadi, dalam contoh di atas dengan sejuta sisipan dan ukuran cache 1.000 ini adalah tambahan ribu log flushes.

Skrip untuk direproduksi

DECLARE @Results TABLE(
  BatchCounter INT,
  NumRows      INT,
  SequenceTime BIGINT,
  IdTime       BIGINT);

DECLARE @NumRows      INT = 10,
        @BatchCounter INT;

WHILE @NumRows <= 1000000
  BEGIN
      SET @BatchCounter = 0;

      WHILE @BatchCounter <= 50
        BEGIN
            --Do inserts using Sequence
            DECLARE @SequenceTimeStart DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO dbo.t1_Seq1_cache_1000
                        (c1)
            SELECT N
            FROM   [dbo].[TallyTable] (@NumRows)
            OPTION (RECOMPILE);

            DECLARE @SequenceTimeEnd DATETIME2(7) = SYSUTCDATETIME();
            --Do inserts using IDENTITY
            DECLARE @IdTimeStart DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO dbo.t1_identity
                        (c1)
            SELECT N
            FROM   [dbo].[TallyTable] (@NumRows)
            OPTION (RECOMPILE);

            DECLARE @IdTimeEnd DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO @Results
            SELECT @BatchCounter,
                   @NumRows,
                   DATEDIFF(MICROSECOND, @SequenceTimeStart, @SequenceTimeEnd) AS SequenceTime,
                   DATEDIFF(MICROSECOND, @IdTimeStart, @IdTimeEnd)             AS IdTime;

            TRUNCATE TABLE dbo.t1_identity;

            TRUNCATE TABLE dbo.t1_Seq1_cache_1000;

            SET @BatchCounter +=1;
        END

      SET @NumRows *= 10;
  END

SELECT NumRows,
       MIN(SequenceTime) AS MinSequenceTime,
       MAX(SequenceTime) AS MaxSequenceTime,
       AVG(SequenceTime) AS AvgSequenceTime,
       MIN(IdTime)       AS MinIdentityTime,
       MAX(IdTime)       AS MaxIdentityTime,
       AVG(IdTime)       AS AvgIdentityTime
FROM   @Results
GROUP  BY NumRows;
Martin Smith
sumber