sql-server - 如何减少 SQL Server 事务日志的使用

标签 sql-server tsql azure

我们有在 Azure SQL 表中写入日志的应用程序。表的结构如下。

CREATE TABLE [dbo].[xyz_event_history]
(
    [event_history_id] [uniqueidentifier] NOT NULL,
    [event_date_time] [datetime] NOT NULL,
    [instance_id] [uniqueidentifier] NOT NULL,
    [scheduled_task_id] [int] NOT NULL,
    [scheduled_start_time] [datetime] NULL,
    [actual_start_time] [datetime] NULL,
    [actual_end_time] [datetime] NULL,
    [status] [int] NOT NULL,
    [log] [nvarchar](max) NULL,

    CONSTRAINT [PK__crg_scheduler_event_history] PRIMARY KEY NONCLUSTERED 
    (
        [event_history_id] ASC
    )
)

表存储为聚集索引 scheduled_task_id列(非唯一)。
CREATE CLUSTERED INDEX [IDX__xyz_event_history__scheduled_task_id] ON [dbo].[xyz_event_history]
(
    [scheduled_task_id] ASC
)
event_history_id由应用程序生成,它是随机(非顺序)GUID。应用程序或者从表中创建、更新和删除旧实体。 log列通常包含 2-10 KB 的数据,但在某些情况下它可以增长到 5-10 MB。这些项目通常通过 PK ( event_history_id ) 访问,最频繁的排序顺序是 event_date_time desc .

在将 Azure SQL 的性能层降低到“S3”(100 个 DTU)后,我们看到的问题是跨越事务日志速率限制。在sys.dm_exec_requests表中可以清楚地看到——会有等待类型LOG_RATE_GOVERNOR的记录( msdn )。

Occurs when DB is waiting for quota to write to the log.



我注意到对日志率造成重大影响的操作是从 xyz_event_history 中删除的操作。和更新 log柱子。更新方式如下。
UPDATE xyz_event_history
SET [log] = COALESCE([log], '') + @log_to_append
WHERE event_history_id = @id

Azure SQL 数据库的恢复模型是 FULL并且无法更改。

这是物理索引统计信息 - 有许多页面超过了每行 8K 的限制。

