
DBコネクション枯渇・デッドロックの見つけ方と一次対処
昼過ぎ、問い合わせが立て続けに入る。「ページが開かない」「保存を押しても返ってこない」。あわてて監視を見ても、サーバーの CPU もメモリもディスクも、いつもと変わらず余裕がある。なのに、サービスだけが詰まっている——。 アプリのログをたどると、Too many connections や Lock wait timeout といった、DB まわりのエラーが並んでいた。
そんな経験、ありませんか。 サーバー本体には余裕があるのに詰まるとき、原因がデータベースの側にあることがあります。よくあるのが2つ。ひとつは、DB への接続(コネクション)が上限まで埋まって、新しい処理がつなげなくなるコネクション枯渇。もうひとつは、複数の処理が互いの更新を待ち合って進めなくなるデッドロックです。どちらも、サーバーの負荷グラフを見ているだけでは気づきにくいのが、追いにくさの正体です。
この記事では、まず「枯渇なのか、デッドロックなのか」を切り分ける手順、いま DB に何がつながっているかを見る読み方、そして一次対処までを、一人運用の目線で順番に整理します。全部を一度に直そうとしなくて大丈夫です。まずは「詰まりの原因が、接続の埋まりか、待ち合いか」を、はっきりさせるところから。
結論:DB が詰まっていそうなら、①いまの接続を見る(SHOW PROCESSLIST/pg_stat_activityで、何本つながって・何を待っているか)②枯渇かデッドロックか分ける(接続数が上限に張り付いていれば枯渇、SHOW ENGINE INNODB STATUSにデッドロック記録があれば待ち合い)③一次対処(詰まりの元になっている長時間クエリを止める・アプリを再起動して接続を解放)④再発を止める(接続数の監視と、長時間クエリ・接続リークの見直し)——の順で落ち着いて進めます。まずは①の「いま何がつながっているか」から。
コマンドは MySQL(8.0系)と PostgreSQL を前提にしています。バージョンやディストリビューションで出力やパスが少し異なります。ここを出発点に、本番へ反映する前に検証環境と公式情報で確認してください。
コネクション枯渇とデッドロックは、何が違うのか
同じ「DB が詰まる」でも、この2つは起きていることが違います。打ち手も変わるので、まずここを分けます。
コネクション枯渇は、DB が同時に受けられる接続数(MySQL なら max_connections)の上限まで埋まってしまい、新しい接続を張れなくなる状態です。アプリからは「つなごうとしたのに拒否された」形でエラーになります(MySQL なら Too many connections)。原因は、遅いクエリで接続が長く占有される、接続を使い終わっても閉じずに溜め込む(接続リーク)、アクセス集中で一気に接続が増える、などです。DB という「受付窓口」の席が全部埋まって、新しいお客さんが入れない状態、とイメージすると近いです。
デッドロックは、すでにつながっている複数の処理が、お互いが持っているロック(更新中の行などをつかんでいる状態)を待ち合って、どちらも進めなくなる状態です。A の処理が行1を、B の処理が行2をつかんだまま、A が行2を、B が行1を要求すると、永遠に待ち合いになります。多くの DB はこれを検知すると、片方を自動でエラーにして巻き戻し(ロールバック)ますが、そのぶんアプリ側には Deadlock found や Lock wait timeout のエラーが返ります。窓口は空いているのに、2人が「お先にどうぞ」を譲り合って前に進めない、という詰まり方です。
見分けの入口はシンプルです。接続数が上限に張り付いているなら枯渇、接続数には余裕があるのに特定の更新処理だけ固まる・エラーになるならデッドロック(ロック待ち)を疑います。次に、それぞれを実際のコマンドで確かめます。
いまの接続を見る(まず現場を確認)

