更新履歴(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)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「MSMQはいつまで使えるのか ── 「非推奨ですらない」レガシーキューの移行判断」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21739470 https://staging.comcomponent.com/blog/msmq-migration-decision-guide/
- DOI(最新版)
- 10.5281/zenodo.21739470
- DOI(この版)
- 10.5281/zenodo.21739471
「このシステム、MSMQを使っているんですが、廃止されたって聞きました。すぐ移行しないとまずいですか」──レガシー移行の相談で、ここ1年ほど繰り返し聞かれる質問です。
答えは少しねじれています。MSMQ(Microsoft Message Queuing)は、廃止どころか公式には非推奨にすらなっていません。2026年7月時点で、マイクロソフトの非推奨機能一覧にMSMQの名前はなく、現行のWindowsにもオプション機能として同梱されています。それなのに現場の感覚として「終わった技術」なのは、.NET側の公式なマネージドAPIが閉じているからです。MSMQを扱う標準ライブラリSystem.Messagingは.NET Frameworkにしか存在せず、.NET(Core以降)には移植されませんでした。
つまりMSMQは「明日止まる」技術ではなく、「アプリを.NETへ上げようとした瞬間に壁になる」技術です。
この記事の対象読者は、MSMQを使った既存システムの保守・移行を担当する開発者と、その判断を求められる情報システム部門の方です。「廃止されたらしい」という噂の真偽を確かめ、移行するかどうかを自分の環境の条件で決められる状態になることを目指します。
この記事では、噂と事実を切り分けたうえで、使い続ける・移行するの判断と、移行先の選び方を整理します。VB6や.NET Frameworkからの移行そのものは「VB6から.NETへの移行実務」「.NET Framework→.NET移行前チェックリスト」で扱っているので、本記事はキューの部分に絞ります。
1. まず結論
- MSMQは公式には非推奨になっていません。Windowsクライアントの非推奨機能一覧にも、Windows Serverの削除・開発終了機能一覧にも載っていません(2026年7月時点)。12
- しかし.NETの公式なマネージドAPIはありません。System.Messagingが対象とするのは.NET Framework 1.1〜4.8.1のみで、3 .NET移行時の互換手段であるWindows互換パックにも含まれていません。4 ネイティブWin32 APIをP/Invokeで呼ぶ道は残っていますが、延命手段の位置づけです(3章)。
- CoreWCFによる移植はありますが、対象はキュー経由のWCFサービス(受け側)の移行に限られ、条件を確認して使う経路です。CoreWCF自体にはマイクロソフトのサポートポリシーがある一方、MSMQトランスポート(CoreWCF.MSMQ)の実装は.NET Framework版System.Messagingのコミュニティ移植に依存しています。5
- 判断の軸は「アプリを.NETへ上げるかどうか」です。上げるなら、キューも一緒に移行するのが原則です(P/InvokeやCoreWCFで延命する道もありますが、保守負担を引き受ける判断になります)。上げない(塩漬け)なら、.NET Framework 4.8がOSコンポーネントとしてサポートされる間は動かし続ける計画が立てられます。6
- 移行先の第一候補は、メッセージブローカーではなくデータベースのテーブルキューです。MSMQ+分散トランザクション(DTC)で守っていた整合性は、業務DBと同一トランザクションで処理できるテーブルキューのほうが素直に置き換えられます(5章)。
- メッセージ形式の棚卸しを最初にやってください。BinaryFormatter系の直列化は.NET 9で実行時から削除されており、7 古いフォーマットのまま新旧併存させると詰まります(6章)。
この記事の知識マップ
MSMQはOS機能としては2026年7月時点でも非推奨になっていませんが、これを扱う標準ライブラリSystem.Messagingは.NET Frameworkにしか存在せず、.NET(Core以降)への公式な道はありません。CoreWCFによる移植やネイティブAPIのP/Invokeは延命手段止まりで、新規採用も推奨できません。移行判断の軸は「アプリを.NETへ上げるか」で、上げるならMSMQ+DTCが担っていた整合性を、業務データと同一のローカルトランザクションで確定できるDBのテーブルキューに置き換えるのが第一候補です。複数システム間の疎結合な連携が必要な場合に限り、RabbitMQやAzure Service Busを検討します。テーブルキューの実装ではREADPASTヒントとREAD_COMMITTED_SNAPSHOTデータベースオプションの組み合わせに注意が要ります。
flowchart LR
accTitle: MSMQ移行判断の知識マップ
accDescr: MSMQがOS機能としては存続する一方System.Messagingが.NET Frameworkに閉じていること、CoreWCF.MSMQやネイティブAPIによる延命手段、DTCが移行の最難関になること、DBのテーブルキューがMSMQ+DTCの整合性を置き換える第一候補であること、RabbitMQ/Azure Service Busが向く要件、BinaryFormatterの扱い、READPASTとREAD_COMMITTED_SNAPSHOTの排他関係の関係を示す図
msmq["MSMQ(Microsoft Message Queuing)"]
system_messaging["System.Messaging"]
dtc["DTC(分散トランザクションコーディネーター)"]
transactional_queue["トランザクションキュー"]
express_message["expressメッセージ"]
recoverable_message["recoverable(回復可能)メッセージ"]
journal_queue["ジャーナルキュー"]
dead_letter_queue["配信不能(デッドレター)キュー"]
private_queue["プライベートキュー"]
message_loss_on_restart["再起動時のメッセージ喪失"]
dotnet_framework[".NET Framework"]
dotnet[".NET(Core以降)"]
msmq_native_api["MSMQのWin32ネイティブAPI"]
corewcf["CoreWCF"]
table_queue["DBのテーブルキュー"]
rabbitmq["RabbitMQ"]
azure_service_bus["Azure Service Bus"]
multi_system_messaging["複数システム・複数言語間の疎結合な連携"]
binaryformatter["BinaryFormatter"]
outbox_pattern["Outboxパターン"]
readpast_hint["READPASTテーブルヒント"]
read_committed_snapshot["READ_COMMITTED_SNAPSHOTデータベースオプション"]
new_project_adoption["新規プロジェクトでの新規採用"]
msmq -.->|"利用する"| dtc
msmq -->|"利用する"| transactional_queue
msmq -->|"利用する"| express_message
msmq -.->|"利用する"| recoverable_message
msmq -->|"利用する"| journal_queue
msmq -->|"利用する"| dead_letter_queue
msmq -.->|"利用する"| private_queue
express_message -.->|"原因になり得る"| message_loss_on_restart
recoverable_message -->|"防止する"| message_loss_on_restart
system_messaging -->|"前提とする"| dotnet_framework
system_messaging -->|"両立しない"| dotnet
dotnet -.->|"利用する"| msmq_native_api
msmq_native_api -->|"前提とする"| msmq
corewcf -->|"利用する"| system_messaging
table_queue -->|"の後継"| msmq
transactional_queue -.->|"前提とする"| dtc
table_queue -->|"推奨される対応"| dtc
dtc -->|"両立しない"| rabbitmq
dtc -->|"両立しない"| azure_service_bus
rabbitmq -.->|"推奨される対応"| multi_system_messaging
azure_service_bus -.->|"推奨される対応"| multi_system_messaging
table_queue -.->|"用いるのは非推奨"| multi_system_messaging
binaryformatter -.->|"両立しない"| dotnet
table_queue -.->|"両立しない"| binaryformatter
table_queue -.->|"利用する"| outbox_pattern
table_queue -.->|"利用する"| readpast_hint
readpast_hint -.->|"両立しない"| read_committed_snapshot
msmq -->|"用いるのは非推奨"| new_project_adoption
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全28件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. MSMQを30秒で
MSMQはWindowsに同梱されてきたメッセージキュー基盤です。アプリケーションはキューにメッセージを書き、別のアプリケーション(同じマシンでも別のマシンでも)が都合のよいタイミングで取り出します。特徴は次の3点に集約されます。
- store-and-forward: 送信先が落ちていてもメッセージはローカルに蓄積され、回復後に届く。拠点間の不安定な回線に強い。ただしこの永続性は無条件ではなく、既定の高速(express)メッセージはメモリ上に留まり得て、MSMQサービスやマシンの再起動で失われます。ディスクに永続化されるのは、送信側でRecoverable(回復可能)を指定したメッセージか、トランザクションメッセージです。
- トランザクション: キュー操作をトランザクションにでき、MS DTC(分散トランザクションコーディネーター)と組み合わせると「キューからの取り出し」と「データベース更新」を1つの分散トランザクションで確定できる。
- OS同梱: 追加のミドルウェア導入なしで使えたため、2000年代の業務システム、とくに受発注データの連携、帳票の非同期処理、製造現場の工程間連携などで広く採用された。
この「OS同梱で、トランザクションが効いて、オフラインに強い」という組み合わせが優秀だったからこそ、いまも現役で残っています。移行先を考えるときも、この3点のどれを実際に使っているかが判断の中心になります。
本記事で使う用語の最小セット
5章の判断表と6章の棚卸しは、次の用語を前提にしています。ここで一度に押さえておいてください。8
- プライベートキュー ── ディレクトリサービス(Active Directory)には発行せず、そのコンピューター上にだけ登録されるキュー。
.\private$\キュー名のような形式で指定します。対するパブリックキューはディレクトリサービスに登録され、ドメイン内から検索できます。中小規模の業務システムで使われているのは、ほとんどがプライベートキューです。 - expressメッセージ ── 既定の送信モード。配送中も配送後もメモリ上に置かれるため高速ですが、MSMQサービスやマシンが止まると失われます。
- recoverable(回復可能)メッセージ ── 送信側と中継する各コンピューターでディスクに書かれ、宛先キューでもディスク上に保持されるモード。再起動をまたいで残ります。送信側が明示的に指定します。
- トランザクションキュー ── トランザクションメッセージだけを扱うキュー。キューの作成時に決まり、後から変更できません(6章の移行手順に効いてきます)。
- DTC(分散トランザクションコーディネーター) ── 複数のリソース(MSMQのキューとデータベースなど)にまたがるトランザクションを調停するWindowsのサービス。「キューからの取り出し」と「DB更新」をひとまとまりで確定させる仕組みで、5章の移行判断で最大の争点になります。
- ジャーナルキュー / 配信不能(デッドレター)キュー ── MSMQが自動生成するシステムキュー。ジャーナルは「送信した/取り出したメッセージの控え」を、配信不能キューは「届けられなかったメッセージ」を溜めます。放置すると容量を食うため、塩漬け運用では監視対象になります(7章)。
3. 事実関係 ── 「廃止された」は正確ではない
「MSMQは使えるのか」という問いが混乱するのは、レイヤーによって答えが違うのに、ひとまとめに語られるからです。先に全体像を1枚の表にしておきます。
| レイヤー | 現在の状態 | 実務上の意味 |
|---|---|---|
| OS機能(Windowsのオプション機能) | 存続。現行のWindowsクライアント/Serverに同梱され、非推奨宣言も出ていない12 | 有効化すれば今も動く。OSのサポート期間の中では使い続けられる |
Win32ネイティブAPI(MQSendMessage など) |
文書化されており、利用可能9 | P/Invokeで.NETから呼ぶ道は残る。ただしフォーマッターやトランザクション連携は自前で書いて保守することになる |
| .NET Framework の System.Messaging | 利用可能。ただし対象は.NET Framework 1.1〜4.8.13 | 既存システムはここで動いている。この先が無い |
| .NET(Core以降)の公式マネージドAPI | 存在しない。Windows互換パックにも含まれない4 | アプリを.NETへ上げる瞬間に、キュー部分の作り直しが必要になる |
| WCFのMSMQバインディング → CoreWCF.MSMQ | コミュニティ主導の移植が存在。CoreWCF自体にはサポートポリシーがあるが、MSMQ実装はSystem.Messagingのコミュニティ移植に依存5 | キュー経由で呼ばれるWCFサービス(受け側)の延命には使えるが、送信側の代替でも汎用のキューAPIでもない |
| 新規採用 | 推奨できない | 公式な将来の道がないため |
以下、この表の各行の根拠を順番に確認します。
第一に、MSMQは非推奨リストに載っていません。Windowsクライアントの「Deprecated features」一覧を見ると、NTLMやVBScript、WordPadは載っていますが、MSMQの項目はありません。1 Windows Server側の「Features Removed or No Longer Developed」一覧でも、Windows Server 2025のタブを含めてMSMQは挙げられていません。2 非推奨(deprecated)は「積極的な開発をやめた」という公式宣言であり、その宣言すら出ていない、というのが現在地です。
第二に、System.Messagingは.NET Frameworkで止まっています。リファレンスの対象バージョンは.NET Framework 1.1から4.8.1までで、.NET(Core以降)の版は存在しません。3 .NET Framework専用APIの受け皿であるWindows互換パック(Microsoft.Windows.Compatibility)は、レジストリ・WMI・Windowsサービス・EventLogなど約2万のAPIを提供しますが、その技術領域一覧にメッセージング(System.Messaging)は含まれていません。4 正確に言えば、閉じているのはマネージドAPIです。MSMQのWin32ネイティブAPI(MQSendMessage、MQReceiveMessage など)は現在も文書化されており、.NETからP/Invokeで呼ぶこと自体は可能です。ただしSystem.Messagingが吸収していたフォーマッターやトランザクション連携を自前のラッパーとして書き、保守し続けることになるため、「使い続けるための本命」ではなく「どうしても残す場合の延命手段」の位置づけです。
第三に、WCFのMSMQ連携も同じ壁の中にあります。.NET FrameworkのWCFにはMSMQを下回りに使うバインディングがありましたが、この経路をモダンな.NETで再現しようとすると、コミュニティ主導のCoreWCFに行き着きます。CoreWCFはキュー系トランスポートの一環としてMSMQ対応パッケージ(CoreWCF.MSMQ)を公開していますが、その実装は.NET Framework版System.Messagingライブラリのコミュニティ移植への依存を明示しています。5 動くかどうかで言えば動きますし、CoreWCF自体にはマイクロソフトの公式なサポートポリシーが用意されているので、「完全に野良のライブラリ」というわけでもありません。5 ただし、MSMQトランスポートが依存するSystem.Messagingのコミュニティ移植までが同じ扱いかは別の問題です。採用するなら、使うバージョンがサポートポリシーの対象かと、この依存部分の扱いを確認したうえで判断してください。
以上が、冒頭に置いた一覧表の各行の根拠です。「MSMQが廃止された」のではなく、「モダンな.NETからMSMQへ至る公式な道が無い」──この区別を持っておくと、社内の議論が噛み合うようになります。
4. 「動いているのに問題」の正体
MSMQが絡むシステムの相談は、たいてい障害ではなく移行の見積もりから始まります。問題の正体は、MSMQそれ自体ではなく、MSMQがアプリ全体を.NET Frameworkに縛る錨(いかり)になることです。
.NET Framework 4.8はWindowsのコンポーネントとして扱われ、インストールされているOSのライフサイクルに沿ってサポートされます。6 だから「動き続けるか」という問いには「当面は動き続ける」と答えられます。それでも次の項目は、時間とともに確実に重くなります。
- 人が採れない・引き継げない。System.MessagingとDTCを説明できる技術者は年々減っています。「ソースも資料もないシステムの保守」で書いた構図の典型例です。
- ランタイムと新しいライブラリの恩恵が受けられない。新しいC#の構文にはコンパイラの更新だけで.NET Frameworkでも使えるものが多くありますが、ランタイム側の性能改善や新しい標準ライブラリは使えず、.NET Frameworkをサポート対象から外していく最近のパッケージも選べなくなっていきます。
- 分散トランザクションが移行の最難関になる。MSMQ+DTCの「キュー取り出しとDB更新をアトミックに」という設計は、クラウド系のキューサービスでは再現できません。ここを放置したまま周辺だけ.NET化すると、最後にいちばん硬い芯が残ります。
- 直列化形式の時限爆弾。古いシステムのメッセージ本文はバイナリ直列化になっていることがあり、BinaryFormatterは.NET 9で実行時から削除されました。7 移行時にメッセージ形式ごと見直す必要があります。
つまり「MSMQはいつまで使えるか」という問いの実務的な答えは、「OSがサポートする限り動くが、移行の難易度は待つほど上がる」です。判断を先送りするにしても、次章の判断表で「どこが難所か」だけは先に把握しておくべきです。
5. 移行先の判断表
移行先は「MSMQの後継製品はどれか」ではなく、実際に使っている性質ごとに選びます。
5.1. まず要件を4つの質問に落とす
- キューの相手(受け側の処理)は、自社のデータベースを更新する処理か?
- 分散トランザクション(DTC)を使っているか?(トランザクションキュー+DB更新)
- 送信側と受信側は別マシン・別拠点か?オフライン耐性(store-and-forward)は本当に使っているか?
- メッセージ量は実際どの程度か?(多くの業務システムでは1日数千〜数万件で、これはどの選択肢でも余裕です)
5.2. 判断表
| 使っている性質 | 第一候補 | 理由と注意 |
|---|---|---|
| 同一システム内の非同期処理(受け側は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判断表」へ |
5.3. テーブルキューの最小実装
「テーブルキュー」と言われてもピンと来ない、という声が多いので、最小の形を示します。まずテーブル定義です(SQL Server)。
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);
取り出しは、1件だけ「処理中」に印を付けながら本文を受け取ります。READPAST を付けることで、他のワーカーがロックしている行を待たずに飛ばせます(複数プロセスで同時に回せます)。
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;
READPAST は、READ_COMMITTED_SNAPSHOT が ON のデータベースでは、そのままでは使えません。公式ドキュメントは「READ_COMMITTED_SNAPSHOT が ON で、かつセッションの分離レベルが READ COMMITTED のとき(または READCOMMITTED ヒントを併用しているとき)、READPAST は指定できない」と明記し、その場合の対処として READCOMMITTEDLOCK ヒントを併記することを挙げています。10 既定の分離レベルは READ COMMITTED なので、READ_COMMITTED_SNAPSHOT を有効にしただけのデータベースでは、この取り出しは「他のワーカーの行を飛ばす」どころか、文そのものがエラーになります。Azure SQL Database では既定で ON ですし、オンプレでも読み取りのブロッキング対策として有効にしている環境は珍しくありません。移行先のDBがどちらかは、先に確認してください。
SELECT is_read_committed_snapshot_on FROM sys.databases WHERE name = DB_NAME();
よく見る WITH (READPAST, UPDLOCK, ROWLOCK) から ROWLOCK を落としているのは、ROWLOCK と READCOMMITTEDLOCK が同じ「粒度ヒント」のグループで、1つのテーブルに両方は指定できないためです。10 READPAST が飛ばせるのは行ロックだけでページロックは飛ばせないので、ROWLOCK を明示したいところですが、TOP (1) の索引シークで取る 1 行に対してエスカレーションが起きることは実質ありません。READ_COMMITTED_SNAPSHOT が OFF だと分かっていて ROWLOCK を明示したいなら、READCOMMITTEDLOCK のほうを外してください。
ORDER BY を省いて UPDATE TOP (1) と書くと、どの行が選ばれるかは不定になります。UPDATE の TOP は対象行を並べ替えないと公式に明記されており、(Status, Id) のインデックスがあっても取り出し順の保証にはなりません。11 投入順に処理されることが前提の連携では、古いジョブが後回しにされ続ける形の不具合になります。
ただし、ORDER BY Id が揃えるのは取り出す順番だけです。処理が終わる順番は揃いません。READPAST は「他のワーカーがロックしている行を飛ばす」ための指定なので、ワーカーAがジョブ1を掴んで長くかかっている間に、ワーカーBはジョブ2を掴んで先にコミットできます。
| 揃うもの | 揃わないもの |
|---|---|
どの行を先に取り出すか(ORDER BY Id) |
どの行が先に業務データへ反映されるか |
「同じ受注番号への更新が入れ替わると困る」のように反映順に意味がある連携では、これでは足りません。取れる形は次のどちらかです。
- 消費者を1本にする。並列度を捨てて順序を取ります。スループットが足りるなら、これが一番単純で壊れにくい形です
- キー単位で分ける。受注番号などのキーでワーカーを固定するか、「同じキーの行が処理中なら取り出さない」条件を取り出し側に足して、キーの中でだけ順序を保証します。キーをまたいだ順序は諦めます
どちらも取らないのであれば、並列に回した時点で全体のFIFOは無いことを、移行の設計として明記しておいてください。MSMQ から移ってくる場合、ここは特に見落とされます。MSMQ のキューも並列に受信すれば同じことが起きますが、「キューだから順番どおり」という理解のまま移ってくると、テーブルキューにした途端に順序の問題が出たように見えます。
受け側のワーカーは、これを業務処理と同じローカルトランザクションの中で回すだけです(C#、擬似コード)。
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行で同時に確定する
}
ここがMSMQ+DTCの置き換えになっている点です。MSMQでは「キューからの取り出し」と「DBの更新」が別のリソースだったため、両者をまとめるのに分散トランザクション(DTC)が必要でした。キューがDBのテーブルなら、両方とも同じデータベースへの更新なので、普通のローカルトランザクションで済みます。RabbitMQやAzure Service Busを入れると、この性質は失われ(キューとDBは別リソースに戻り)、冪等性や再送でアプリ側に作り込むことになります。
実運用で足す必要があるのは、次の3つくらいです。
- 処理中のまま落ちた行の回収。
Status = 1かつStartedAtが一定時間より古い行をStatus = 0に戻す復旧処理を、定期実行に入れます。 - リトライ上限と退避。
RetryCountが上限を超えた行は別テーブル(またはStatus = 9)へ寄せます。MSMQの配信不能キューに相当する置き場です。 - 完了行の掃除。
Status = 2の行を定期的に削除・アーカイブします。放っておくとテーブルが太り、インデックスの効きが落ちます。
送信側では、業務データの更新とメッセージ行のINSERTを同じトランザクションに入れます(Outboxパターン)。これで「業務データは更新されたのにメッセージが出ていない」という不整合も消えます。
強調したいのは、中小規模の業務システムではテーブルキューが第一候補になることが多いという点です。メッセージキュー製品の比較記事は「RabbitMQ vs Kafka vs Service Bus」という形になりがちですが、MSMQ世代のシステムがキューに載せているのは多くの場合「同じDBを更新する非同期ジョブ」であり、それはDBのテーブルとポーリング(または通知)で十分に、しかもより単純に実現できます。新しいミドルウェアをひとつ増やすことの運用コスト(監視・冗長化・パッチ・担当者教育)は、中小の体制では特に重くつきます。
6. 移行の実務手順
進め方は、レガシー移行の定石どおり「観測してから動かす」です。
-
依存の棚卸し。コードから
System.Messagingの参照とMessageQueueの生成箇所を洗い出します。あわせて、WCF構成ファイルのnetMsmqBinding/msmqIntegrationBinding、ネイティブAPI(MQSendMessageなどのMQ*関数)やCOM経由の呼び出しも検索してください。System.Messagingを参照していなくてもMSMQに依存している経路はあり得ます。確認する観点は、(a) キューのパス(ローカルかリモートか、プライベートかパブリックか)、(b) トランザクションキューか、メッセージをRecoverableにしているか(どちらでもなければ現行システムは再起動でメッセージが失われる前提で動いている)、(c) フォーマッター(XmlMessageFormatter / BinaryMessageFormatter / ActiveXMessageFormatter)、(d) ジャーナル・配信不能キューの使用、(e) 誰が作成・削除しているか(インストーラーかアプリか)、です。棚卸しは、確認しただけでは移行判断に効きません。次の形でキュー1本につき1行を埋めて、そのまま移行方針の根拠にしてください(そのまま稟議資料の別紙にできます)。
観点 どこを見るか 記録する内容 判定への効き方 (a) キューのパス MessageQueueに渡すパス文字列、構成ファイルの接続先、FormatName:指定ローカル/リモートの別、プライベート/パブリックの別、相手のマシン名 リモート・拠点間ならstore-and-forwardの要否を判定(5.2の「オフライン耐性」行)。ローカル完結なら5.2の1行目に落ちる (b) トランザクションとRecoverable キュー作成箇所(トランザクションキューか)、送信時にRecoverableを指定しているか、DTCを使っているか トランザクションキューか否か、Recoverable指定の有無、DTC参加の有無 DTCありなら移行先はテーブルキューがほぼ確定。どちらも無い場合は、現行が「再起動でメッセージが消える前提」で運用されていることを明記する (c) フォーマッター XmlMessageFormatter/BinaryMessageFormatter/ActiveXMessageFormatterの指定箇所使用しているフォーマッターと、メッセージ本体の型 BinaryFormatter系ならJSONへの置き換えが必須(手順2)。移行工数の主要因になる7 (d) ジャーナル・配信不能キュー キューのプロパティ(ジャーナル有効か)、システムキューの中身、運用手順書 ジャーナルの利用有無、配信不能キューを実際に見ているか 「失敗メッセージをどう扱っているか」が移行先の設計要件になる(テーブルキューなら退避テーブル) (e) 誰が作成・削除しているか インストーラー、配置スクリプト、アプリ起動時の Create呼び出し作成主体と、権限設定を誰が行っているか 移行時に「新キューを誰が用意するか」がそのまま置き換わる。塩漬けを選ぶ場合はキッティング手順に転記する(7章) - メッセージ形式を決める。移行後の形式はJSONを既定にします。持ち込んではいけないのは、BinaryMessageFormatterのようなBinaryFormatter系の直列化です。BinaryFormatterは.NET 9で削除済みで、セキュリティ上も推奨されません。7 一方、Protocol BuffersやMessagePackのように仕様が独立して保守されているバイナリ形式を使っているなら、その形式を新システムへそのまま運ぶこと自体は問題ありません。
- 受け側から移行する。新しいキュー(たとえばテーブルキュー)を先に用意し、受け側の処理を新キュー対応にしてから、送信側を切り替えます。移行期間中は、旧MSMQから新キューへメッセージを移す小さなブリッジ(これは.NET Frameworkのままでよい)を挟むと、送信側を一斉に切り替えずに済みます。ただしブリッジは「MSMQからの取り出し」と「新キューへの書き込み」の間で落ちると、メッセージを失うか二重に流します。移行元がトランザクションキューなら、MSMQ側をトランザクション受信にして書き込み失敗時に戻せるようにし、新キュー側はメッセージIDで重複を弾ける(冪等な)設計にするのが最低条件です。移行元が非トランザクションキューの場合はこの手が使えません。キューのトランザクション属性は作成時に決まり、後から変えられないためです。その場合は「Peekで読む→新キューへ冪等に書く→書けたことを確認してから取り除く」の順で組みます(取り除く前に落ちても、重複は新キュー側の冪等性が吸収します)。どちらの作り込みも規模に見合わないなら、ブリッジを挟まずに手順5の「空にして切り替え」へ寄せるほうが安全です。
- 挙動を固定してから書き換える。キュー処理はタイミング依存のバグが出やすい領域です。移行前に「この入力メッセージならこの結果」という特性テストを用意しておくと、置き換え後の検証が機械的になります(「特性テストで挙動を固定してからリファクタリングする」)。
- キューの中身が空の状態で切り替える。メッセージが残った状態での切り替えは、二重処理・欠落の温床です。計画停止でキューを吐き切ってから切り替えるのが、結局いちばん安全で速い方法です。
7. 塩漬けを選ぶ場合の最低条件
「アプリを.NETへ上げる計画がなく、費用対効果からも当面移行しない」という判断は、条件付きで合理的です。その場合の最低条件を、稟議・レビュー資料にそのまま転記できるチェックリストの形で挙げます。4つすべてに印が付いてはじめて「塩漬け」が選択肢になる、と考えてください。
| 確認 | 最低条件 | 具体的にやること | 満たさないと何が起きるか |
|---|---|---|---|
| □ | キッティング・リストア手順にMSMQを明記する | MSMQはWindowsのオプション機能。「Windowsの機能の有効化」またはDISM/PowerShellでの有効化手順を、環境構築手順書に書いておく | PC・サーバーの入れ替え時に有効化を忘れ、移行当日に原因不明で動かない |
| □ | キュー長を監視する | 滞留件数のしきい値監視と通知を仕込む。配信不能キューとジャーナルキューも対象に含める | 受け側が止まってもエラーにならず溜まり続けるため、誰も気づかないまま業務が止まる |
| □ | 構成をドキュメント化する | キューのパス、権限、トランザクションの有無、フォーマッター、ジャーナル設定を記録する(6章の棚卸し表がそのまま使える) | 将来の移行見積もりが調査からやり直しになり、精度も工数も悪化する |
| □ | 年1回、判断を見直す | 「非推奨リストに載ったか」「OSの更新で挙動が変わっていないか」を年次で確認する | 非推奨の告知が出てから慌てて動くことになる |
4つ目は特に軽く見られがちですが、この年次確認があるからこそ、告知が出てからでも慌てずに済みます。逆にこの4つが埋まらない環境での塩漬けは、「見えないまま止まるのを待っている」のと同じです。
8. まとめ
- MSMQは2026年7月時点で公式に非推奨になっておらず、OS機能としては存続しています。「廃止済み」という認識は正確ではありません。12
- 一方でSystem.Messagingは.NET Framework専用のまま止まり、互換パックにも含まれず、.NET(Core以降)への公式な道はありません。CoreWCF自体にはサポートポリシーがあるものの、そのMSMQトランスポートはSystem.Messagingのコミュニティ移植に依存します。345
- 判断の軸は「アプリを.NETへ上げるか」です。上げるならキューも一緒に移行するのが原則(P/Invoke等での延命は保守負担との交換)、塩漬けなら監視とドキュメントを条件に成立します。6
- 移行先の第一候補はDBのテーブルキューです。MSMQ+DTCの整合性はローカルトランザクションで置き換えるのが本質で、ブローカー製品の導入はその後に検討します。
- BinaryFormatter系の直列化は.NET 9での削除により新システムへ持ち込めません。移行時はJSONを既定にしつつ、protobuf等の独立して保守されている形式ならそのままでも構いません。7
- 受け側→ブリッジ→送信側の順で切り替え、特性テストで挙動を固定してから書き換えるのが、事故らない進め方です。
関連記事
- VB6アプリはいつまで動くのか ── ランタイムのサポート状況と現実的な.NET移行の進め方
- .NET Framework→.NET移行前チェックリスト
- 特性テスト(Characterization Test)で挙動を固定してからリファクタリングする
- WindowsのIPC(プロセス間通信)判断表
- TCPでSendした単位ごとにReceiveできるという誤解 ── バイトストリームとして扱うための受信設計
- ソースも資料もないシステムの保守をどう引き受けるか
- 業務アプリのDBスキーマ移行とバージョン管理
関連する相談領域
合同会社小村ソフトでは、MSMQを含むレガシー構成の棚卸しと移行計画、.NET Frameworkから.NETへの移行、キュー処理を含む業務システムの改修を扱っています。
参考リンク
-
Microsoft Learn, Deprecated features in the Windows client. Windowsクライアントで積極的な開発が終了した(非推奨の)機能の公式一覧。2026年7月時点の一覧にNTLM、VBScript、WordPad等は掲載されている一方、MSMQ(Microsoft Message Queuing)は掲載されていないこと、および非推奨(deprecated)が「積極的に開発されておらず将来の更新で削除される可能性がある」段階であり削除(removed)とは区別されることについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Features Removed or No Longer Developed in Windows Server. Windows Serverで削除された機能と開発終了(非推奨)機能の公式一覧。Windows Server 2025のタブを含む2026年7月時点の一覧にMSMQが掲載されていないこと、非推奨のコンポーネントもWindows Serverに同梱され続け、製品ライフサイクルに従って本番環境向けにサポートされ、セキュリティ更新と品質更新を受け続けることについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MessageQueue Class (System.Messaging). System.Messaging.MessageQueueクラスのリファレンスが対象とするバージョンが.NET Framework 1.1から4.8.1までであり、.NET(Core以降)向けの版が存在しないことについて。 ↩ ↩2 ↩3 ↩4
-
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 ↩3 ↩4
-
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 ↩4 ↩5
-
Microsoft Learn, .NET Framework official support policy. .NET Framework 4.8がWindowsオペレーティングシステムのコンポーネントとして定義され、インストールされている親製品(OS)のライフサイクルポリシーに従ってサポートされることについて。 ↩ ↩2 ↩3
-
Microsoft Learn, BinaryFormatter migration guide. BinaryFormatterがセキュリティ上の理由から段階的に廃止され、.NET 9以降はランタイムから実装が削除されて既定では使用できないこと、および移行先としてJSON(System.Text.Json)等の安全な直列化形式が案内されていることについて。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn(アーカイブ), Express and Recoverable Messaging、System-Generated Queues、Message Queuing (MSMQ). expressメッセージが配送中も配送後もRAM上に保持され、メッセージが存在するコンピューターの停止やMSMQサービスの停止で失われること、recoverableメッセージが送信側と中継する各コンピューターでディスクに書き込まれ、宛先キューでもディスク上に保持されること、パブリックキューがディレクトリサービスに登録される一方でプライベートキューはローカルコンピューターにのみ登録されディレクトリサービスには公開されないこと、キューから取り出されたメッセージや送信済みメッセージの控えを保持するジャーナルキューと、配送できなかったメッセージを保持する配信不能(デッドレター)キューがMSMQの生成するシステムキューであることについて。 ↩
-
Microsoft Learn(アーカイブ), Message Queuing FunctionsおよびMQSendMessage、MQReceiveMessage. MSMQのWin32ネイティブAPI(MQCreateQueue、MQSendMessage、MQReceiveMessageなど)がC/C++アプリケーション向けに文書化されており、キューの作成・送信・受信をマネージドAPIを経由せずに行えることについて。 ↩
-
Microsoft Learn, テーブル ヒント (Transact-SQL).
READPASTが他のトランザクションのロックしている行を読まずに飛ばすヒントであり、飛ばせるのは行レベルのロックだけでページレベルのロックは飛ばせないこと、READ COMMITTEDまたはREPEATABLE READの分離レベルでのみ指定できることについて。とくに、「READ_COMMITTED_SNAPSHOTデータベースオプションがONに設定されていて、かつ (a) セッションのトランザクション分離レベルがREAD COMMITTEDである、または (b) クエリでREADCOMMITTEDテーブルヒントも指定されている、のいずれかが真の場合、READPASTテーブルヒントは指定できない。これらの場合にREADPASTヒントを指定するには、READCOMMITTEDテーブルヒントがあれば削除し、クエリにREADCOMMITTEDLOCKテーブルヒントを含める」という記述について。READCOMMITTEDLOCKがREAD_COMMITTED_SNAPSHOTの設定にかかわらずロックによるREAD COMMITTEDを行わせるヒントであること、および1つのテーブルに対して粒度ヒント(PAGLOCK/NOLOCK/READCOMMITTEDLOCK/ROWLOCK/TABLOCK/TABLOCKX)を2つ以上指定できないことも同ページを参照。データベース側の設定は sys.databases のis_read_committed_snapshot_onで確認できます。 ↩ ↩2 -
Microsoft Learn, TOP (Transact-SQL). INSERT・UPDATE・MERGE・DELETE で TOP を使う場合、参照される行が何らかの順序で並べられるわけではないこと、および順序を決めたいときは TOP と ORDER BY を持つ副問い合わせを使うことについて。 ↩
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
VB6アプリはいつまで動くのか ── ランタイムのサポート状況と現実的な.NET移行の進め方
VB6アプリはいつまで動くのか。ランタイムはWindows 11でも動作対象、IDEはサポート終了という現状を整理し、全面リライト・自動変換・段階移行の判断表、移行前の棚卸し、VB6と.NETの非互換までをまとめます。
マルチスレッドの実務ベストプラクティス .NET編 ── スレッドを増やす前に決めておくこと
「スレッドを立てたら、たまに落ちる・固まる」を防ぐ設計の定石を.NET/C#向けに整理。スレッドを自分で作らずTaskに乗る、共有可変状態を減らす、ロックの規律、CancellationTokenでの停止設計、UIスレッドの扱いまで解説します。
WMI/CIMをC#・PowerShellから使う ── ハードウェア情報取得・プロセス監視・リモート照会の実務ガイド
PCのシリアル番号取得、ディスク空き監視、プロセス起動検知の定番がWMI/CIMです。Get-CimInstance等のCIMコマンドレットの使い方と旧Get-WmiObjectからの移行、C#のSystem.ManagementとCIM APIの使い分け、実例レシピと落と...
WindowsのTPMとは何か ── 図解でわかる「鍵を外に出さない金庫」と測定起動
TPMを図解で解説します。鍵をチップの外へ出さない仕組み、PCRと測定起動、BitLockerやWindows Helloでの使われ方、dTPM・fTPM・Plutonの違い、Get-Tpmでの確認方法、回復キーを求められたときの対処までを実務目線で整理します。
DLL・COMインターフェースの後方互換性 ── どの変更が呼び出し側を壊すのかの判断表
DLLやCOMコンポーネントのどの変更が呼び出し側を壊すのか。バイナリ互換・ソース互換・動作互換の3層を整理し、変更内容別の判断表、COMインターフェース不変の鉄則、semver運用までを実務ガイドとしてまとめます。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
ActiveX / 移行テーマ
COM / ActiveX / OCX を残すか、包むか、置き換えるかを整理するトピックです。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
既存Windowsソフトの改修・保守
既存 Windows ソフトの機能追加、保守、段階的モダナイゼーションを支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- 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点を最低限やってください。塩漬けの本当のリスクは技術ではなく、「触れる人がいなくなること」です。