SQL ServerでMSrepl_commandsが増え続ける原因とレプリケーション遅延の確認方法
公開日 2026-08-13 · 更新日 2026-08-13 · 最終検証日 2026-08-13
結論
`MSrepl_commands` が増え続ける原因は大きく2つです。(a) 配布エージェントがサブスクライバーへコマンドを適用できていない、(b) クリーンアップジョブが動いていない、または保持期間が長すぎる。切り分けは `MSdistribution_history` でエージェントの最終稼働時刻を確認し、`sp_replmonitorsubscriptionpendingcmds` で未配布コマンド数を取れば判断できます。
この文書の適用条件
| 対象製品 | SQL Server(トランザクションレプリケーション構成) |
|---|---|
| 確認バージョン | SQL Server 2008 以降(ディストリビューターの構成に依存するため詳細は要確認) |
| 適用環境 | オンプレミス、EC2、Azure(Amazon RDS ではレプリケーション構成に制約があるため要確認) |
| 必要権限 | 配布データベースへの参照権限。`sp_replmonitorsubscriptionpendingcmds` は replmonitor ロールまたは sysadmin。保持期間の変更は sysadmin |
| 実行影響 | 参照系は変更なし。保持期間の変更とレプリケーションの再初期化は構成に影響します |
| 再起動 | 不要 |
| 最終検証日 | 2026-08-13 |
そのまま実行できるコマンド
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- 配布データベースへの参照権限
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: 配布データベースへの参照権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
USE [distribution];
GO
SELECT
da.id AS agent_id,
da.name AS agent_name,
da.publisher_db,
da.publication,
da.subscriber_db,
MAX(dh.time) AS last_history_time,
MAX(dh.delivered_commands) AS last_delivered_commands,
MAX(dh.delivery_latency) AS last_delivery_latency_ms
FROM dbo.MSdistribution_agents AS da
LEFT JOIN dbo.MSdistribution_history AS dh
ON da.id = dh.agent_id
GROUP BY da.id, da.name, da.publisher_db, da.publication, da.subscriber_db
ORDER BY last_history_time ASC;`last_history_time` が現在時刻から大きく離れているエージェントは、動いていないか、動いていても履歴を書けていません。先頭(最も古い)に並んだエージェントが調査対象です。
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- 配布データベースへの参照権限
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: 配布データベースへの参照権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
USE [distribution];
GO
SELECT TOP (50)
dh.agent_id,
da.name AS agent_name,
dh.runstatus, -- 1:開始 2:成功 3:実行中 4:アイドル 5:再試行 6:失敗
dh.start_time,
dh.time,
dh.duration,
dh.delivered_transactions,
dh.delivered_commands,
dh.delivery_rate,
dh.delivery_latency,
dh.comments
FROM dbo.MSdistribution_history AS dh
INNER JOIN dbo.MSdistribution_agents AS da
ON dh.agent_id = da.id
ORDER BY dh.time DESC;`runstatus = 6`(失敗)や `runstatus = 5`(再試行)が続いていれば、`comments` にエラー内容が入ります。適用できないコマンドがあると、そこで配布が止まり `MSrepl_commands` が減りません。
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- replmonitor データベースロール または sysadmin
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: replmonitor データベースロール または sysadmin
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
EXEC distribution.dbo.sp_replmonitorsubscriptionpendingcmds
@publisher = N'LEGACY-SQL01',
@publisher_db = N'SampleDB',
@publication = N'SamplePublication',
@subscriber = N'LEGACY-SQL01',
@subscriber_db = N'SampleDB',
@subscription_type = 0; -- 0: プッシュ / 1: プル未配布コマンド数(pendingcmdcount)と処理見込み時間が返ります。この値が増え続けているなら、原因はサブスクライバー側への適用が追いついていないこと((a) のケース)です。逆にこの値が小さいのに `MSrepl_commands` が大きい場合は、クリーンアップ側((b) のケース)を疑います。
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- 配布データベースへの参照権限
- 変更作業
- なし(参照のみ。ただし全件スキャンを伴う)
- Production実行
- 可能だが、大規模な配布データベースでは実行時間が長くなる
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: 配布データベースへの参照権限
-- 変更作業: なし(参照のみ。ただし全件スキャンを伴う)
-- Production 実行: 可能。ただし件数が多いと実行時間が長くなるため負荷の低い時間帯を選ぶこと
USE [distribution];
GO
-- トランザクション側(entry_time で保持範囲が分かる)
SELECT
t.publisher_database_id,
COUNT(*) AS transaction_count,
MIN(t.entry_time) AS oldest_entry_time,
MAX(t.entry_time) AS newest_entry_time
FROM dbo.MSrepl_transactions AS t
GROUP BY t.publisher_database_id;
-- コマンド側
SELECT
c.publisher_database_id,
COUNT(*) AS command_count
FROM dbo.MSrepl_commands AS c
GROUP BY c.publisher_database_id;`MSrepl_commands` は行数が非常に多くなることがあり、`COUNT(*)` は全件スキャンになります。参照のみですが実行時間と I/O を消費するため、負荷の低い時間帯を選んでください。`oldest_entry_time` が保持期間(既定 72 時間)より明らかに古い場合、クリーンアップが動いていません。
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- msdb への参照権限
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: msdb への参照権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT TOP (30)
j.name AS job_name,
j.enabled,
h.run_date,
h.run_time,
h.run_duration,
h.run_status, -- 0:失敗 1:成功 2:再試行 3:取消 4:実行中
h.message
FROM msdb.dbo.sysjobs AS j
LEFT JOIN msdb.dbo.sysjobhistory AS h
ON j.job_id = h.job_id
AND h.step_id = 0
WHERE j.name LIKE 'Distribution clean up%'
ORDER BY h.run_date DESC, h.run_time DESC;既定のジョブ名は `Distribution clean up: distribution` です。`enabled = 0` や `run_status = 0`(失敗)が続いていれば、配布済みコマンドが削除されず `MSrepl_commands` が増え続けます。
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- sysadmin または配布データベースの db_owner
- 変更作業
- なし(参照のみ)
- Production実行
- 可能
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: sysadmin または配布データベースの db_owner
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
EXEC sp_helpdistributiondb @database = N'distribution';`min_distretention`(最小保持時間)、`max_distretention`(最大保持時間、既定 72 時間)、`history_retention`(履歴保持時間、既定 48 時間)が返ります。保持期間が長く設定されているほど、配布済みコマンドも長く残ります。
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- sysadmin または配布データベースの db_owner
- 変更作業
- なし(参照のみ。ただし大規模な配布DBでは長時間実行になる)
- Production実行
- 推奨しない。範囲を絞ったうえで、負荷の低い時間帯に限定すること
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: sysadmin または配布データベースの db_owner
-- 変更作業: なし(参照のみ。ただし高コスト)
-- Production 実行: 推奨しない。xact_seqno の範囲を必ず絞ること
EXEC distribution.dbo.sp_browsereplcmds
@xact_seqno_start = '0x00000000000000000000',
@xact_seqno_end = '0x00000000000000000000',
@publisher_database_id = 1;`sp_browsereplcmds` はコマンドを復元して読める形で返しますが、範囲を絞らずに実行すると配布データベース全体を走査します。行数が多い環境では長時間実行となりディストリビューター全体の性能に影響するため、`MSdistribution_history` で特定した `xact_seqno` の前後だけに絞って使ってください。上記の seqno は書式を示すための例示値です。
- 対象
- SQL Server 2008 以降(ディストリビューター)
- 権限
- sysadmin
- 変更作業
- あり(配布データベースの保持ポリシーが変わる)
- Production実行
- 実行可能だが、短縮すると未配布のコマンドが削除される可能性がある
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: sysadmin
-- 変更作業: あり(配布データベースの保持ポリシー変更)
-- Production 実行: 可能。ただし短縮は未配布コマンド削除のリスクを伴う
-- 下の値は例示。現在の配布遅延を踏まえて決めること
EXEC sp_changedistributiondb
@database = N'distribution',
@property = N'max_distretention',
@value = 72;
EXEC sp_changedistributiondb
@database = N'distribution',
@property = N'history_retention',
@value = 48;保持期間を短くするとクリーンアップの対象が増えますが、サブスクライバーがまだ受け取っていないコマンドが削除されると、そのサブスクリプションは再初期化が必要になります。現在の配布遅延の最大値より十分に長い値を保ってください。数値は例示であり推奨値ではありません。
- 対象
- SQL Server 2008 以降(パブリッシャー側のパブリケーションデータベースで実行)
- 権限
- sysadmin、またはパブリケーションデータベースの db_owner
- 変更作業
- あり(滞留コマンドを破棄し、スナップショットからの再同期を要求する)
- Production実行
- 不可。業務停止相当の影響評価と実施計画の確定が前提
-- 対象: SQL Server 2008 以降(パブリッシャー側のパブリケーションDBで実行)
-- 権限: sysadmin または パブリケーションDBの db_owner
-- 変更作業: あり(滞留コマンドを破棄し、スナップショットからの再同期を要求)
-- Production 実行: 不可。影響評価と実施計画を確定させ、承認を得てから実施すること
USE [SampleDB];
GO
EXEC sp_reinitsubscription
@publication = N'SamplePublication',
@subscriber = N'LEGACY-SQL01',
@destination_db = N'SampleDB';再初期化を要求すると、次回のスナップショット適用まで対象サブスクライバーのデータは整合しません。スナップショットの生成と適用にはデータ量に比例した時間と I/O が必要です。まず滞留の原因(適用エラー、クリーンアップ停止)を解消できないかを検討し、それでも解消できない場合の最終手段として扱ってください。
結果の読み方
| 列 | 意味 | 確認するポイント |
|---|---|---|
| last_history_time | 配布エージェントが最後に履歴を書いた時刻 | 現在時刻から大きく離れていればエージェントが動いていない |
| runstatus | エージェントの実行状態(2:成功 5:再試行 6:失敗 など) | 5 や 6 が続く場合は `comments` でエラー内容を確認する |
| delivery_latency | 配布遅延 | 継続的に増加していればサブスクライバー側の適用が追いついていない |
| comments | エージェントのメッセージ | 適用時エラーの本文が入る。停止の直接原因になることが多い |
| pendingcmdcount | 未配布コマンド数(`sp_replmonitorsubscriptionpendingcmds`) | 大きく増加していれば原因は配布側。小さいのに `MSrepl_commands` が多ければクリーンアップ側 |
| oldest_entry_time | 配布データベースに残る最古のトランザクション時刻 | 保持期間を大きく超えていればクリーンアップが動作していない |
| command_count | `MSrepl_commands` の行数 | 時系列で取得し、増加が続いているかを見る。単発の値では判断できない |
| run_status(クリーンアップジョブ) | ジョブの実行結果 | 0(失敗)が続いていれば `message` で原因を確認する |
| max_distretention | 配布データベースの最大保持時間(既定 72 時間) | 長すぎる設定になっていないか。ただし配布遅延より短くしないこと |
こういう状況で使います
- 配布データベースのサイズが増え続け、ストレージを圧迫している
- サブスクライバー側のデータがある時刻以降で更新されていない
- パブリッシャー側のトランザクションログが解放されず、`log_reuse_wait_desc` が REPLICATION のまま
- レプリケーションモニターで配布遅延が増加し続けている
- スナップショットの適用が終わらない、または途中で失敗する
考えられる原因(可能性の高い順)
01
配布エージェントがコマンドを適用できていない
最も一般的な原因です。サブスクライバー側の制約違反、対象行の欠落、権限不足、接続断などで適用が失敗すると、そこで配布が止まり以降のコマンドが積み上がります。`MSdistribution_history` の `comments` にエラーが記録されます。
02
クリーンアップジョブが動作していない
`Distribution clean up: distribution` ジョブが無効化されている、または失敗し続けている場合、配布済みのコマンドも削除されずに残ります。エージェントは正常でもテーブルは増え続けます。
03
保持期間が長すぎる
`max_distretention` を大きくしていると、配布済みコマンドが長時間保持されます。安全側の設定として意図的に長くしている場合もあるため、設定の妥当性は運用要件と合わせて判断します。
04
サブスクライバーが長時間停止している
サブスクライバー側のサーバーが停止していたり、ネットワークが切れていると、その分のコマンドが配布データベースに滞留します。復旧後に一気に配布されるため、その間の容量を見込む必要があります。
05
大量更新が一度に発生した
一括削除・一括更新は行数分のコマンドを生成します。1回の処理で数百万行を更新すると、それに比例して `MSrepl_commands` が増えます。バッチ分割で平準化できます。
06
複数パブリケーションで同一データを配布している
同じテーブルを複数のパブリケーションに含めると、コマンドがパブリケーションごとに生成されます。構成の重複がないか確認してください(構成に依存するため、対象環境での検証が必要な仮説です)。
確認手順
- 1
配布エージェントの最終稼働時刻を確認する
参照のみ`MSdistribution_agents` と `MSdistribution_history` を結合し、`last_history_time` が古いエージェントを特定します。
- 2
直近の実行結果とエラーを確認する
参照のみ`runstatus` と `comments` から、適用時エラーで止まっていないかを見ます。
- 3
未配布コマンド数を取得する
参照のみ`sp_replmonitorsubscriptionpendingcmds` で、原因が配布側かクリーンアップ側かを切り分けます。
- 4
クリーンアップジョブの履歴を確認する
参照のみジョブが有効か、直近で成功しているかを `msdb` で確認します。
- 5
保持期間と最古データの時刻を突き合わせる
参照のみ`sp_helpdistributiondb` の `max_distretention` と `MSrepl_transactions` の `oldest_entry_time` を比較します。
- 6
件数の推移を時系列で取得する
低1回の `COUNT(*)` では増加傾向は分かりません。時間をおいて複数回取得し、増え続けているかを確認します。
対応方法
すぐに実施できる低リスクの対応
配布エージェントのエラーを解消する
中`comments` に出ているエラー(制約違反、行欠落、権限、接続)を解消し、エージェントを再開します。滞留が解消されればコマンドは減り始めます。
クリーンアップジョブを有効化・再実行する
中ジョブが無効または失敗しているなら、原因を確認したうえで有効化します。滞留量が多い場合、初回の削除処理は時間がかかります。
配布データベースの空き容量を確保する
中容量枯渇で書き込みが失敗している場合、まず空きを確保してから原因対応に進みます。
事前検討が必要な変更
保持期間を運用実態に合わせる
中`max_distretention` と `history_retention` を、実際の配布遅延の最大値より十分に長く、かつ必要以上に長くない値へ調整します。短縮しすぎると未配布コマンドが削除されます。
一括更新をバッチ分割する
低大量更新を分割して実行することで、コマンド生成のピークを抑えられます。
配布遅延と滞留件数を監視項目に加える
参照のみ`pendingcmdcount` と配布エージェントの `last_history_time` を定期取得し、閾値超過で通知します。容量枯渇として顕在化する前に検知できます。
配布データベースの配置とサイジングを見直す
中配布データベースのファイル配置、初期サイズ、自動拡張設定を見直し、滞留が発生しても即座に枯渇しない余裕を確保します。
専門家のレビューが必要な作業
サブスクリプションの再初期化(スナップショット再適用)
専門家レビュー必須滞留したコマンドを破棄して初期状態から同期し直す方式です。再初期化中はサブスクライバー側のデータが一時的に整合しない期間が生じ、スナップショット生成と適用の負荷も大きくなります。影響評価と実施計画の確定が前提です。
レプリケーション構成そのものの見直し
専門家レビュー必須パブリケーションの分割、アーティクルの絞り込み、レプリケーション方式の変更などは構成全体に影響します。設計レビューを前提とします。
!注意事項
- 保持期間を短縮すると、サブスクライバーがまだ受け取っていないコマンドが削除される可能性があります。削除されたサブスクリプションは再初期化が必要になります。現在の配布遅延の最大値より十分に長い値を保ってください。
- `sp_browsereplcmds` は範囲を絞らずに実行すると配布データベース全体を走査します。行数の多い環境ではディストリビューター全体の性能に影響するため、`xact_seqno` の範囲指定を必ず行ってください。
- `MSrepl_commands` に対する `COUNT(*)` は全件スキャンです。参照のみですが実行時間と I/O を消費します。
- サブスクリプションの再初期化はスナップショットの再生成と再適用を伴い、その間サブスクライバー側のデータは整合しません。業務影響の確認と承認が前提です。
- `log_reuse_wait_desc = REPLICATION` はトランザクションレプリケーションと CDC の両方で発生します。片方だけを見て原因を確定しないでください。
- Amazon RDS for SQL Server ではレプリケーション構成に制約があります。ディストリビューターの配置可否や利用可能な機能は対象環境で確認してください。
バージョン・環境による違い
これで解決しない場合に確認すること
パブリッシャー側のログリーダーエージェント
配布データベースにコマンドが入ってこない場合、原因は配布側ではなくログリーダー側です。ログリーダーの稼働状況を確認します。
サブスクライバー側のブロッキング
適用が遅い場合、サブスクライバー側でロック待ちが発生していないかを確認します。
配布データベースのインデックス断片化
滞留が長期化した後は、配布データベース側のインデックス状態も確認対象になります。
ネットワーク経路の安定性
再試行が繰り返されている場合、原因がデータベースではなくネットワーク側であることがあります。
この文書の根拠と限界
製品の公式ドキュメントに基づく説明
SQL Server トランザクションレプリケーションの配布データベーススキーマ(`MSrepl_commands`、`MSrepl_transactions`、`MSdistribution_agents`、`MSdistribution_history`)および `sp_replmonitorsubscriptionpendingcmds`、`sp_browsereplcmds`、`sp_helpdistributiondb`、`sp_changedistributiondb` の公開仕様に基づく一般的な確認手順です。保持期間の具体値は環境依存のため例示にとどめています。Amazon RDS でのレプリケーション制約は対象環境での確認を前提としています。
よくある質問
MSrepl_commandsが増え続ける原因は何ですか?
大きく2つです。配布エージェントがサブスクライバーへコマンドを適用できていない場合と、ディストリビューションクリーンアップジョブが動作しておらず配布済みコマンドが削除されていない場合です。`sp_replmonitorsubscriptionpendingcmds` の未配布コマンド数が大きければ前者、小さいのにテーブルが大きければ後者です。
Productionで実行できますか?
配布エージェントの状態確認、未配布コマンド数の取得、ジョブ履歴の参照はいずれも参照専用で本番環境で実行できます。`MSrepl_commands` の `COUNT(*)` と `sp_browsereplcmds` は参照のみですが高コストなため、時間帯と範囲を絞ってください。保持期間の変更と再初期化は影響の大きい操作です。
どの権限が必要ですか?
配布データベースのテーブル参照には該当データベースへの参照権限が必要です。`sp_replmonitorsubscriptionpendingcmds` は replmonitor データベースロールまたは sysadmin、保持期間の変更(`sp_changedistributiondb`)は sysadmin が必要です。
保持期間を短くすれば解決しますか?
クリーンアップの対象は増えますが、未配布のコマンドまで削除されるとそのサブスクリプションは再初期化が必要になります。まず配布が止まっている原因を解消し、そのうえで実際の配布遅延より十分に長い保持期間を設定してください。
結果をどう判断しますか?
単発の件数では判断できません。`last_history_time` が更新されているか、`pendingcmdcount` が時系列で増えているか、`oldest_entry_time` が保持期間を超えていないかの3点を組み合わせて、配布側の問題かクリーンアップ側の問題かを決めます。
AWS RDSでも使用できますか?
Amazon RDS for SQL Server ではレプリケーションの構成に制約があり、担える役割もサービス仕様に依存します。ディストリビューターの配置可否を含め、対象環境での確認が必要です。
この文書がカバーする質問
- Snapshot Replication 遅延の確認
- 配布データベースのサイズが増え続ける原因を知りたい
- サブスクライバーにデータが届かない原因を切り分けたい
リスク表示の意味
- 参照のみデータと設定を変更しません。
- 低影響は限定的ですが、権限と負荷の確認が必要です。
- 中性能・ロック・コストに影響する可能性があります。
- 高障害・データ損失・復旧作業が発生する可能性があります。
- 専門家レビュー必須本番適用前に別途レビューが必須です。
GIIPの対応範囲
レプリケーションの滞留は、配布データベースの容量が尽きるか、サブスクライバー側の業務が「データが古い」と気付くまで表面化しません。GIIP では配布エージェントの最終稼働時刻と未配布コマンド数を定期取得し、増加が一定時間続いた時点で通知する運用を組んでいます。再初期化のようにデータ整合と業務停止に直結する操作はAIエージェントの自動実行対象から外し、検知・原因切り分け・影響範囲の整理までを自動化して、実施判断は人間が行う形にしています。
執筆・技術検証
GIIP プロダクション運用チーム
大規模Webサービス、SQL Server、Oracle、AWS、Azureの設計・移行・運用に約30年従事。x12largeクラスのAWS RDS for SQL Server環境12セット、約12万テーブルのOracle環境、約3TBのTiDBからAurora MySQLへの移行を経験。現在も複数のクラウドデータベースと約30のWebサービスを、AIエージェントと人間の専門家が継続的に監視・運用しています。
SQL ServerのCDCでログスキャンが止まっていないか確認する方法
sys.dm_cdc_log_scan_sessions と cdc.lsn_time_mapping、キャプチャジョブの状態から、CDCのログスキャンが進んでいるかを確認する手順です。
sql-serverRDS for SQL Serverでトランザクションログの使用率を確認する方法
ログ使用率を DBCC SQLPERF(LOGSPACE) と sys.dm_db_log_space_usage で確認し、解放されない理由を log_reuse_wait_desc で切り分ける参照専用手順です。
sql-serverSQL Serverで長時間開いたままのトランザクションを確認するSQL
sys.dm_tran_active_transactions 系のDMVで、開始時刻・セッション・最後に実行したSQLまで含めて放置トランザクションを特定する手順です。
monitoringサーバーとデータベースを24時間監視するときに設定する項目
24時間監視を設計する際の監視対象・しきい値の考え方・エスカレーション体制・外形監視の必要性を、層ごとに整理したチェックリストです。
関連サービス
レプリケーション遅延の切り分けを依頼する
同じ確認を複数の環境で継続する必要がある場合は、運用体制ごと相談できます。
レプリケーション遅延の切り分けを依頼する