MSMQはいつまで使えるのか ── 「非推奨ですらない」レガシーキューの移行判断

· 更新日: · · Windows, .NET, C#, MSMQ, メッセージキュー, レガシー技術, 移行, 情報システム

更新履歴(7件・最終更新 2026年08月02日)

この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。

この記事の知識マップを見直し、本文が述べている以上に広い意味になっていた関係を修正しました。本文の主張は変えていません。
記事の冒頭に「この記事の知識マップ」を追加しました。本文で扱う概念どうしの関係を一枚の図で見渡せます。関係の全一覧(根拠のURL・確度・確認日つき)と主要概念の定義は知識マップ詳細ページにまとめ、機械可読データをJSON-LDとTurtleで公開しています。
テーブルキューの取り出しSQLに`READCOMMITTEDLOCK`を足しました。公式ドキュメントは「`READ_COMMITTED_SNAPSHOT`が`ON`で、かつセッションの分離レベルが`READ COMMITTED`のとき、`READPAST`テーブルヒントは指定できない」と明記しており、その場合の対処として`READCOMMITTEDLOCK`の併記を挙げています。既定の分離レベルは`READ COMMITTED`なので、`READ_COMMITTED_SNAPSHOT`を有効にしただけのデータベース(Azure SQL Databaseでは既定でON)では、この取り出しは「他のワーカーの行を飛ばす」どころか文そのものがエラーになっていました。よく見る`ROWLOCK`を落としているのは、`ROWLOCK`と`READCOMMITTEDLOCK`が同じ粒度ヒントのグループで、1つのテーブルに両方を指定できないためです。設定の確認方法と、`OFF`と分かっている場合の選択も本文に追記しました。
テーブルキューの`ORDER BY Id`が「投入順に処理される」保証だと読める書き方になっていたのを直しました。揃うのは取り出す順番だけで、`READPAST`で並列に回す以上、先に取り出したジョブが後からコミットされることがあります。反映順に意味がある連携では消費者を1本にするかキー単位で分ける必要があること、どちらも取らないなら全体のFIFOは無いと明記すべきことを追記しました。
テーブルキューの取り出しSQLに並び順の指定がなく、投入順に取り出される保証がありませんでした。SQL Serverの仕様上 TOP は対象行を並べ替えないため、古いジョブが後回しになり続けます。並び順を指定する形に直しました。
判断表が前提にしているMSMQの用語を、2章末に最小セットとして定義しました。あわせてサポート状況をOS機能・Win32 API・.NET Framework・.NET・CoreWCFのレイヤー別に一覧化し、第一候補として勧めているテーブルキューの最小実装、棚卸し観点の記録用の対応表、塩漬けの条件のチェックリストを追加しています。
本文中の関連記事へのリンクの文言が、リンク先の現在のタイトルと食い違っていたのを、実際のタイトルに揃えました。本文の内容は変えていません。
初版公開
この記事を引用する(DOI(登録済みアーカイブ): 10.5281/zenodo.21739470)

以下のDOIは過去に登録されたアーカイブを指しており、現在の本文とは一致しない場合があります。現在の本文を参照するときは、このページのURLを使用してください。

小村 豪(2026)「MSMQはいつまで使えるのか ── 「非推奨ですらない」レガシーキューの移行判断」合同会社小村ソフト. https://comcomponent.com/blog/msmq-migration-decision-guide/

DOI(登録済みアーカイブ)
10.5281/zenodo.21739470
DOI(前回登録した版)
10.5281/zenodo.21739471

「MSMQは廃止されたと聞いた。いま動いているシステムも、すぐに移行しなければならないのか」──既存システムの保守では、まずここを切り分ける必要があります。

MSMQ(Microsoft Message Queuing)のOS機能としての存続と、.NETから利用するAPIの対応は別の話です。本記事の基準時点である2026年7月には、MSMQは公式の非推奨一覧に載っておらず、Windowsのオプション機能として残っています。一方、標準ライブラリの System.Messaging は.NET Framework専用で、.NET(Core以降)には移植されていません。123

つまり、問題は「明日MSMQが止まること」ではなく、「アプリを.NETへ移すときに、キューの実装が移行の障害になること」です。

この記事は、MSMQを使うシステムの保守・移行を担当する開発者と、方針を決める情報システム部門に向けたものです。まず事実関係と継続利用の条件を確認し、棚卸し、移行先の選定、テーブルキューの実装、切替手順へ進みます。

VB6や.NET Frameworkからの移行全体は「VB6から.NETへの移行実務」「.NET Framework→.NET移行前チェックリスト」を参照してください。本記事はキューの部分に絞ります。

1. まず結論:OSの存続と、アプリの移行を分ける

アプリを.NETへ移すなら、キューも一緒に移行するのが原則です。当面.NET Frameworkのまま運用するなら、OSのサポート期間と運用条件を確認したうえで、継続利用を選べます。新規プロジェクトでのMSMQ採用は勧めません。4

いま決めたいことから読む