表名 AllocUnitTp PgCt AvgPgSpcUsed RcdCt MinRcdSz MaxRcdSz
xyz_event_history IN_ROW_DATA 4145 47.6372868791698 43771 102 7864
xyz_event_history IN_ROW_DATA 59 18.1995058067705 4145 11 19
xyz_event_history IN_ROW_DATA 4 3.75277983691623 59 11 19
xyz_event_history IN_ROW_DATA 1 0.914257474672597 4 11 19
xyz_event_history LOB_DATA 168191 97.592290585619 169479 38 8068
xyz_event_history IN_ROW_DATA 7062 3.65090190264393 43771 38 46
xyz_event_history IN_ROW_DATA 99 22.0080800593032 7062 23 23
xyz_event_history IN_ROW_DATA 1 30.5534964170991 99 23 23
xyz_event_history IN_ROW_DATA 2339 9.15620212503089 43771 16 38
xyz_event_history IN_ROW_DATA 96 8.70488015814184 2339 27 27
xyz_event_history IN_ROW_DATA 1 34.3711391153941 96 27 27
xyz_event_history IN_ROW_DATA 1054 26.5034840622683 43771 28 50
xyz_event_history IN_ROW_DATA 139 3.81632073140598 1054 39 39
xyz_event_history IN_ROW_DATA 1 70.3854707190511 139 39 39
  • 有没有办法减少事务日志的使用?
  • 如上例所示,SQL Server 如何记录更新事务?它只是“旧”加"new"值吗? (可以想象,在事务日志大小方面,经常添加少量数据会非常低效)

  • 更新(4 月 20 日):
    我已经用答案中的建议做了一些实验,并且对 INSERT 的差异印象深刻。而不是 UPDATE使。

    根据以下有关 SQL Server 事务日志内部结构的 msdn 文章 ( https://technet.microsoft.com/en-us/library/jj835093(v=sql.110).aspx ):

    Log records for data modifications record either the logical operation performed or they record the before and after images of the modified data. The before image is a copy of the data before the operation is performed; the after image is a copy of the data after the operation has been performed.



    这会自动生成 UPDATE ... SET X = X + 'more' 的场景效率极低 在事务日志使用方面 - 它需要“图像前”捕获。

    我创建了简单的测试套件来测试将数据添加到“日志”列的原始方式与我们将新数据插入新表的方式。结果我得到了相当惊人的(至少对我来说,对 SQL Server 不太有经验)。

    测试很简单:5'000 次添加 1'024 个字符长的日志部分 - 结果只有 5MB 的文本(并没有想象的那么糟糕)。

    完全恢复模式、SQL Server 2014、Windows 10、SSD

    更新插入
    持续时间 07:48 (!) 00:02
    数据文件增长 ~8MB ~8MB
    陈。日志增长 ~218MB (!) 0MB(为什么?!)

    enter image description here

    只需 5000 个添加 1KB 数据的更新就可以让 SQL Server 挂起 8 分钟( 哇!) - 我没想到!

    我认为原始问题此时已解决,但提出了以下问题:
  • 为什么事务日志增长看起来是线性的(不是我们在简单地捕获“之前”和“之后”图像时所期望的二次方)?从图中我们可以看到“每秒的项目数”与平方根成比例增长 - 如果开销随插入的项目数量线性增长,则符合预期。
  • 为什么在插入事务日志的情况下看起来与任何插入之前的大小相同?
    我查看了事务日志(带有 Dell's Toad )的插入情况,看起来只有最后 297 个项目在那里 - 可以想象事务日志被截断了,但为什么如果它是 FULL恢复模式?

  • 更新(4 月 21 日)。 DBCC LOGINFO带有 INSERT 的案例的输出- 之前和之后。日志文件的物理大小与输出匹配 - 磁盘上正好是 1,048,576 字节。
    为什么看起来事务日志仍然存在?

    RecoveryUnitId FileId FileSize StartOffset FSeqNo 状态奇偶校验 CreateLSN
    0 2 253952 8192 131161 0 64 0
    0 2 253952 262144 131162 2 64 0
    0 2 253952 516096 131159 0 128 0
    0 2 278528 770048 131160 0 128 0

    RecoveryUnitId FileId FileSize StartOffset FSeqNo 状态奇偶校验 CreateLSN
    0 2 253952 8192 131221 0 128 0
    0 2 253952 262144 131222 0 128 0
    0 2 253952 516096 131223 2 128 0
    0 2 278528 770048 131224 2 128 0

    对于那些感兴趣的人,我已经使用 Process Monitor 记录了“sqlserv.exe”事件。 - 我可以看到该文件一次又一次地被覆盖 - 看起来 SQL Server 由于某种原因将旧日志项视为不再需要:https://dl.dropboxusercontent.com/u/1323651/stackoverflow-sql-server-transaction-log.pml .

    更新(4 月 24 日)。 似乎我终于开始了解那里发生的事情并想与您分享。上面的推理通常是正确的,但有一个严重的警告,即使用 INSERT 也对奇怪的事务日志重用产生了混淆。 s。

    Database will behave like in SIMPLE recovery mode until first full backup is taken (even though it's in FULL recovery mode).



    我们可以将上面的数字和图表视为对 SIMPLE 有效恢复模式,我必须重新测量真实 FULL - 他们甚至更多 惊人 .

    更新插入
    持续时间 13:20 (!) 00:02
    数据文件增长 8MB 11MB
    陈。日志增长 55.2GB (!) 14MB

    real FULL recovery mode UPDATE stats

    最佳答案

    您正在使用日志字段违反标准形式的基本租户之一。日志字段接缝保存与主相关的附加信息序列。解决方法是停止这样做。

    1 创建一个表。 xyz_event_history_LOG(event_history_id,log_sequence#,log)

    2 停止更新 [xyz_event_history] ​​中的日志字段,而是插入到 xyz_event_history_LOG

    事务日志中的数据量将大大减少。

    关于sql-server - 如何减少 SQL Server 事务日志的使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36661572/

    相关文章:

    sql-server - 在不使用链接表的情况下,可以将多个事实表连接到相同的维度表吗?

    sql-server - Microsoft Sync Framework - 如何在架构更改后重新配置表(或整个范围)?

    sql - 当我不想使用聚合时,是否应该使用 Pivot 将 SQL Server 2008 行值转换为列名?

    sql - 如何从 SQL Server 中的字符串中删除最后一个符号。如果它是一个循环

    Azure Arm策略拒绝没有标签的资源组

    c# - 以编程方式上传的 Azure Blob 被删除

    sql - 使用 CTE 从下线记录中获取总计数和总金额之和

    sql - 字符串或二进制数据将被截断

    sql - 如果sql中的一个字段相同,如何过滤上一条记录

    azure - 在 azure 容器注册表实例上运行 spring boot 应用程序