LASSIC Media らしくメディア
レースコンディションの原因と対策|処理の競合を防ぐ方法
監修・編集責任者:牛尾 昭昌(株式会社LASSIC 執行役員)
この記事の結論
- レースコンディションは、複数の処理の同時アクセスとタイミングのずれによって、結果が変わってしまう不具合です。
- 銀行残高の更新や在庫数の管理など、共有データを読み書きする処理で起きやすく、通常のテストでは見つけにくくなります。
- 対策には、アトミックな操作やトランザクション、冪等性の設計、キューによる直列化など複数の考え方があります。
※ 本記事は2026年9月時点の公式情報(省庁・公的機関、および製品やサービスを提供する事業者が公開している資料)に基づきます。
複数の利用者が同時に操作すると、まれに残高の数字が合わなくなる。同じ注文が二重に登録されたのに、エラーログには目立った記録が無く、同じ手順を繰り返しても再現しない——。多くの利用者が同時に使う業務システムでは、こうした不具合が起こりがちです。その代表的な原因がレースコンディション(競合状態)です。レースコンディションとは、複数の処理が同時に同じ資源(データやファイルなど)へアクセスし、実行のタイミングの違いによって結果が変わってしまう不具合のことです。
発生の仕組みを知っておくと、開発会社とのレビューで仕様の抜けに気付きやすくなります。ただし、どれか一つの対策で済むわけではありません。本記事では、発注者やプロジェクトマネージャーに向けて、競合が起きる仕組み、典型的な不具合、見つかりにくい理由と対策の考え方、そして外部に委託するときに確認したい点を整理します。
目次
レースコンディションとは
レースコンディションが厄介なのは、起きるかどうかが処理のタイミングの重なりに左右される点です。通常のプログラムの不具合は、同じ入力を与えれば同じ結果になるため、原因の箇所を絞り込みやすいものです。レースコンディションでは、同じ入力・同じコードであっても、処理がどの順番で実行されるかによって結果が変わります。
米国MITRE社が公開している脆弱性の分類CWEでは、CWE-362として、共有リソースへの排他制御(同時アクセスの制限)が不十分なまま並行して実行される問題として定義されています。*1
競合状態が重大な事故につながった例として、放射線治療装置「Therac-25」が知られています。操作者がパネルへの入力を素早く行うと本来の保護機構を回避できてしまい、過剰照射による死亡事故が起きました。処理のタイミングしだいで想定した制御が効かなくなる点は、業務システムでも同じです。
なぜ起きるのか
レースコンディションは、複数の処理が同じ資源——データベースのレコード、ファイル、メモリ上の変数など——へ同時にアクセスする場面で起きます。とくに起きやすいのは、「値を読み込む」「計算する」「書き戻す」という一連の操作の途中に、別の処理が割り込む場合です。これらの操作は内部では複数の段階に分かれていて、ひとまとまり(アトミック、不可分)に実行される保証はありません。入金と出金が同じ口座に同時に届いた場合を図にしました。
入金処理Aと出金処理Bが、同じ残高100を同時に読み込みます。処理Aは50を加えて150を書き込み、処理Bは出金額を引いた70で上書きします。最終的な残高は70になり、処理Aが加えた50の分は消えてしまいます。この現象は「lost update(更新の消失)」と呼ばれ、レースコンディションの典型例です。
もう一つの典型が、TOCTOU(確認と使用の間の競合)です。「在庫が1個残っているか確認する」処理と「注文を確定する」処理の間に別の注文が割り込むと、確認したときには在庫があったのに、確定するときにはもう無い、という状態が生まれます。
共有資源はデータベースのレコードに限りません。ファイルやディレクトリ、キャッシュサーバー上の値など、複数の処理から読み書きされるものはすべて対象になります。サーバーを複数台にして負荷を分散しているシステムでは、1台のサーバーの中だけでなく、サーバーをまたいだ競合にも注意が要ります。処理を並行させて速くするほど、同じデータに同時にアクセスする機会も増えます。
マイクロサービス(小さなサービスの組み合わせ)の構成では、複数のサービスが互いの完了を待たずにメッセージをやり取りしながら1つの処理を完成させます。処理が終わる順番を制御しにくい構成ほど、競合が起きる設計になりやすくなります。同時アクセスは、夜間のバッチ処理と利用者の操作が重なったときや、通信エラーで自動的に再送したリクエストが元のリクエストと同時に届いたときにも起こります。
どんな不具合として現れるのか
レースコンディションが引き起こす不具合は、業務システムのさまざまな場面で見られます。代表的な症状と、起きやすい場面を表にまとめました。
| 症状 | 典型的な発生場面 |
|---|---|
| 更新の消失(lost update) | 在庫数や残高など、同じ値を複数の担当者・処理が並行して更新する場面 |
| 二重登録・二重決済 | 確認から確定までの間に同じ操作が連続で送信される、または複数の端末から同時に送信される場面 |
| カウンタの不整合 | アクセス数やポイント数の合計値を、ロックなしで加算する場面 |
| 在庫のオーバーブッキング | 残数の確認と確保の処理の間に、別の注文が割り込む場面(TOCTOU) |
カウンタの不整合は、アクセス数やポイント数のように、単純な加算を大量に行う処理で起きやすい不具合です。値を読んでから1を足して書き戻すまでの間に別の処理が割り込むと、加算されるはずだった分がまるごと消えます。
二重登録・二重決済は、通信の遅れや再送によって、ボタンを一度しか押していなくても同じリクエストが何度もサーバーに届くことでも起こります。サーバー側で同じ注文かどうかを判定する仕組みがなければ、届いた回数だけ重複して登録されます。在庫のオーバーブッキング(在庫を超えた受注)は、ECサイトや予約システムで典型的に見られます。「残り1点」の商品を複数の利用者がほぼ同時に注文すると、在庫を確認した時点ではどちらの注文も成立して見え、確保の処理が終わってから重複が分かります。
いずれの不具合も、起きた直後にエラー画面が出るわけではありません。後日、経理担当者がデータの不一致に気付いたり、利用者からの問い合わせで分かったりと、発覚が遅れがちです。金額や在庫数のように、後から数字を合わせ直すのが難しい情報ほど影響は大きくなります。
なぜ見つかりにくいのか
レースコンディションは、通常のテストで見つけにくい不具合の代表です。開発中の動作確認は、たいていひとりの担当者が順番に操作するため、複数の処理が同時に動く状況そのものが生まれません。システム全体を通したテストでも、同時アクセスを意図して再現しない限り、不具合は表に出ません。本番環境でアクセスが集中する時間帯や、サーバーの負荷が高く処理のタイミングがずれやすいときになって、初めて起きることも珍しくありません。
ログに残る情報が乏しいことも、原因の調査を難しくします。同じ条件で試しても再現しないため、一時的な現象として片付けられ、原因の調査そのものが先送りになることもあります。
負荷試験の環境で、本番のタイミングを完全に再現できるわけでもありません。サーバーの台数やネットワークの遅延など、本番に特有の条件がそろって初めて起きる場合があります。クラウドで台数を自動で増減させている場合も、台数が変わるたびに処理のタイミングが変わります。
コードレビューで、共有資源に同時にアクセスする箇所をあらかじめ洗い出しておくのは有効な方法の一つです。ただし、それだけですべての競合を見つけられるわけではなく、設計の段階で同時実行を考えておくことの代わりにはなりません。
どう防ぐのか
対策の目的はどれも同じで、複数の処理が同じ資源に同時にアクセスしても、結果が壊れないようにすることです。そのための手段はいくつかの考え方に分かれ、向いている場面もそれぞれ違います。表にまとめました。
| 対策の考え方 | 概要 | 向いている場面 |
|---|---|---|
| アトミックな操作 | 読み込みから書き込みまでを1回の不可分な処理にする | 単純な加算・更新など、操作の内容が単純な場合 |
| 排他制御(ロック) | 同時にアクセスできる処理を1つに制限する | 複数の段階からなる処理をまとめて保護したい場合 |
| トランザクションの分離レベル | 並行して動く処理の影響をどこまで遮断するかを設定する | データベースを使う処理全般 |
| 冪等性の設計 | 同じリクエストを繰り返しても結果が変わらないようにする | 二重登録・二重決済の防止 |
| キューによる直列化 | 同時に届いた処理を一列に並べ、順番に処理する | 大量の同時リクエストをさばく場面 |
アトミックな操作は、処理をデータベースに一度で実行させ、途中で割り込まれる余地そのものをなくします。排他制御(ロック)は順番を守らせる方法ですが、ほかの処理に待ち時間が生じ、システム全体の応答の速さに影響することがあります。MySQLの公式マニュアルでは、ロック付きの読み取り(SELECT … FOR UPDATE)を使い、読み込みから書き込みまでをほかの処理に邪魔されずに続けて行う方法が示されています。*2
トランザクションの分離レベルは、並行して動く処理の影響をどこまで遮断するかをデータベースで選べる仕組みで、設定しだいで更新の消失を防げます。PostgreSQLの公式ドキュメントでは、直列化可能(Serializable)と呼ばれる分離レベルを使うと、読み書きの依存関係を検出できると説明されています。*3
冪等性(べきとうせい)は、同じ処理を何度実行しても結果が変わらない性質のことで、二重登録・二重決済を防ぐために設計に組み込みます。代表的な実装は、リクエストごとに一意な「冪等キー」を発行する方法です。同じ冪等キーを持つリクエストが二度届いても、サーバー側では最初の1回だけを処理し、二度目以降は前回と同じ結果を返します。オンライン決済の仕組みなどで広く採られている考え方です。
キューによる直列化は、そもそも競合が起きない状態を作る方法ですが、待ち行列が伸びれば応答までの時間も延びるため、性能の要件と見比べながら選びます。実際には、どれか1つを選ぶのではなく、処理の重要さに応じて組み合わせるのが一般的です。
外部に委託するときに確認しておきたい点
レースコンディションは、設計の段階で競合が起きない作りにしておくのが基本です。そのため、発注する側が仕様の確認やレビューで見ておきたい点を挙げます。
一つ目は、同時実行を前提にした仕様になっているかです。複数の利用者が同じデータを同時に操作したらどうなるかを、要件定義やレビューの段階で確かめておきます。仕様書に同時実行時の動きが書かれていないと、実装する人の判断に任され、対策が漏れやすくなります。
二つ目は、データベースの一意制約(重複を許さない設定)を使っているかです。二重登録・二重決済を防ぐには、アプリケーション側の確認に加えて一意制約も使っておくのが要点です。アプリケーションで確認してからデータベースに書き込むまでの間にも、わずかな時間差があるためです。
三つ目は、負荷テストで同時アクセスを再現しているかです。大量のリクエストを同時に送る負荷テストが計画に入っているか、シナリオに同時実行の場合が含まれているかを、開発会社に確認しておきます。
四つ目は、仕様を変えるときに再び確かめる体制があるかです。機能の追加やアクセスの増加に合わせてシステムを拡張するとき、同時実行の前提が崩れていないかを見直す必要があります。当初は問題のなかった処理が、利用者が増えて初めて競合を起こすことも珍しくありません。
五つ目は、起きたときに追跡できるログの設計になっているかです。「いつ」「どの処理の組み合わせで」不整合が生じたのかを後から追えるよう、処理の開始・終了の時刻や関連するIDを記録しておくかどうかで、調査にかかる時間は大きく変わります。
あわせて、同時実行の対策をどこまで作り込むかが契約に含まれるかも、先にすり合わせておきます。要件定義書に同時実行時の動きを明記しておけば、追加費用や責任の分担をめぐる行き違いを防ぎやすくなります。分離レベルや排他制御の専門知識が要るため、自社の担当者だけで仕様書の妥当さを判断しにくい場合は、設計レビューの段階から外部の専門家に加わってもらうと、見落としを減らしやすくなります。
まとめ:レースコンディション対策の3つの点
レースコンディションを防ぐうえで、発注する側が確かめておきたい点は3つに整理できます。第一に、複数の利用者が同じデータを同時に操作したときの動きを、仕様書に書いておくこと。第二に、アトミックな操作や分離レベル、冪等性、キューなどの対策を、処理の重要さに応じて組み合わせること。第三に、同時アクセスを再現する負荷テストと、後から追跡できるログの設計を計画に入れておくことです。この3点を踏まえておけば、「リリースしてから、残高の数字が合わないという問い合わせが来た」という事態を避けやすくなります。同時実行の設計やレビューに迷いがあれば、外部の手を借りるのも一つの選択肢です。
よくある質問
レースコンディションとデッドロックは同じものですか
別の問題です。デッドロックは、複数の処理が互いの完了を待ち続けて止まってしまう不具合です。排他制御(ロック)を入れて競合を防ぐと、今度はデッドロックに注意が必要になる、という関係にあります。
一度起きれば気づけますか
気づけるとは限りません。処理そのものはエラーを出さずに終わるため、データの不整合として後から分かることがあります。定期的にデータの整合性を確かめる仕組みと、負荷テストでの再現確認を組み合わせておくと有効です。
対応を先送りにすると、どうなりますか
金額や在庫数の不整合が利用者の目に触れる形で表に出て、問い合わせへの対応や個別のデータ修正に追われることになりがちです。件数が積み重なるほど原因の切り分けが難しくなるため、仕様を決める早い段階で対策を組み込んでおくのが望ましいでしょう。
システム設計・品質改善のご相談はLASSICへ
元請(プライムベンダー)として、要件整理からご提案・開発・保守までお手伝いします。まずはお気軽にご相談ください。
Remoguとリラシクなら、同時実行を前提にした設計やテストに加わるITエンジニアも探せます。
Remoguは、リモート前提で全国から即戦力のITプロ人材を調達するサービスです。リラシクは、扱う求人がすべてリモートワークのITエンジニア専門転職エージェントです。どちらもLASSICが運営しています。
出典
- *1 参考:MITRE「CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (‘Race Condition’)」(https://cwe.mitre.org/data/definitions/362.html)。出典:競合状態の定義を参照(2026年9月確認)
- *2 参考:MySQL 8.0 Reference Manual「Locking Reads」(https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html)。出典:SELECT … FOR UPDATEによるロック付き読み取りを参照(2026年9月確認)
- *3 参考:PostgreSQL公式ドキュメント「13.2. Transaction Isolation」(https://www.postgresql.org/docs/current/transaction-iso.html)。出典:分離レベルと直列化可能(Serializable)の説明を参照(2026年9月確認)