いま決めたいこと 判断の出発点 説明
「廃止された」という話は本当か OS機能とマネージドAPIの状況を分ける 1章: 事実関係
当面そのまま使えるか OSのサポート期間に加え、監視・復旧・引継ぎを整えられるか確認する 2章: 継続利用の条件
.NET移行と一緒に置き換えたい 依存・形式・DTC・オフライン耐性を棚卸しする 3章: 棚卸し4章: 移行先
P/InvokeやCoreWCFで残せるか 呼び出せることと、保守負担を引き受けられることを分ける 1.3節: マネージドAPIとP/Invoke1.4節: CoreWCF
RabbitMQとAzure Service Busのどちらにするか その二択の前に、同じ業務DBのテーブルキューで足りるか検討する 4章: 要件別の選定5章: テーブルキュー
取り出しSQLを自分のDBへ持ち込みたい 分離レベル、スナップショット設定、ロックヒントを確認する 5.3節: SQLとDB設定
キューの処理順序を守りたい 取り出し順と業務データへの反映順を分ける 5.4節: 並列処理と順序
ワーカーを実運用へ載せたい トランザクション境界と、回収・再試行・掃除を確認する 5.5節: ワーカー5.6節: 運用
稼働中のシステムをどう切り替えるか メッセージ形式、受け側、重複対策、残存メッセージの扱いを決める 6章: 切替手順

通して読む場合は、存続の確認 → 継続利用の条件 → 棚卸し → 移行先 → 実装 → 切替の順に進みます。判断を振り返るときは7章のまとめを参照してください。

1.1. どのレイヤーの話かを確認する

「MSMQは使えるか」への答えは、次のようにレイヤーごとに違います。状態の基準時点は、冒頭と同じ2026年7月です。

レイヤー 現在の状態 実務上の意味
OS機能(Windowsのオプション機能) 存続。現行のWindowsクライアント/Serverに同梱され、非推奨宣言も出ていない12 有効化すれば今も動く。OSのサポート期間の中では使い続けられる
Win32ネイティブAPI(MQSendMessage など) 文書化されており、利用可能5 P/Invokeで.NETから呼ぶ道は残る。ただしフォーマッターやトランザクション連携は自前で書いて保守することになる
.NET Framework の System.Messaging 利用可能。ただし対象は.NET Framework 1.1〜4.8.13 既存システムはここで動いている。この先が無い
.NET(Core以降)の公式マネージドAPI 存在しない。Windows互換パックにも含まれない6 アプリを.NETへ上げる瞬間に、キュー部分の作り直しが必要になる
WCFのMSMQバインディング → CoreWCF.MSMQ コミュニティ主導の移植が存在。CoreWCF自体にはサポートポリシーがあるが、MSMQ実装はSystem.Messagingのコミュニティ移植に依存7 キュー経由で呼ばれるWCFサービス(受け側)の延命には使えるが、送信側の代替でも汎用のキューAPIでもない
新規採用 推奨できない 公式な将来の道がないため

1.2. 非推奨になっていないのはOS機能

Windowsクライアントの「Deprecated features」にはNTLM、VBScript、WordPadなどが載っていますが、MSMQの項目はありません。Windows Serverの「Features Removed or No Longer Developed」も、Windows Server 2025のタブを含めて同様です。12

非推奨(deprecated)は、積極的な開発を終了し、将来削除される可能性があることを示す公式な段階です。削除(removed)とは異なります。MSMQについては、その非推奨の宣言も出ていない、というのが本記事で確認した状況です。

1.3. .NETへの移行を妨げるのはマネージドAPI

System.Messaging.MessageQueue のリファレンスが対象とするのは.NET Framework 1.1〜4.8.1で、.NET(Core以降)向けの版はありません。Windows互換パック(Microsoft.Windows.Compatibility)は、レジストリ、WMI、Windowsサービス、EventLogなど約2万のAPIを提供しますが、System.Messaging は含んでいません。36

P/Invokeで残すなら、ラッパーの保守まで引き受ける

ネイティブAPIまで無くなったわけではありません。MQSendMessageMQReceiveMessage などのWin32 APIを、.NETからP/Invokeで呼ぶこと自体は可能です。ただし、System.Messaging が担っていたフォーマッターやトランザクション連携をラッパーとして自作し、保守することになります。これは移行の本命ではなく、どうしてもMSMQを残す場合の延命手段です。5

1.4. CoreWCFは「WCFの受け側」の経路として判断する

.NET FrameworkのWCFには、MSMQを使うバインディングがありました。これを.NETでホストする経路として、CoreWCFのMSMQトランスポート(CoreWCF.MSMQ)があります。7

ただし、用途はキュー経由で呼び出されるWCFサービスの受け側です。System.Messaging の代わりになる汎用キューAPIでも、送信側クライアントの代替でもありません。

CoreWCF自体にはMicrosoftのサポートポリシーがあります。一方、MSMQトランスポートの実装は.NET Framework版 System.Messaging のコミュニティ移植に依存しており、その依存部分まで同じ保証があるとは扱えません。利用するバージョンがサポート対象か、依存部分の保守を誰が担うかを確認して採用します。7

このように、「MSMQは廃止済み」と「.NETへそのまま移せない」は同じ意味ではありません。P/InvokeやCoreWCFを選べば、キューの置き換えを先送りする代わりに、その経路の保守負担を引き受けることになります。

図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全28件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle

2. 継続利用するか:.NETへの移行計画と、運用体制で決める

2.1. 「動いている」だけでは判断できない理由

.NET Framework 4.8はWindowsのコンポーネントとして、インストール先OSのライフサイクルに沿ってサポートされます。したがって、サポート期間内で動かし続ける計画は立てられます。4

確認したいのは、動作するかだけではありません。MSMQへの依存によってアプリ全体を.NET Frameworkにとどめる場合は、次の保守・移行上の負担も残ります。