まずは、DB にいま何本つながっていて、それぞれが何をしているかを見ます。これが分かると、枯渇なのかロック待ちなのかの見当がつきます。
MySQL の場合:SHOW PROCESSLIST
-- いまの接続と、各接続が実行中の処理を一覧で見る
SHOW FULL PROCESSLIST;
-- 上限(max_connections)と、いまの接続数を確認する
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
SHOW FULL PROCESSLIST の各行で見たいのは、次のところです。
Time:その処理が始まってから何秒たっているか。ここが極端に大きい(何十秒〜何百秒)行があれば、長時間居座っているクエリの候補です。State/Command:LockedやWaiting for ... lockとあれば、ロック待ち(デッドロックやロック競合)を疑います。Sleepが大量にあれば、使い終わった接続が閉じられずに溜まっている(接続リーク)サインのことがあります。Info:実行中の SQL 文。どのテーブル・どの処理が詰まりの元かの手がかりになります。
Threads_connected が max_connections に張り付いていれば、枯渇とほぼ判断できます。
PostgreSQL の場合:pg_stat_activity
-- いまの接続と、各接続の状態・実行中クエリを見る
SELECT pid, state, wait_event_type, now() - query_start AS duration, query
FROM pg_stat_activity
ORDER BY duration DESC NULLS LAST;
-- 上限(max_connections)を確認する
SHOW max_connections;
state が active のまま duration(実行時間)が長い行、wait_event_type が Lock の行が、詰まりの手がかりです。接続数そのものは SELECT count(*) FROM pg_stat_activity; で数えられます。
ここで、接続が上限近くまで埋まっているのか、接続には余裕があるのに特定の処理だけ固まっているのかが見えれば、原因を枯渇とデッドロックに切り分ける準備ができます。
枯渇かデッドロックかを分ける
接続一覧を見たうえで、もう一歩踏み込んで確かめます。
枯渇の確認:Threads_connected(MySQL)や接続数(PostgreSQL)が上限に張り付いていれば枯渇です。このとき、SHOW FULL PROCESSLIST の中身を見て、「長時間の重いクエリで席が埋まっている」のか「Sleep の接続が溜まっている(リーク)」のかを分けます。前者はクエリやインデックスの問題、後者はアプリ側の接続の閉じ忘れ・コネクションプール設定を疑う方向になります。原因の当たりどころが変わるので、ここは丁寧に。
デッドロックの確認(MySQL):接続数には余裕があるのに更新処理が固まる・Deadlock found が出るなら、InnoDB のデッドロック記録を見ます。
-- 直近のデッドロックの詳細(LATEST DETECTED DEADLOCK の項)を確認する
SHOW ENGINE INNODB STATUS\G
出力の LATEST DETECTED DEADLOCK の項に、どのトランザクションが・どのテーブルの・どの行のロックを待ち合ってデッドロックになったかが記録されています。ここを読むと、「どの2つの処理がぶつかっているか」が分かり、アプリ側で更新の順番をそろえる・トランザクションを短くする、といった再発対策の入口になります。
ロック待ちの確認:デッドロック(相互待ち)まで至らなくても、片方が長くロックを握っていて他方が待たされる「ロック競合」でも詰まります。MySQL 8.0 なら performance_schema.data_lock_waits、PostgreSQL なら pg_locks と pg_stat_activity を突き合わせると、「誰が誰を待たせているか」が見えます。まずは「待たせている側の長時間クエリ」を見つけるのが先決です。
補足:ここまで来て「接続にも余裕がある・ロック待ちも見当たらない」なら、詰まりの原因は DB ではなく、アプリ側の処理やネットワークかもしれません。切り分けの向きを、そちらへ切り替えます。犯人が違えば、打ち手も変わります。
一次対処:まず詰まりをほどく

