one*_*act 2 sql-server transaction-log
我需要更新分区表中的数十亿行;更新也可能会在分区之间移动数据。我试图弄清楚作为单个事务执行此操作需要多少额外的日志空间(我意识到我最好批量处理这项工作,但我试图避免这种情况)。我想尝试一个较小的集合,这样我就可以看看它在规模上需要什么。是否有任何我可以监控的系统管理视图可以帮助我解决这个问题?
我认为您不想尝试在单个事务中更新数十亿行,坦率地说,我认为不值得花精力去尝试推断这将花费多少时间或会消耗多少事务日志。
批处理就是做到这一点的方法......它允许您最大限度地减少对事务日志的影响,暂停批次之间的过程以进行验证或维护,允许用户自由访问当前批次当前未锁定的表部分,并在批次出现问题(或服务器上的任何问题,其中一些问题在服务重新启动之前不会真正影响您,例如有人被电源线绊倒)的情况下提供更平滑的回滚。
没有理由在表中存储脏标志。假设您有一个不可变的键(并且您不是故意更改键,或者至少将其更改为一些可预测的内容,不会使当前行与不同行的未来版本发生冲突),您可以简单地扫描一次表以预先确定您将只更新一次的行的批次/范围。假设您有一个带有bigint
密钥的表,如下所示:
CREATE TABLE dbo.MyBigTable
(
MyKey bigint NOT NULL,
-- OtherColumns here,
CONSTRAINT PK_MyBigTable PRIMARY KEY (MyKey)
); -- Partition stuff here
Run Code Online (Sandbox Code Playgroud)
其中有十亿行,您可以通过表的一次传递来确定要更新的每个批次的低边界和高边界:
DECLARE @BatchSize int = 10000; -- find your sweet spot (see article below)
;WITH x AS
(
SELECT rn = ROW_NUMBER() OVER (ORDER BY MyKey),
ln = ROW_NUMBER() OVER (ORDER BY MyKey DESC),
MinKey = FIRST_VALUE(MyKey) OVER (ORDER BY MyKey),
MyKey
FROM dbo.MyBigTable
)
SELECT BatchNumber = ROW_NUMBER() OVER (ORDER BY rn),
RangeStart = COALESCE(LAG(MyKey,1) OVER (ORDER BY MyKey)+1, MinKey),
RangeEnd = MyKey,
Processed = 0
INTO dbo.UpdatingQueue
FROM x WHERE rn % @BatchSize = 0 OR ln = 1
ORDER BY BatchNumber;
CREATE UNIQUE CLUSTERED INDEX CIX_UpdatingQueue
ON dbo.UpdatingQueue(RangeStart, RangeEnd);
Run Code Online (Sandbox Code Playgroud)
现在你的批次可以说:
DECLARE @BatchNumber int;
SELECT @BatchNumber = MIN(BatchNumber)
FROM dbo.UpdatingQueue
WHERE Processed = 0;
WHILE @BatchNumber IS NOT NULL
BEGIN
-- need try/catch here obviously
UPDATE mbt SET { whatever your update is }
FROM dbo.MyBigTable AS mbt
INNER JOIN dbo.UpdatingQueue AS q
ON mbt.MyKey >= q.RangeStart AND mbt.MyKey <= q.RangeEnd
WHERE q.BatchNumber = @BatchNumber
AND q.Processed = 0;
UPDATE dbo.UpdatingQueue
SET Processed = 1
WHERE BatchNumber = @BatchNumber;
SELECT @BatchNumber = MIN(BatchNumber)
FROM dbo.UpdatingQueue
WHERE Processed = 0;
-- add a delay, explicit checkpoint
-- / log backup here, what have you
END
Run Code Online (Sandbox Code Playgroud)
确定批处理是否跨越分区边界并手动更改批处理窗口以便批处理仅处理单个分区可能是值得的。假设您用来标识行的内容与分区键匹配(情况并非总是如此),这很容易;如果聚类键是其他内容(例如领先于datetime
),则情况会稍微复杂一些。
无论如何,该批处理可以被中断,因为如果您停止它并在明天或下周再次启动它,它将从它停止的地方继续(这就是为什么我在这里不使用#temp 表)。如果您拥有处理能力,您可以发挥创意,让多个进程同时在队列中工作,只要它们每个都配置为在自己的分区上工作(但只有在它们不这样做的情况下,您才能真正看到任何收益)使 I/O 饱和,例如分区是物理分区而不是逻辑分区)。
我在这里整理了一个简单的例子:
但您还应该为这篇文章添加书签,其中讨论了批处理的其他一些方面(尽管该文章适用于删除,但它也适用于更新):