負担 移行判断への影響
保守担当者の確保・引継ぎ System.Messaging とDTCを説明できる技術者が減り、構成を調べ直す費用が増える
ランタイムとライブラリの制約 新しいC#構文にはコンパイラ更新だけで使えるものもあるが、ランタイムの性能改善や新しい標準ライブラリは使えない。.NET Frameworkを対象外にするパッケージも増える
DTCへの依存 「キュー取り出しとDB更新を一緒に確定する」部分を残すと、周辺だけ.NET化しても最難関が最後に残る
古い直列化形式 BinaryFormatter の実装は.NET 9でランタイムから削除されており、形式も含めた移行が必要になる8

保守担当者がいなくなる問題は「ソースも資料もないシステムの保守」でも扱っています。MSMQの相談が障害対応より移行見積もりから始まるのは、動作そのものより、こうした依存と引継ぎが問題になるためです。

「OSがサポートする間は動く」と「待つほど移行しやすくなる」は別です。当面移行しない場合も、難所だけは先に棚卸ししておきます。

2.2. 当面移行しないなら、4つの最低条件を満たす

アプリを.NETへ上げる計画がなく、費用対効果からも当面は変更しない、いわゆる「塩漬け」は条件付きで合理的です。ただし、次の4項目をすべて満たすことを前提にします。

確認 最低条件 具体的にやること 満たさないと何が起きるか
キッティング・リストア手順にMSMQを明記する MSMQはWindowsのオプション機能。「Windowsの機能の有効化」またはDISM/PowerShellでの有効化手順を、環境構築手順書に書いておく PC・サーバーの入れ替え時に有効化を忘れ、移行当日に原因不明で動かない
キュー長を監視する 滞留件数のしきい値監視と通知を仕込む。配信不能キューとジャーナルキューも対象に含める 受け側が止まってもエラーにならず溜まり続けるため、誰も気づかないまま業務が止まる
構成をドキュメント化する キューのパス、権限、トランザクションの有無、フォーマッター、ジャーナル設定を記録する(3.2節の棚卸し表がそのまま使える) 将来の移行見積もりが調査からやり直しになり、精度も工数も悪化する
年1回、判断を見直す 「非推奨リストに載ったか」「OSの更新で挙動が変わっていないか」を年次で確認する 非推奨の告知が出てから慌てて動くことになる

とくに年次の見直しは、非推奨の告知やOS更新の影響を見落とさないための手順です。担当者の異動に備え、構成だけでなく回復手順も残します。この条件が埋まらないままの現状維持は、止まったことも原因も分からなくなるリスクを抱えます。

3. 棚卸し:MSMQに何を任せているかを記録する

3.1. 機能と用語を押さえる

MSMQでは、送信側がキューへメッセージを書き、同じマシンまたは別のマシンのアプリが、都合のよいタイミングで取り出します。移行先に引き継ぐべき性質は、主に次の3つです。

送信先が止まっていても蓄積する: store-and-forward

送信先が停止していてもローカルに蓄積し、回復後に届けられます。不安定な拠点間回線で役立ちますが、再起動をまたぐ永続性まで無条件に保証するわけではありません。

キューとDBの更新をまとめて確定する: トランザクション

キュー操作をトランザクションにでき、MS DTC(分散トランザクションコーディネーター)を使えば、キューからの取り出しとDB更新を1つの分散トランザクションで確定できます。

追加ミドルウェアを導入せずに使えた: OS同梱

追加のミドルウェア導入が不要だったため、2000年代の受発注連携、帳票の非同期処理、製造現場の工程間連携などで広く使われました。この3つの組み合わせが、いまも残っている理由です。

蓄積・永続化・トランザクションを用語で区別する

判断表で使う用語も、ここで揃えます。9

用語 意味と、確認する理由
プライベートキュー / パブリックキュー プライベートはローカルコンピューターにだけ登録され、Active Directoryには発行されない。.\private$\キュー名 のような形式で指定する。パブリックはディレクトリに登録され、ドメイン内で検索できる。中小規模の業務システムではプライベートが多い
expressメッセージ 既定の送信モード。配送中も配送後もメモリ上に置かれるため高速だが、そのコンピューターやMSMQサービスが停止すると失われる
recoverable(回復可能)メッセージ 送信側と中継先、宛先キューでディスクに保持され、再起動をまたいで残る。送信側で明示的に指定する
トランザクションキュー トランザクションメッセージだけを扱う。属性はキュー作成時に決まり、後から変更できない。トランザクションメッセージはディスクに永続化される
DTC MSMQとDBなど、複数リソースにまたがるトランザクションを調停するWindowsサービス。キューとDBの整合性をどう置き換えるかが移行時の争点になる
ジャーナルキュー / 配信不能キュー MSMQが生成するシステムキュー。前者は送信・取り出し済みメッセージの控え、後者は届けられなかったメッセージを保持する。放置による容量増加を監視する

「受け側が止まっても蓄積できること」と「MSMQやマシンが再起動しても残ること」は別です。前者だけを使っているのか、Recoverableまたはトランザクションメッセージによる後者も必要なのかを、分けて調べます。

3.2. コード・構成・運用から依存を洗い出す

ライブラリの参照だけで棚卸しを終えない

最初に System.Messaging の参照と MessageQueue の生成箇所を探します。ただし、そこに参照がないだけで「MSMQを使っていない」とは判断できません。呼び出し経路を分けて確認します。

