Dev Study
← 研修トップ

データベースの仕組み

消したはずの行がなぜ残り、なぜ掃除が必要になるのか。落ちても壊れない仕組み、同時に触られても平気な仕組み、そして索引が使われない理由を、PostgreSQL と MySQL の公式文書の文言から積み上げます。広く教えられている説明が公式には無い領域でもあります。

  1. 1
    なぜ古い行を消さないのか — 読む人と書く人がすれ違わないために
    PostgreSQL は `UPDATE` や `DELETE` をしても、古い行をその場で消しません。公式はこの方式を多版同時実行制御の利益を得るために**必要である**と書いています。まだ他のトランザクションから見えている可能性のある行版を、消してはいけないからです。
    約 9 分
  2. 2
    その保証が効かないとき — 素のSELECTが止まる唯一の条件
    「PostgreSQL では読み取りがブロックされない」は言い過ぎです。公式は例外を1つだけ、はっきり書いています——**Only an `ACCESS EXCLUSIVE` lock blocks a `SELECT` (without `FOR UPDATE/SHARE`) statement.** 逆に読めば、**それ以外のロックでは素の `SELECT` は止まりません**。例外は1種類だけです。
    約 9 分
  3. 3
    掃除は4つの仕事をしている — そしてディスクは空かない
    `VACUUM` が必要な理由は、よく語られる2つ(不要行の回収・統計情報の更新)ではありません。PostgreSQL 18 の公式は "several reasons" として**4つ**を番号付きで列挙しています。残る2つを知らないと、なぜ何もしていない表まで掃除されるのかが説明できません。
    約 9 分
  4. 4
    ディスクを空けるコマンドが、作業中に追加のディスクを要求する
    `VACUUM FULL` は通常の `VACUUM` の「強い版」ではなく、**別物**です。公式は "There are two variants of `VACUUM`" と2つに分けた上で、`VACUUM FULL` は表の内容を**新しいファイルに書き直す**と説明します。だからOSに空間を返せます。
    約 9 分
  5. 5
    32ビットが尽きる日 — 止まるのではなく、書けなくなる
    トランザクションID(XID)は **32ビット**です。長く動き続けたクラスタでは番号が一周し、公式の言葉では "transactions that were in the past appear to be in the future — which means their output become invisible"。**過去が未来に見え、結果が見えなくなります。** 対策は `FrozenTransactionId` という特別なXIDで、常にどの通常XIDより古いと見なされます。
    約 9 分
  6. 6
    先に書くログ — 順序を縛ると、書く回数が減る
    データベースは、テーブルの中身を書き換える前に「何をどう書き換えるか」を記録したログを先に永続化します。データファイルへの変更は **それを記述した WAL レコードが永続化された後でなければ書けない**。これが WAL の中核です。
    約 9 分
  7. 7
    どこまで戻ればいいか — 印を打つ代償
    チェックポイントは、復旧をどこから始めればよいかを決める**印**です。印があると、落ちたあとログを遡る範囲が限られます。印より前の変更は *guaranteed to be already on disk* ——すでにディスクに載っていると保証されるからです。
    約 9 分
  8. 8
    既定値が違う2つのDB — 同じSQLが、同じようには走らない
    同じSQLを書いても、**InnoDB は REPEATABLE READ、PostgreSQL は READ COMMITTED** で走ります。分離レベルは「自分が選ぶもの」だと思われがちですが、多くの現場では**誰も選んでいない既定値**のまま動いています。そしてその既定値が、製品によって逆側にずれています。
    約 9 分
  9. 9
    隙間に鍵をかける — next-key lock と、それが効く場面
    InnoDB は行だけでなく**行と行の隙間**にも鍵をかけます。公式は “To prevent phantoms, `InnoDB` uses an algorithm called next-key locking that combines index-row locking with gap locking.” と書いています。存在しない行の場所を押さえるので、他のトランザクションは**そこに挿入できません**。
    約 9 分
  10. 10
    ロックは行ではなく索引につく — WHEREで除外した行も巻き込む
    InnoDB のロックは行そのものではなく**インデックスレコード**に掛かります。公式は “Record locks always lock index records, even if a table is defined with no indexes.” と書いています。索引が無ければ隠しクラスタ化インデックスが作られ、それが使われます。
    約 9 分
  11. 11
    「B+木」を公式は使わない — 通称と原典の表記を分ける
    インデックスの構造は「B+木」と説明されることが多いですが、**MySQL も PostgreSQL も公式文書では B-tree と書きます**。InnoDB は “`InnoDB` indexes are B-tree data structures”、PostgreSQL は “the standard btree (multi-way balanced tree) index data structure” です。
    約 9 分
  12. 12
    索引が使われない理由 — 左端プレフィックスと、その反例
    複合インデックスは**左端プレフィックス**として使われます。`(col1, col2, col3)` なら `(col1)` `(col1, col2)` `(col1, col2, col3)` で引けます。ただし公式は**反例**も並べています——`WHERE last_name='Jones' OR first_name='John'` は、第1列を含んでいるのに**使われません**。
    約 9 分
  13. 13
    デッドロックは異常ではない — "Just try again."
    デッドロックは障害ではありません。公式はこう書いています——“Deadlocks are a classic problem in transactional databases, but they are not dangerous unless they are so frequent that you cannot run certain transactions at all.” そして対処の指示は3語です——**“Just try again.”**
    約 9 分