原因の当たりがついたら、まずはサービスを動く状態に戻します。急いでいるときほど、この順で。
- 詰まりの元になっている長時間クエリを止める:
PROCESSLIST/pg_stat_activityで見つけた「明らかに居座っている重いクエリ」があれば、その接続だけを狙って止めます(MySQL はKILL <id>;、PostgreSQL はSELECT pg_terminate_backend(<pid>);)。止めるのはピンポイントで。関係ない接続まで巻き込むと、別の処理が中断されて被害が広がります。どのid/pidを止めるのか、Info/queryの中身を必ず確認してから実行します。 - 溜まった接続を解放する(リークが疑わしいとき):
Sleepの接続が大量に溜まっているなら、それを掴んでいるアプリ側を再起動すると、接続がまとめて解放されて枯渇がほどけることがあります。ただし、再起動しても原因(閉じ忘れ・プール設定)が残っていれば、また同じところで溜まります。応急処置と割り切ります。 - アプリを再起動して復旧させる(最終手段):どうにも身動きが取れないときは、アプリ(Web/API)側を再起動して接続をリセットします。DB 本体の再起動は、走っているトランザクションを巻き込むため、より慎重に。根本原因は消えていないので、再発監視とセットにします。
- いつ・何が詰まっていたかを記録に残す:止めたクエリ、そのときの接続数、
INNODB STATUSのデッドロック記録などを、後から見返せる形で残します。「再起動で直った」で終わらせないことが、次に同じことが起きたときの近道になります。
一次対処の目的は、原因の完全解決ではなく、まずサービスの詰まりをほどいて、腰を据えて調べられる状態を作ることです。ここまで来れば、いちばん怖い「鳴り続けている最中の対応」からは抜けられます。
影響:仕組みで気づけると、何が変わるか
- 「サーバーは余裕なのに詰まる」が「DB の接続(またはロック)で詰まっていた」に変わり、次に何を調べればいいかがはっきりする
- 接続数を先に監視で拾えれば、上限に張り付いて全部が止まる前に手を打てる
- デッドロックの記録を残せば、ぶつかっている処理どうしを特定でき、アプリ側の再発対策につなげられる
- 対応の手順を残せば、自分が休みの日でも誰かが同じように確認・復旧できる
放置すると、DB の詰まりは「アクセスが増えた日に、また突然」戻ってきます。しかも枯渇すると新しい接続が一切張れなくなるため、サービス全体が一気に止まる形になりやすいのがつらいところです。逆に言えば、一度「見る場所」と「監視」を用意しておけば、同じ詰まり方でうろたえることは、ぐっと減ります。
明日やること:まず「いまの接続」を見る1手から
- DB が詰まった記憶のあるサーバーで、
SHOW FULL PROCESSLIST(MySQL)またはpg_stat_activity(PostgreSQL)を一度たたいて、いま何本つながって・何が長く居座っているかを見てみる。 max_connectionsと現在の接続数を並べて、上限までどれくらい余裕があるかを把握する。張り付いていれば枯渇、余裕があってもロック待ちがあればデッドロックを疑う。- いちばん止まると困る DB だけ、接続数の監視(上限の◯割を超えたら通知)を1本入れる。まずは1台でいい。
- 余力があれば、
slow query log(時間のかかったクエリの記録)を有効にして、詰まりの元になりやすい重いクエリを平時から拾えるようにする。
この流れなら、30分で「いまの接続の把握」と「枯渇かロック待ちかの見当づけ」までは終わります。接続数の監視の1本は、次に同じことが起きたとき、あなたが問い合わせで気づく前に、DB が教えてくれる備えになります。
DBコネクション枯渇・デッドロック対応チェックリスト
最低ライン(優先順位つき:これだけで回る) 1) 現場を見る:SHOW FULL PROCESSLIST / pg_stat_activity で接続と長時間クエリを確認する 2) 原因を分ける:接続数が上限に張り付き=枯渇、余裕ありでロック待ち=デッドロックと切り分ける 3) 気づく仕組み:重要な DB に「接続数が上限の◯割で通知」の監視を1本入れる
余力が出たら拡張
slow query logを有効にし、詰まりの元になる重いクエリを平時から把握する- 接続リークが疑わしいアプリは、コネクションプールの上限・接続の閉じ方を見直す
- 繰り返すデッドロックは
INNODB STATUSの記録から、更新順序・トランザクションの短縮で対策する
免除条件(省略可)
- 使い捨ての検証 DB や、止まっても業務に影響しない一時環境は監視を省略可。ただし「詰まったときにまず接続を見る」手順だけは共有しておく。
確認項目
- DB が詰まったとき、まず
SHOW FULL PROCESSLIST/pg_stat_activityで接続を確認する手順を知っている -
max_connectionsと現在の接続数を並べて、枯渇かどうかを判断できる - 接続数に余裕があるのに固まる場合はデッドロック(ロック待ち)を疑える
- MySQL なら
SHOW ENGINE INNODB STATUSでデッドロックの詳細を確認できる - 詰まりの元の長時間クエリを、
KILL/pg_terminate_backendでピンポイントに止められる -
Sleep接続の溜まり(接続リーク)を、PROCESSLISTから読み取れる - 重要な DB に「接続数が上限の◯割で通知」の監視が入っている
- 「再起動で直った」で終わらせず、いつ・何が詰まったかを記録に残している
よければ、こちらも
DB の詰まりは、「どこかが遅い・詰まっている」サインのひとつです。同じ“重い・返ってこない”を切り分ける手順や、逼迫を前もって拾う監視の考え方も、あわせて型にしておくと落ち着けます。
- 「サイトが重い」の切り分け手順|どこが遅いかを順番に絞る
- 5xxエラーが出たとき最初に見る5つのログと場所
- 死活監視・外形監視・リソース監視の違い|最低限の監視設計
- 障害対応runbookテンプレート|初動・切り分け・連絡・記録を1枚に

サーバーは余裕なのにサービスだけ詰まる、というのは、原因の入口が見えにくくて心細いものです。でも、「まず DB の接続を見る」と決めておくだけで、そのもやもやは、ずいぶん小さくなります。 今日は、気になっていたあの DB で SHOW FULL PROCESSLIST を一度たたいて、いま何がつながっているかを眺めるだけで十分です。現場が見えれば、次の一手は自然と見えてきます。原因の入口にたどり着けた時点で、もう対応は前に進んでいます。
ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。