呼び出し経路 コード・構成で探すもの
.NET Frameworkのライブラリ System.Messaging の参照、MessageQueue の生成箇所
WCFの構成 netMsmqBinding / msmqIntegrationBinding
ネイティブAPI・COM MQSendMessage などの MQ* ネイティブ関数、COM経由の呼び出し

キューごとに、移行判断の根拠を残す

次の表を使い、キューごとに確認結果を記録します。確認して終わりにせず、移行方針や稟議資料の根拠として残すことが目的です。

観点 どこを見るか 記録する内容 判定への効き方
(a) キューのパス MessageQueue に渡すパス文字列、構成ファイルの接続先、FormatName: 指定 ローカル/リモートの別、プライベート/パブリックの別、相手のマシン名 リモート・拠点間ならstore-and-forwardの要否を判定(4.2節の「オフライン耐性」行)。ローカル完結なら4.2節の1行目に落ちる
(b) トランザクションとRecoverable キュー作成箇所(トランザクションキューか)、送信時にRecoverableを指定しているか、DTCを使っているか トランザクションキューか否か、Recoverable指定の有無、DTC参加の有無 DTCありなら移行先はテーブルキューがほぼ確定。どちらも無い場合は、現行が「再起動でメッセージが消える前提」で運用されていることを明記する
(c) フォーマッター XmlMessageFormatter / BinaryMessageFormatter / ActiveXMessageFormatter の指定箇所 使用しているフォーマッターと、メッセージ本体の型 BinaryFormatter系ならJSONへの置き換えが必須(6.1節)。移行工数の主要因になる8
(d) ジャーナル・配信不能キュー キューのプロパティ(ジャーナル有効か)、システムキューの中身、運用手順書 ジャーナルの利用有無、配信不能キューを実際に見ているか 「失敗メッセージをどう扱っているか」が移行先の設計要件になる(テーブルキューなら退避テーブル)
(e) 誰が作成・削除しているか インストーラー、配置スクリプト、アプリ起動時のCreate呼び出し 作成主体と、権限設定を誰が行っているか 移行時に「新キューを誰が用意するか」がそのまま置き換わる。塩漬けを選ぶ場合はキッティング手順に転記する(2章)

4. 移行先:後継製品名ではなく、必要な性質で選ぶ

4.1. 4つの質問で要件を整理する

棚卸しの結果から、次の4点を確認します。

  1. 受け側は、自社の業務データベースを更新する処理か。
  2. トランザクションキューとDB更新を、DTCで一緒に確定しているか。
  3. 送信側と受信側は別マシン・別拠点か。store-and-forwardを実際に使っているか。
  4. メッセージ量はどの程度か。多くの業務システムの1日数千〜数万件という規模なら、候補の処理能力だけでは決め手になりにくい。

4.2. 性質ごとの第一候補

同一システムの業務DBを更新する非同期ジョブなら、まずテーブルキューを検討します。複数システムへの配信やルーティングなど、テーブルキューでは足りない要件があるときにブローカーを選びます。

使っている性質 第一候補 理由と注意
同一システム内の非同期処理(受け側はDB更新) DBのテーブルキュー 「メッセージ取り出し」と「業務データ更新」を同一ローカルトランザクションで確定でき、DTCが不要になる。既存のバックアップ・監視・運用がそのまま使える。なお送信側でも「業務データ更新とメッセージ行の追加」を同一トランザクションにでき、こちらはいわゆるOutboxパターン
DTCでキューとDB更新をアトミックにしている DBのテーブルキュー 分散トランザクションをローカルトランザクションに置き換えるのが移行の本質。クラウド系キューはDTCに参加できないため、この要件がある限りブローカー系は遠回りになる
複数システム・複数言語間の疎結合な連携 RabbitMQ(オンプレ) / Azure Service Bus(クラウド可) 配信のファンアウトやルーティングはブローカーの得意分野。RabbitMQは自前運用の負荷(冗長化・更新)を見積もること
拠点間のオフライン耐性(store-and-forward) Azure Service Bus等+再送設計、または拠点側テーブルキュー+同期 MSMQの「送信側ローカルに貯めて後で届く」を透過的に代替する仕組みは少ない。送信側で貯める責務をアプリ側に明示的に持たせる設計に変える
同一プロセス内の生産者/消費者 .NETのChannels等のインメモリキュー そもそもプロセス間キューが不要だったケース。プロセス内で完結させ、永続性が要るならテーブルキューへ
Windowsサービス間のプロセス間通信としてだけ使っている 名前付きパイプ等のIPC ただし置き換えられるのは両者が常に同時に動いている場合だけ。受け側の停止中もMSMQは(expressメッセージでも)送信を受け付けて後で届けるが、パイプは即座に失敗する。停止中の蓄積に頼っているなら、再送・バッファをアプリ側に実装するかキューを残す。選び方は「WindowsのIPC判断表」へ

テーブルキューを先に検討する理由

ここで「テーブルキューを先に検討する」のは、どの用途にもDBを勧めたいからではありません。MSMQ世代の業務システムに多い同じDBを更新する非同期処理では、整合性を単純に保てるうえ、既存のバックアップ・監視・運用を活用できるからです。

ブローカーが向く要件と、追加で設計すること

RabbitMQやAzure Service Busは、複数システム・複数言語の疎結合な配信、ファンアウト、ルーティングなどに向きます。高いスループットが必要な場合も検討対象です。ただし、キューと業務DBが別リソースになるため、MSMQ+DTCの原子性をそのまま移すのではなく、冪等性や再送をアプリ側で設計します。新しいミドルウェアの監視・冗長化・パッチ・担当者教育も見積もりに含めます。

