为什么Denali序列的性能要优于Identity列?


36

在他对哪个更好的回答中:标识列或生成的唯一ID值?麦丁妮说:

当SQL Denali发布时,它将支持比身份更有效的序列,但是您自己无法创建更有效的序列。

我不确定。了解Oracle的序列后,我要么创建一个插入触发器,要么将每个插入封装到存储过程的调用中,要么祈祷当我进行临时插入时不要忘记正确使用序列。

我怀疑序列的优势是否如此明显。


2
我知道它不能回答您的问题,但是除了性能上的差异外,序列还有其他优点。例如,序列不会阻止您更新目标列,这是IDENTITY的非常不便的限制。
nvogel 2011年

Answers:


37

我也会在这里回答。它与内部IDENTITYSEQUENCE工作方式有关。

使用IDENTITY,SQL Server将值预先缓存到内存中,以便随时可用。有关详细信息,请参见Martin Smith的答案。使用值时,后台进程会生成更多值。可以想象,该池很快就会用完,而应用程序则受生成值的后台进程的支配。

使用SEQUENCE,SQL Server允许您定义缓存的大小。尽管SQL Server实际上并未将值保留在缓存中,但仅保留当前值和顶端值,这将大大减少创建值所需的IO数量。

不要将缓存设置得太高,因为这会减少可使用的数量:如果SQL Server崩溃,则当前缓存范围中未使用的任何指定值都会丢失。

对于行插入,只需为列指定默认值,如下所示:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),

21

自从撰写Itzik Ben Gan文章以来,IDENTITY似乎已更改了10的硬编码缓存大小。从此连接项的评论中

预分配的大小基于定义身份属性的列的数据类型的大小。对于SQL Server整数列,服务器会在1000个值的范围内预分配标识。对于bigint数据类型,服务器在10000个值的范围内预分配。

T-SQL查询本书包含以下表,但强调的是,这些值不记录或保证是不变的。

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

本文测试了各种序列缓存大小和插入批处理大小,并得出以下结果。

在此处输入图片说明

这似乎表明,对于大的插入IDENTITY,执行效果很好SEQUENCE。它不会测试缓存大小1,000,但是这些结果只是一项测试。专门查看具有各种批处理大小的插入的1,000高速缓存大小,我得到以下结果(将每个批处理大小尝试50次,并将结果汇​​总为以秒为单位)。

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

对于较大的批量,该IDENTITY版本通常看起来更快

TSQL查询书还解释了为什么IDENTITY与顺序相比可以具有性能优势。

IDENTITY是表具体SEQUENCE是不是。如果灾难是在刷新日志缓冲区之前在插入中间发生的,则恢复的身份是否为较早的身份并不重要,因为恢复过程还将撤消插入操作,因此SQL Server不会强制在每个标识上强制刷新日志缓冲区缓存相关的光盘写入。然而,对于这种序列强制执行,价值可能被用于任何目的-包括数据库之外。因此,在上面的示例中,具有一百万次插入并且缓存大小为1,000,这是额外的一千次日志刷新。

复制脚本

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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.