移行しても、停止中の蓄積を消してよいとは限らない

オフライン耐性や受信側停止中の蓄積は、消してよい要件かを確認してください。クラウドのキューを選んでも送信側ローカルの蓄積が自動で付くわけではなく、名前付きパイプに替えれば停止中の受信待ちも同じ形では残りません。必要ならアプリ側に再送・バッファを持たせるか、キューを残します。

5. テーブルキュー:キュー操作と業務更新を同じDBで確定する

この章では、まず同一DBにまとめる意味を確認します。そのうえで、取り出しSQLの前提順序の保証範囲ワーカーのトランザクション実運用で追加する処理を順に見ます。

5.1. DTCを不要にできる理由

MSMQでは、キューとDBは別リソースです。「キューから取り出した」と「業務データを更新した」を一緒に確定するには、DTCによる分散トランザクションが必要でした。

キューを業務データと同じデータベースのテーブルにすると、どちらもそのDBへの更新になります。取り出し・業務更新・完了記録を、1つのローカルトランザクションで確定できます。分散トランザクションをローカルへ置き換えることが、この移行の中心です。

受信側だけでなく、送信側も同時に確定できる

送信側でも、業務データの更新とメッセージ行の INSERT を同じトランザクションに入れられます。これがOutboxパターンで、「業務データだけ更新され、メッセージが出ていない」という不整合を防ぎます。

以下はSQL Serverでの最小の形です。C#部分はトランザクションの境界を示す擬似コードであり、そのまま配置する完成実装ではありません。

5.2. ジョブを保存するテーブル

CREATE TABLE dbo.JobQueue (
    Id          BIGINT IDENTITY(1,1) PRIMARY KEY,
    Payload     NVARCHAR(MAX) NOT NULL,                    -- 本文。JSONで持つ
    Status      TINYINT       NOT NULL DEFAULT 0,          -- 0:未処理 1:処理中 2:完了
    EnqueuedAt  DATETIME2     NOT NULL DEFAULT SYSUTCDATETIME(),
    StartedAt   DATETIME2     NULL,                        -- 処理中のまま落ちた行の回収に使う
    RetryCount  INT           NOT NULL DEFAULT 0
);
CREATE INDEX IX_JobQueue_Status ON dbo.JobQueue (Status, Id);

Payload にJSONの本文を持ち、Status で未処理・処理中・完了を区別します。StartedAtRetryCount は、回収・再試行の運用に使います。

5.3. 1件の取り出しと、DB設定の確認

取り出す行の更新と、本文の取得を一つのSQLで行う

取り出しでは、1件を「処理中」に更新しながら OUTPUT で本文を受け取ります。READPAST により、別ワーカーが行ロックしている候補を待たずに飛ばし、複数プロセスで処理できます。10

WITH next_job AS (
    SELECT TOP (1) *
    -- READCOMMITTEDLOCK は READ_COMMITTED_SNAPSHOT が ON のDBで必要(後述)。
    -- OFF のDBでは既定の動作と同じなので、付けたままで両方に効く
    FROM   dbo.JobQueue WITH (READPAST, UPDLOCK, READCOMMITTEDLOCK)
    WHERE  Status = 0
    ORDER  BY Id          -- 投入順に取り出す。これが「キュー」であるための条件
)
UPDATE next_job
SET    Status = 1, StartedAt = SYSUTCDATETIME()
OUTPUT inserted.Id, inserted.Payload;

スナップショット設定と分離レベルを先に確認する

このSQLを読むときは、READ_COMMITTED_SNAPSHOT とセッションの分離レベルを確認します。

公式ドキュメントでは、DBの READ_COMMITTED_SNAPSHOTON で、セッションが READ COMMITTED、または READCOMMITTED ヒントを併用している場合、そのままでは READPAST を指定できません。対処は READCOMMITTED ヒントがあれば外し、READCOMMITTEDLOCK を含めることです。10

既定の分離レベルは READ COMMITTED なので、スナップショットを有効にしたDBに無対策で持ち込むと、単にブロックされるのではなくSQL文がエラーになります。Azure SQL Databaseは既定で ON です。オンプレでも読み取りのブロッキング対策で有効になっている場合があるため、先に調べます。

SELECT is_read_committed_snapshot_on FROM sys.databases WHERE name = DB_NAME();

上の取り出しSQLは READCOMMITTEDLOCK を付け、スナップショット設定にかかわらずロックによる読み取りを使います。OFF のDBでは既定の動作と同じです。10

ROWLOCKを併記しない理由と、READPASTの限界

一方、よくある WITH (READPAST, UPDLOCK, ROWLOCK) と違い、ROWLOCK は併記していません。ROWLOCKREADCOMMITTEDLOCK は同じ粒度ヒントのグループで、同じテーブルに両方は指定できないためです。10

READPAST が飛ばせるのは行ロックだけで、ページロックは飛ばせません。この例のように TOP (1) の索引シークで1行を取る範囲ではエスカレーションは実質問題になりませんが、ROWLOCK を明示したいなら、READ_COMMITTED_SNAPSHOTOFF だと確認したうえで READCOMMITTEDLOCK のほうを外します。

5.4. 取り出し順と、業務データへの反映順は別

取り出す候補には並び順を指定する

ORDER BY を省いて UPDATE TOP (1) にすると、どの行が選ばれるかは不定です。UPDATETOP は対象行を並べ替えず、(Status, Id) のインデックスがあるだけでも順番の保証にはなりません。古いジョブが後回しになり続けるのを避けるため、例では ORDER BY Id を指定しています。11

並列処理で先に反映されるとは限らない

ただし、並列処理の完了順までは揃いません。ワーカーAがジョブ1を処理している間、ワーカーBは READPAST でその行を飛ばし、ジョブ2を先にコミットできます。

揃うもの 揃わないもの
どの行を先に取り出すか(ORDER BY Id) どの行が先に業務データへ反映される

同じ受注番号への更新など、反映順に意味がある場合は、次のどちらかを選びます。

順序を守る方法 得られる性質と引き換えになるもの
消費者を1本にする 並列度を捨てて順序を取る。必要なスループットを満たせるなら最も単純
キー単位で分ける 受注番号などでワーカーを固定するか、同じキーが処理中なら取り出さない条件を設ける。保証するのはキーの中の順序で、キーをまたぐ順序は保証しない

どちらも選ばないなら、並列処理では全体のFIFOを保証しないことを設計に明記します。MSMQでも並列に受信すれば同じ問題が起こります。「キューだから順番どおり」という理解のまま移行しないことが重要です。

5.5. ワーカーのトランザクション境界

受け側でそろえるのは、取り出し・業務データの更新・完了記録のトランザクションです。次の擬似コードでは、三つの処理を同じ接続・トランザクションに渡し、最後にまとめて確定します。

while (!stoppingToken.IsCancellationRequested)
{
    using var tx = connection.BeginTransaction();

    var job = DequeueOne(connection, tx);          // 上のUPDATE ... OUTPUT
    if (job is null)
    {
        tx.Commit();
        await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);  // 空ならポーリング間隔だけ待つ
        continue;
    }

    ApplyBusinessData(connection, tx, job);        // ← 業務データの更新(本来やりたい仕事)
    MarkDone(connection, tx, job.Id);              // Status = 2

    tx.Commit();   // 「取り出し」と「業務更新」がこの1行で同時に確定する
}

DequeueOneApplyBusinessDataMarkDone が同じ接続・トランザクションを使い、最後の Commit で取り出しと業務更新を一緒に確定するところが要点です。ブローカーへ置き換える場合には、この同一DBの性質が失われるため、別の整合性設計が必要になります。

5.6. 実運用で追加する回収・再試行・掃除

最小例に加え、次の運用を設計します。

運用 処理する内容
処理中のまま残った行の回収 Status = 1 かつ StartedAt が一定時間より古い行を Status = 0 に戻す復旧処理
リトライ上限と退避 RetryCount が上限を超えた行を別テーブル、または Status = 9 などへ移す。MSMQの配信不能キューに相当する置き場
完了行の掃除 Status = 2 の行を定期的に削除・アーカイブし、テーブルとインデックスの肥大化を防ぐ

中小規模の業務システムでは、テーブルとポーリング、または通知による形で十分なことが多くあります。キュー製品を増やす前に、この実装で必要な性質と運用を満たせるか確認します。

6. 切替手順:形式・挙動・受け側を先に揃える

切替では、メッセージ形式と入力・結果のテストを先にそろえます。その後、ブリッジで新旧をつなぐか、旧キューを空にして切り替えるかを選びます。

6.1. 新旧が共存できるメッセージ形式を決める

移行後の既定はJSONとします。持ち込まないのは、BinaryMessageFormatter のようなBinaryFormatter系の直列化です。BinaryFormatterの実装は.NET 9でランタイムから削除されており、セキュリティ上も推奨されません。8

一方、バイナリ形式がすべて駄目なわけではありません。Protocol BuffersやMessagePackなど、仕様が独立して保守されている形式なら、そのまま新システムへ運ぶこと自体は問題ありません。3.2節の棚卸しで、フォーマッターと本文の型を先に確認します。

6.2. 書き換える前に、入力と結果をテストで固定する

キュー処理はタイミング依存の不具合が出やすい部分です。先に「この入力メッセージなら、この結果になる」という特性テストを用意し、置き換え後も同じ結果になることを機械的に確認できるようにします。「特性テストで挙動を固定してからリファクタリングする」も参照してください。

6.3. 受け側から対応し、ブリッジは欠落と重複を前提に設計する

受け側を先に対応させる

まず新しいキューを用意し、受け側を対応させてから送信側を切り替えます。

旧MSMQから新キューへメッセージを移す小さなブリッジを挟めば、送信側を一斉に切り替えずに済みます。ブリッジ自体は.NET Frameworkのままで構いません。

トランザクションの有無で、受け渡し手順を分ける

ただし、MSMQから取り出して、新キューへ書くまでの間に停止すると、欠落または二重配送が起こります。移行元のキューの種類に応じて、次のように設計します。

移行元 最低限必要な受け渡し
トランザクションキュー MSMQ側をトランザクション受信にして、書き込み失敗時に戻せるようにする。新キュー側ではメッセージIDで重複を弾き、冪等に処理する
非トランザクションキュー Peek で読む → 新キューへ冪等に書く → 書けたことを確認してから旧キューから取り除く。取り除く前に停止した場合の重複は、新キュー側で吸収する

キューのトランザクション属性は作成時に決まり、後から変更できません。非トランザクションキューに、トランザクション受信の手順をそのまま使うことはできない点に注意してください。

6.4. ブリッジが割に合わないなら、空にして切り替える

ブリッジの欠落・重複対策がシステム規模に見合わないなら、無理に新旧併存させず、計画停止で旧キューを吐き切ってから切り替える方法に寄せます。

メッセージを残したままの切り替えは、二重処理・欠落の原因になります。キューを空にして切り替えるのが、結局もっとも安全で速い場合が多い、というのが本記事の実務上の判断です。

7. まとめ

MSMQは、本記事の基準時点である2026年7月には公式に非推奨となっていません。OS機能として残っていることと、.NETへ移行しやすいことは別です。System.Messaging は.NET Frameworkに閉じており、アプリを.NETへ上げるならキューも一緒に見直すのが原則です。123

当面使い続けるなら、OSのサポート期間を確認し、構築・回復手順、キュー監視、構成の記録、年次の判断見直しを用意します。塩漬けの大きなリスクは、技術だけでなく、触れる人がいなくなることです。

移行するなら、最初に依存とメッセージ形式を棚卸しします。同じ業務DBを更新する非同期処理では、テーブルキューが第一候補です。MSMQ+DTCが担っていた整合性を、同一DBのローカルトランザクションへ置き換えられます。複数システムへの配信やルーティングなど、そこで足りない要件がある場合にRabbitMQやAzure Service Busを検討します。

そのうえで、SQLの分離レベルとヒント、並列時の反映順、ブリッジの欠落・重複対策を確認します。「廃止されたらしい」から急ぐのではなく、自分のシステムが何に依存しているかを把握して、維持と移行を決めることが重要です。

関連記事

関連する相談領域

合同会社小村ソフトでは、MSMQを含むレガシー構成の棚卸しと移行計画、.NET Frameworkから.NETへの移行、キュー処理を含む業務システムの改修を扱っています。

参考リンク

  1. Microsoft Learn, Deprecated features in the Windows client. Windowsクライアントで積極的な開発が終了した(非推奨の)機能の公式一覧。2026年7月時点の一覧にNTLM、VBScript、WordPad等は掲載されている一方、MSMQ(Microsoft Message Queuing)は掲載されていないこと、および非推奨(deprecated)が「積極的に開発されておらず将来の更新で削除される可能性がある」段階であり削除(removed)とは区別されることについて。  2 3 4

  2. Microsoft Learn, Features Removed or No Longer Developed in Windows Server. Windows Serverで削除された機能と開発終了(非推奨)機能の公式一覧。Windows Server 2025のタブを含む2026年7月時点の一覧にMSMQが掲載されていないこと、非推奨のコンポーネントもWindows Serverに同梱され続け、製品ライフサイクルに従って本番環境向けにサポートされ、セキュリティ更新と品質更新を受け続けることについて。  2 3 4

  3. Microsoft Learn, MessageQueue Class (System.Messaging). System.Messaging.MessageQueueクラスのリファレンスが対象とするバージョンが.NET Framework 1.1から4.8.1までであり、.NET(Core以降)向けの版が存在しないことについて。  2 3 4

  4. Microsoft Learn, .NET Framework official support policy. .NET Framework 4.8がWindowsオペレーティングシステムのコンポーネントとして定義され、インストールされている親製品(OS)のライフサイクルポリシーに従ってサポートされることについて。  2

  5. Microsoft Learn(アーカイブ), Message Queuing FunctionsおよびMQSendMessageMQReceiveMessage. MSMQのWin32ネイティブAPI(MQCreateQueue、MQSendMessage、MQReceiveMessageなど)がC/C++アプリケーション向けに文書化されており、キューの作成・送信・受信をマネージドAPIを経由せずに行えることについて。  2

  6. Microsoft Learn, Use the Windows Compatibility Pack to port code to .NET. Windows互換パック(Microsoft.Windows.Compatibilityパッケージ)が.NET Framework専用APIへの依存を.NET移行時に補うために約2万のAPIを提供すること、その技術領域一覧(CodeDom、構成、Directory Services、Drawing、ODBC、ACL、WCF、レジストリ、WMI、パフォーマンスカウンター、Windowsサービス、EventLog等)にメッセージング(System.Messaging)が含まれていないことについて。  2

  7. CoreWCF project, CoreWCF.MSMQ (NuGet) および CoreWCF 1.4.0 Preview release、Microsoft, CoreWCF Support Policy. CoreWCFがWCFのサーバー側を.NETに移植するコミュニティ主導プロジェクトであり、マイクロソフトが公式なサポートポリシーを提供していること、キュー系トランスポートの一環としてMSMQ対応(CoreWCF.MSMQパッケージ)が公開されていること、そのMSMQ実装が.NET Framework版System.Messagingライブラリのコミュニティ移植に依存していることについて。  2 3

  8. Microsoft Learn, BinaryFormatter migration guide. BinaryFormatterがセキュリティ上の理由から段階的に廃止され、.NET 9以降はランタイムから実装が削除されて既定では使用できないこと、および移行先としてJSON(System.Text.Json)等の安全な直列化形式が案内されていることについて。  2 3

  9. Microsoft Learn(アーカイブ), Express and Recoverable MessagingSystem-Generated QueuesMessage Queuing (MSMQ). expressメッセージが配送中も配送後もRAM上に保持され、メッセージが存在するコンピューターの停止やMSMQサービスの停止で失われること、recoverableメッセージが送信側と中継する各コンピューターでディスクに書き込まれ、宛先キューでもディスク上に保持されること、パブリックキューがディレクトリサービスに登録される一方でプライベートキューはローカルコンピューターにのみ登録されディレクトリサービスには公開されないこと、キューから取り出されたメッセージや送信済みメッセージの控えを保持するジャーナルキューと、配送できなかったメッセージを保持する配信不能(デッドレター)キューがMSMQの生成するシステムキューであることについて。 

  10. Microsoft Learn, テーブル ヒント (Transact-SQL). READPAST が他のトランザクションのロックしている行を読まずに飛ばすヒントであり、飛ばせるのは行レベルのロックだけでページレベルのロックは飛ばせないこと、READ COMMITTED または REPEATABLE READ の分離レベルでのみ指定できることについて。とくに、READ_COMMITTED_SNAPSHOT データベースオプションが ON に設定されていて、かつ (a) セッションのトランザクション分離レベルが READ COMMITTED である、または (b) クエリで READCOMMITTED テーブルヒントも指定されている、のいずれかが真の場合、READPAST テーブルヒントは指定できない。これらの場合に READPAST ヒントを指定するには、READCOMMITTED テーブルヒントがあれば削除し、クエリに READCOMMITTEDLOCK テーブルヒントを含める」という記述について。READCOMMITTEDLOCKREAD_COMMITTED_SNAPSHOT の設定にかかわらずロックによる READ COMMITTED を行わせるヒントであること、および1つのテーブルに対して粒度ヒント(PAGLOCK / NOLOCK / READCOMMITTEDLOCK / ROWLOCK / TABLOCK / TABLOCKX)を2つ以上指定できないことも同ページを参照。データベース側の設定は sys.databasesis_read_committed_snapshot_on で確認できます。  2 3 4

  11. Microsoft Learn, TOP (Transact-SQL). INSERT・UPDATE・MERGE・DELETE で TOP を使う場合、参照される行が何らかの順序で並べられるわけではないこと、および順序を決めたいときは TOP と ORDER BY を持つ副問い合わせを使うことについて。 

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

MSMQは廃止されたのですか?
いいえ。2026年7月時点で、Windowsクライアントの非推奨機能一覧にも、Windows Serverの「削除された機能・開発終了機能」一覧にも、MSMQは載っていません。Windowsのオプション機能として現行OSにも同梱されています。「廃止された」という言い方が広まっているのは、.NET側の状況と混同されているためです。MSMQを扱う標準クラスライブラリSystem.Messagingは.NET Frameworkにしか存在せず、.NET(Core以降)には移植されませんでした。つまり正確には「OS機能としては存続しているが、モダンな.NETからの公式な利用手段がない」という状態です。
.NET 8や.NET 10からMSMQを使う方法はありませんか?
公式のマネージドAPIはありません。System.Messagingは.NET Framework 4.8.1までのAPIで、Windows互換パック(Microsoft.Windows.Compatibility)にも含まれていません。MSMQのネイティブWin32 API(MQSendMessageなど)をP/Invokeで呼ぶことは技術的には可能ですが、フォーマッターやトランザクション連携を含むラッパーの自作と保守を引き受けることになります。コミュニティ側では、CoreWCFプロジェクトがMSMQトランスポート用のパッケージ(CoreWCF.MSMQ)を公開していますが、これはキュー経由で呼び出されるWCFサービスをモダンな.NETでホストするためのもの(WCFのサーバー側の移植)であり、System.Messagingの代わりになる汎用のキューAPIでも、送信側クライアントの代替でもありません。実装も.NET Framework版System.Messagingのコミュニティ移植に依存しています。検証や暫定的な延命の選択肢にはなります。CoreWCF自体にはマイクロソフトの公式なサポートポリシーがありますが、このMSMQ実装が依存するSystem.Messagingのコミュニティ移植まで同じ保証があるわけではないため、業務システムの前提に据えるなら、使うバージョンがサポートポリシーの対象かと、依存部分の扱いを確認したうえで判断すべきです。実務的には、アプリを.NETへ上げるタイミングでキューも移行するのが本筋です。
移行先はRabbitMQとAzure Service Busのどちらがよいですか?
その二択の前に、データベースのテーブルをキューにする案を検討してください。MSMQを使う中小規模の業務システムの多くは、キューの相手が自社のデータベースを更新する処理です。その場合、テーブルキューなら業務データと同じローカルトランザクションで「メッセージの取り出し」と「業務データの更新」を確定でき、MSMQ+分散トランザクション(DTC)で実現していた整合性を、より単純な仕組みで置き換えられます。テーブルキューで足りない要件、たとえば複数システム間の疎結合な配信や高いスループットが必要な場合に、オンプレ要件が強ければRabbitMQ、クラウドに置けるならAzure Service Busを検討する、という順番が失敗しにくいです。
当面は.NET Frameworkのまま塩漬けにする判断はありですか?
条件付きでありです。.NET Framework 4.8はWindowsのコンポーネントとしてOSのライフサイクルに沿ってサポートされ、MSMQ自体も非推奨になっていないため、「動き続ける」見込みは立てられます。ただし塩漬けを選ぶ場合は、キッティング手順にMSMQ機能の有効化を明記する、キュー長とジャーナルの監視を仕込む、メッセージ形式と接続構成をドキュメント化する、担当者の異動に備えて回復手順を残す、の4点を最低限やってください。塩漬けの本当のリスクは技術ではなく、「触れる人がいなくなること」です。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る