Skip to content

データベース基礎(インデックス / トランザクション / クエリ最適化)

核心となる問い

なぜExcelの検索には10秒かかるのに、淘宝(Taobao)の検索は0.01秒で済むのか? データが「数千件」から「10億件」に、利用者が「1人」から「数千万人の同時アクセス」になると、Excelでは対応できなくなります。データベースはまさにこの問題を解決するために生まれました——大量データと高同時アクセスを専門に処理する「スーパーExcel」です。この章では、ゼロからデータベースの核心原理を理解していきます。


1. なぜ「データベース」が必要なのか?

1.1 小さな書店から淘宝へ:データ規模の変遷

あなたが小さな書店を経営していて、毎日数冊の本を売っているとします。ノートに手書きで記録します:

2024-01-15:張三が『百年の孤独』を購入、59元
2024-01-16:李四が『活きる』を購入、39元

この段階では、ノートで十分です。しかし、書店が「アマゾン」のようになり、毎日100万件の注文が殺到すると、問題が発生します:

  • データ量:数十行ではなく、数億行
  • 同時アクセス:1人が検索するのではなく、数千万人が同時にアクセス
  • データ関連:注文がユーザー、商品、在庫、物流に関連……複雑な関係を効率的に管理する必要がある
  • データセキュリティ:停電ですべての注文が失われてはならない

Excel / ノート

  • 個人や小規模チーム向け
  • データ量:数千〜数万行
  • 単一ユーザー、順次アクセス
  • 手動検索、速度が遅い

データベース

  • エンタープライズアプリケーション向け
  • データ量:数億件以上
  • 数千万人が同時にオンラインアクセス
  • ミリ秒レベルのクエリ速度

これこそが「データベース」が解決する問題です。大量データを効率的に保存し、高速にクエリし、安全に管理するにはどうすればよいか?

1.2 実際の教訓:なぜユーザーデータにExcelを使ってはいけないのか

「私のプロジェクトは数万人のユーザーしかいないから、Excelで十分では?」と思うかもしれません。ある実話をお話ししましょう。

小林のスタートアップでの失敗談

小林はスタートアップでソーシャルアプリを開発しました。当初はユーザーが少なく、Excelでユーザー情報(名前、電話番号、登録時間など)を保存していました。毎日Excelをエクスポートしてユーザー増加を追跡し、すべて正常に機能していました。

ユーザーが10万人を突破したとき、問題が現れ始めました:

  • Excelを開くのに5分かかる
  • 「北京のユーザー」をフィルタリングすると長時間フリーズする
  • 一度Excelファイルが破損し、数千件のユーザーデータが永久に失われた

最も致命的だったのは、「特定のユーザーのすべての注文を表示する」機能を実装したかったことです。しかし、ユーザー情報と注文が別々のExcelファイルにあり、毎回手動でコピー&ペーストするしかなく、毎回30分かかっていました。

その後、先輩に相談しました。先輩は一目見て笑いました。「あなたが必要なのはExcelではなく、データベースです。」

データベースに切り替えた後、すべてが変わりました:

  • 「北京のユーザー」のクエリに0.01秒しかかからない
  • 「リレーション」によりユーザーと注文が自動的に関連付けられ、1つのSQL文で完了
  • データが自動的にバックアップされ、ファイル破損の心配がなくなった

小林は一つの教訓を得ました。データ量が少ないときは何でも使えるが、データが大きくなるとExcelは災難である。

重要なポイント

データベースは「より複雑なExcel」ではなく、全く異なる設計思想です:

  • Excel:小規模データ、単一ユーザー向けに設計
  • データベース:大規模データ、高同時アクセス、複雑な関連付け向けに設計

適切なツールを選ぶことで、システムパフォーマンスを数千倍向上させることができます。


2. 中核概念:テーブル、行、列、主キー

これらの概念はデータベースとどう関係するのか?

テーブル、行、列、主キーはデータベースの「積み木」です。

家を建てることを想像してください:

  • テーブル = 1つの部屋(1種類のデータを保存)
  • = 部屋の中の1つの箱(1つの完全なレコード)
  • = 箱のラベル(名前、年齢など)
  • 主キー = 箱の一意のシリアル番号(絶対に重複しない)

これらの基礎概念を理解することで、データがどのように整理されているかを把握できます。

データベースを深く学ぶ前に、まずこれらの中核概念を明確にする必要があります。理解を助けるために、図書館の比喩を使います。

2.1 図書館の比喩でデータベース構造を理解する

図書館に入ると想像してください。その構成はデータベースと驚くほど似ています:

概念図書館の比喩実際の役割具体的な例
データベース (Database)図書館全体すべてのデータを格納するコンテナECサイトのデータベース
テーブル (Table)1つの棚同じ種類のデータの集合ユーザーテーブル、商品テーブル、注文テーブル
列 (Column)本の背表紙のラベルデータの属性(フィールド)名前、年齢、電話番号
行 (Row)棚にある1冊の本1つの具体的なデータレコード「張三、25歳、東京」
主キー (Primary Key)各本のISBN番号各行を一意に識別するIDuser_id = 1001

実際の例:ユーザーテーブル (users)

user_id (主キー)nameagecityemail
1001張三25東京zhangsan@example.com
1002李四30上海lisi@example.com
1003王五28東京wangwu@example.com
  • テーブルusers(すべてのユーザーデータを保存)
  • user_idnameagecityemail(各ユーザーの属性)
  • :各行が1人のユーザー(例:「張三、25歳、東京」)
  • 主キーuser_id(1001、1002、1003、絶対に重複しない)

2.2 主キー (Primary Key):データの「ID番号」

主キーとは?

主キーとは、テーブル内の各行の一意の識別子で、ID番号のようなものです。

主な特徴

  • 一意性:絶対に重複しない(同じID番号を持つ二人の人はいない)
  • 非NULL:必ず値を持つ(ID番号のない人は存在しない)
  • 不変性:一度設定されると変更されない(あなたのID番号は変わらない)

一般的なアプローチ

  • 自動採番整数を使用:1、2、3、4...
  • UUID(汎用一意識別子)を使用:550e8400-e29b-41d4-a716-446655440000

なぜ主キーが必要なのか?主キーのない世界を想像してみてください:

シナリオ:「張三」の年齢を変更したいが、テーブルには3人の「張三」がいる。システムはどれを更新すべきか?

sql
-- 主キーがないと、これが「張三」という名前の全員を更新してしまう!
UPDATE users SET age = 26 WHERE name = '張三';

-- 主キーがあれば、正確に更新
UPDATE users SET age = 26 WHERE user_id = 1001;

主キーの黄金法則:すべてのテーブルには主キーを設定すべきであり、絶対に変更してはならない。

2.3 外部キー (Foreign Key):テーブルをつなぐ架け橋

これこそがデータベースをExcelより強力にするポイントです。テーブル間に関係を築くことができます

外部キーとは?

外部キーとは、別のテーブルの主キーを指す列で、テーブル間の関連付けに使用されます。

シンプルな理解

  • 主キー = 自分のID番号
  • 外部キー = 自分が参照する他人のID番号

:注文テーブルのuser_idが外部キーで、ユーザーテーブルの主キーを指しています。

実際の例を見てみましょう:

ユーザーテーブル (users)

user_id (主キー)namephone
1001張三138xxxx
1002李四139xxxx

注文テーブル (orders)

order_id (主キー)product_namepriceuser_id (外部キー)
5001iPhone 1559991001
5002MacBook149991001
5003AirPods19991002

重要なポイント

  • 注文テーブルのuser_id = 1001は、ユーザーテーブルのuser_id = 1001(張三)を指している
  • 「注文5001は誰が買ったか」を確認したいとき、データベースは自動的にユーザーテーブルでuser_id = 1001のユーザーを検索する

メリット

  • データの重複なし:張三が100回購入しても、ユーザーテーブルには1回しか保存されない
  • 保守が容易:張三が電話番号を変更した場合、ユーザーテーブルだけを更新すれば、すべての注文が自動的に新しい番号に関連付けられる
  • 柔軟なクエリ:「各ユーザーの合計消費額はいくらか」といった複雑な質問に簡単に答えられる
🔗外键关系演示理解表与表之间如何关联
想象你在管理一个家族谱系:有"家谱表"记录每个人,有"婚姻表"记录谁和谁结婚了。两张表通过"人名"关联起来,这就是外键的作用。
👥用户表 (users)主表
🔑 user_id
name
phone
address
101
张三
138xxxx
北京
102
李四
139xxxx
上海
103
王五
137xxxx
广州
user_id (外键) → user_id (主键)
📦订单表 (orders)从表
🔑 order_id
book_name
🔗 user_id
price
001
百年孤独
101
59
002
活着
101
39
003
三体
101
99
004
百年孤独
102
59
005
红楼梦
102
79
006
西游记
103
69
💡 核心概念

主键(Primary Key):用户表的 user_id 是主键,唯一标识每个用户。

外键(Foreign Key):订单表的 user_id 是外键,指向用户表的主键。

关联查询:通过外键,数据库可以快速找到"订单 001 是用户 101 买的",然后去用户表查到"用户 101 是张三"。

🎯核心优势:外键消除了数据冗余。张三的地址只存一次,无论他买多少本书。如果要修改地址,只需改用户表的一行,所有订单自动关联到新地址。

3. データベースとどう対話するか?SQL入門と実践

データベースをマウスで「クリック」して直接操作することはできません(GUIツールはありますが、本質的にはコマンドに変換しています)。データベースに指示を出すには特別な言語が必要です。

その言語がSQL(Structured Query Language、構造化照会言語)です。

良いニュースは、SQLが自然な英語に非常に近く、話しかけているように読めることです。

3.1 SQLの中核操作:CRUD

ほとんどの場合、4つの操作をマスターすれば十分です。業界ではCRUDと呼ばれています:

操作英語SQLキーワード平易な理解
Create作成INSERT新しいデータレコードを追加
Read読取SELECTデータを検索
Update更新UPDATEデータを変更
Delete削除DELETEデータを削除

この表から何が分かるか?

これら4つの操作は、データ処理のあらゆるシナリオをカバーします:

  • Create:ユーザー登録時、新しいユーザーレコードを挿入
  • Read:ユーザーログイン時、ユーザー名とパスワードを検索
  • Update:ユーザーがプロフィールを編集した際、テーブルのデータを更新
  • Delete:ユーザーがアカウントを削除した際、ユーザーデータを削除

この4つを覚えれば、日常のSQL操作の80%をマスターしたことになります。

3.2 データの検索 (SELECT):データベースで最も使用される操作

検索はデータベースの最も重要な機能であり、パフォーマンス最適化の鍵です。

例1:東京のすべてのユーザーを検索

sql
SELECT name, age FROM users WHERE city = '東京';

単語ごとの理解

  • SELECT name, age:name列とage列を選択
  • FROM users:usersテーブルから
  • WHERE city = '東京':cityが「東京」と等しい条件で

結果

nameage
張三25
王五28

例2:価格が5000から15000の間の商品を検索

sql
SELECT name, price FROM products
WHERE price BETWEEN 5000 AND 15000;

例3:あいまい検索(名前に「張」を含むユーザーを検索)

sql
SELECT name FROM users WHERE name LIKE '%張%';

パフォーマンスの罠:LIKEの使用

LIKE '%張%'フルテーブルスキャンを引き起こし、データ量が大きいと非常に遅くなります。

最適化のアドバイス

  • LIKE '%張%'は使用しない(両側に%がある)
  • LIKE '張%'は使用可能(右側にのみ%がある)

LIKE '張%'はインデックスを利用できますが、LIKE '%張%'はインデックスを使用できないためです。

3.3 データの挿入 (INSERT):レコードの追加

:新しいユーザーを追加

sql
INSERT INTO users (user_id, name, age, city, email)
VALUES (1004, '趙六', 35, '広州', 'zhaoliu@example.com');

単語ごとの理解

  • INSERT INTO users:usersテーブルに挿入
  • (user_id, name, age, city, email):挿入する列を指定
  • VALUES (1004, '趙六', ...):対応する値

バッチ挿入(より効率的):

sql
INSERT INTO users (name, age, city) VALUES
('小明', 25, '東京'),
('小紅', 28, '上海'),
('小剛', 30, '広州');

3.4 データの更新 (UPDATE):レコードの変更

:東京のすべてのユーザーの年齢を1つ増やす

sql
UPDATE users SET age = age + 1 WHERE city = '東京';

非常に危険:WHEREを忘れないで!

WHERE句を書き忘れると、すべての行が変更されてしまいます!

sql
-- 危険!すべてのユーザーの年齢が26になってしまう
UPDATE users SET age = 26;

-- 正しい:user_id = 1001のユーザーだけを変更
UPDATE users SET age = 26 WHERE user_id = 1001;

実際の教訓:2012年、ある有名企業のエンジニアがWHEREを書き忘れ、本番環境で数百万件のユーザーデータが誤って更新され、システムが4時間ダウンし、甚大な損失を出しました。

3.5 データの削除 (DELETE):レコードの削除

:user_id = 1004のユーザーを削除

sql
DELETE FROM users WHERE user_id = 1004;

二重の危険:DELETEにはWHEREがさらに必要!

sql
-- 危険!テーブルの全データが削除されてしまう!
DELETE FROM users;

-- 正しい:指定した行だけを削除
DELETE FROM users WHERE user_id = 1004;

ベストプラクティス

  1. 削除前に必ずSELECTでデータを確認する
  2. 重要なシステムでは「論理削除」を使用(is_deletedフィールドを追加して削除をマーク)
  3. 本番環境での操作前にはデータをバックアップする

3.6 複数テーブルのクエリ (JOIN):データベースの魔法の瞬間

前に説明した「外部キー」を覚えていますか?SQLの最も強力な点は、複数の関連テーブルを一度にクエリできることです。

シナリオ:「張三が購入したすべての商品」をクエリする

3つのテーブルがあるとします:

ユーザーテーブル (users)

user_idname
1001張三

商品テーブル (products)

product_idnameprice
201iPhone 155999
202MacBook14999

注文テーブル (orders)

order_iduser_idproduct_idquantity
500110012011
500210012022

SQLクエリ

sql
SELECT u.name, p.name AS product_name, p.price, o.quantity
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN products p ON o.product_id = p.product_id
WHERE u.name = '張三';

結果

nameproduct_namepricequantity
張三iPhone 1559991
張三MacBook149992

JOINのプロセスの理解

  1. FROM orders o:注文テーブルから開始
  2. JOIN users u ON o.user_id = u.user_id:user_idを介してユーザーテーブルに関連付け
  3. JOIN products p ON o.product_id = p.product_id:product_idを介して商品テーブルに関連付け
  4. WHERE u.name = '張三':張三の注文をフィルタリング
💻SQL 练习场体验 SQL 的 CRUD 操作
SQL 就像和数据库对话:你说"给我找所有年龄大于 25 的用户",数据库就会执行查询并返回结果。即使不会编程,也能很快上手。
📝 示例 SQL
SELECT name, age FROM users WHERE age > 25;
💡 逐词翻译
SELECT name, age选择 name 和 age 这两列
FROM users从 users 这张表
WHERE age > 25在 age 大于 25 的条件下
📊 返回结果
name
age
李四
30
王五
28
🎯核心概念:CRUD 涵盖了所有数据管理的基本需求。无论是淘宝、微信、抖音,它们的数据库操作本质上就是这四种:增、删、改、查。

4. なぜデータベースはこれほど速いのか?インデックスの原理を解明

これはデータベースの最も神秘的な部分であり、面接で最もよく聞かれるトピックでもあります。

Excelで「張という苗字の全員」を検索する場合、Excelは1行目から最後の行までスキャンする必要があります。これがフルテーブルスキャンです。データが増えるほど遅くなります。

しかしデータベースでは、10億行があっても検索は数ミリ秒で完了します。

その秘訣は:インデックス (Index) です。

4.1 直感的な理解:辞書からのヒント

目次のない1000ページの本の中からある言葉を探さなければならないと想像してください。どうしますか?

ページを一枚ずつめくるしかない——これがフルテーブルスキャンで、平均500ページをめくる必要があります。

しかし、この本にピンインインデックスがあったら?

「データベース」という言葉を探したい場合:

  1. インデックスを開き、「デ」で始まるセクションを見つける
  2. 「デ」のセクション内で「ータ」を探す
  3. インデックスが告げる:256ページにある

3回の検索で見つかりました!これがインデックス検索です。

データベースのインデックスは本の目次のようなもの

  • インデックスなし:行ごとのスキャン(10億行 = 数分)
  • インデックスあり:直接ジャンプ(10億行 = 3回のディスクI/O = ミリ秒)

4.2 フルテーブルスキャン vs インデックス検索:速度比較

1000万件のレコードがあるユーザーテーブルがあるとします。

シナリオuser_id = 5,555,555のユーザーを検索

方式プロセス確認が必要な行数推定時間
フルテーブルスキャン1行目から開始、一行ずつ確認平均500万行5-30秒
インデックス検索インデックスツリーを検索、ターゲットに直接ジャンプ3-4回の比較0.003秒

速度の差:数千倍!

重要なポイント

インデックスは銀の弾丸ではなく、コストがかかります:

  • ストレージのオーバーヘッド:インデックスには追加のストレージ容量が必要
  • 書き込み速度の低下:INSERT/UPDATE/DELETEのたびにインデックスも更新する必要がある

いつインデックスを作成すべきか?

  • 頻繁にクエリに使用される列(WHERE、JOINの条件)
  • データ量が大きい(数千行以下の場合は不要)

いつインデックスを作成すべきでないか?

  • ほとんどクエリされない列
  • 頻繁に更新される列
  • データ量の少ないテーブル

4.3 基盤となるデータ構造:B+木

実際のインデックスは単純な「アルファベット順リスト」ではなく、B+木 (B+ Tree)と呼ばれる慎重に設計されたデータ構造です。

B+木とは?

B+木は「低くて幅広い」ツリー状のデータ構造です:

  • 低い:ルートからリーフまで通常3-4階層しかない
  • 幅広い:各ノードに数百のキー値を格納可能

なぜ「低くて幅広い」必要があるのか?

データはディスクに保存されており、ディスクの読み取り(I/O)は非常に遅い(メモリより数千倍遅い)からです。B+木の設計目標は、ディスクI/O回数を最小限に抑えることです。

  • 3-4階層の高さ = 最大3-4回のディスク読み取り
  • 各階層に大量のデータを格納 = ツリーが高くなりすぎないことを保証

実際の例

各ノードが1000のキー値を格納できるB+木があるとします:

  • ルートノード:1000のキー値 → 1000の子ノードを指す
  • 中間ノード:各ノードに1000のキー値 → 1000のリーフノードを指す
  • リーフノード:各ノードに1000の実際のデータレコードを格納

総データ量 = 1000 × 1000 × 1000 = 10億件のデータ

ツリーの高さ = 3階層

これは、10億件のデータの中から任意の1件を検索するのに、3回のディスクI/Oしか必要ないことを意味します!

これがデータベースのクエリが劇的に速い秘密です。

🌳B+ 树索引演示理解数据库如何快速查找数据
想象你要在字典里找一个字。你会先看目录,定位到首字母的区域,再在这个区域里找具体页码。B+ 树就是这样的多层目录,让数据库在 10 亿条数据中 3 次就能找到目标。
🐢全表扫描
001用户1
002用户2
003用户3
004用户4
005用户5
006用户6
007用户7
008用户8
009用户9
010用户10
011用户11
012用户12
013用户13
014用户14
015用户15
016用户16
017用户17
018用户18
019用户19
020用户20

👆 点击"开始查找"看全表扫描有多慢

索引查找
根节点
1-100
中间节点
1-10
叶子节点
1
2
3
4
5
6
7
8
9
10

👆 点击"开始查找"看索引有多快

数据量
100 万条
全表扫描
平均 50 万次比较
B+ 树索引
仅 3 次比较
速度提升
10 万倍+
💡核心原理:B+ 树通过"矮胖"的设计,让树的高度只有 3-4 层。每层可以存储成百上千个键值,所以 10 亿数据也只需要 3 次磁盘 I/O。这就是数据库查询飞快的秘密。

5. トランザクション:データが失われたり壊れたりしないことをどう保証するか?

春節の帰省ラッシュで切符を争奪するシーンを想像してください:

  • 時間T1:ユーザーAがクエリし、「G1234列車は残り1枚」と発見
  • 時間T2:ユーザーBもクエリし、「残り1枚」と発見
  • 時間T3:ユーザーAが「購入」をクリック、システムが在庫を引き当て、切符はAに販売
  • 時間T4:ユーザーBが「購入」をクリック——保護メカニズムがなければ、システムは再び在庫を引き当て、同じ切符をBに販売してしまう!

これは典型的な同時実行の競合問題です。

5.1 トランザクション (Transaction) とは?

トランザクションはデータベースの操作のグループで、すべて成功するか、すべて失敗するかのいずれかであり、「途中まで完了」状態にはなりません。

生活の中の例

銀行振込は典型的なトランザクションです:

  1. 口座Aから100元を引き落とす
  2. 口座Bに100元を追加する

ステップ1が成功したが、ステップ2が失敗した場合(停電など)、どうなるでしょうか?

  • トランザクションなし:口座Aのお金がなくなり、口座Bには入金されず、お金が忽然と消滅
  • トランザクションあり:システムがステップ2の失敗を検出し、自動的にステップ1をロールバック。両方の口座が元の状態に戻る

これがトランザクションの原子性です。すべてやるか、一切やらないかです。

5.2 トランザクションの4つの特性 (ACID)

トランザクションには4つの特性があり、ACIDと略されます:

特性英語意味銀行振込の例
Atomicity(原子性)Atomicityすべてやるか、一切やらないか引き落としと入金は両方が成功しなければならず、引き落としだけで入金しないことはできない
Consistency(一貫性)Consistencyデータは常に有効な状態を保つ振込の前後で、両口座の合計金額は変わらないはず
Isolation(隔離性)Isolation複数のトランザクションが互いに影響しないAが振込中のとき、Bが見るのは「振込前」または「振込後」の残高であり、中間状態は見えない
Durability(永続性)Durabilityコミットされたデータは永久に保存される振込が成功した後、停電が起きても口座残高は戻らない

この表から何が分かるか?

これら4つの特性がデータの安全性を保証します:

  • 原子性:「途中まで」の操作を防止(引き落としたが入金されない)
  • 一貫性:不当なデータを防止(振込後に合計金額が変わる)
  • 隔離性:同時実行の競合を防止(二人が同時に同じデータを変更)
  • 永続性:データ損失を防止(コミット後は停電でも影響なし)

これらの保証がなければ、銀行システムは決して機能し得ません。

5.3 トランザクションの隔離レベル:安全性とパフォーマンスのバランス

理論的には、トランザクションを完全に隔離したいところです。しかし、完全な隔離 = 極めて低いパフォーマンスです(大量のロックが必要となり、他のトランザクションは待機するしかなくなるため)。

そのため、データベースは4つの隔離レベルを提供しています:

隔離レベルダーティーリードリピータブルリード不可ファントムリードパフォーマンス適用シナリオ
読み取り未コミット可能可能可能最速ほとんど使用されない(データが不正の可能性)
読み取りコミット済み不可能可能可能高速一般的な業務(Oracleのデフォルト)
リピータブルリード不可能不可能可能中程度銀行振込(MySQLのデフォルト)
直列化可能不可能不可能不可能最も遅い極めて厳格なシナリオ(ほとんど使用されない)

3つの「読み取り」とは?

  • ダーティーリード:他のトランザクションがまだコミットしていないデータを読み取ってしまう(ロールバックされる可能性があり、データが不正確)
  • リピータブルリード不可:同じトランザクション内で、同じデータを2回読み取った結果が異なる(他のトランザクションによって変更された)
  • ファントムリード:同じトランザクション内で、2回のクエリで結果セットの行数が異なる(他のトランザクションがデータを挿入/削除した)

平易な例(銀行残高の照会):

  • ダーティーリード:残高が1000元と表示されたが、相手のトランザクションがロールバックされ、実際は100元しかない
  • リピータブルリード不可:1回目の残高照会が1000元、2回目が800元になった(引き落としがあった)
  • ファントムリード:1回目の照会で5件の取引、2回目で6件になった(新しい取引が追加された)
🔒事务 ACID 特性演示理解事务如何保证数据安全
想象银行转账:A 转给 B 100 元。这个操作包含两步:从 A 扣 100,给 B 加 100。如果只扣了钱但没到账,就是灾难。事务保证这两步要么全成功,要么全失败
⚛️
A
原子性
Atomicity
⚖️
C
一致性
Consistency
🔒
I
隔离性
Isolation
💾
D
持久性
Durability
👆 点击上方任意特性,查看详细解释
🎯 12306 抢票场景

场景:用户 A 和 B 同时看到还剩 1 张票,同时点击购买。

没有事务:A 扣库存,B 也扣库存,同一张票卖给了两个人!

有事务(隔离性):A 的操作加锁,B 必须等待。A 买完后,库存变为 0,B 看到的是"已售罄"。

💡核心思想:ACID 四个特性共同保证了数据在高并发环境下的不丢、不乱、不冲突。这就是为什么所有涉及资金、订单的系统都必须使用数据库事务。

6. パフォーマンス最適化:クエリを1000倍速くする実践的なテクニック

ここまでで、インデックスやトランザクションといった中核概念を理解しました。しかし実際のプロジェクトでは、さまざまなパフォーマンス問題に直面する可能性があります。

このセクションでは、すぐに適用できる最適化戦略を紹介します。

6.1 インデックス使用の落とし穴ガイド

よくある間違い:インデックスが効かない罠

インデックスを作成したのに、クエリがまだ遅いということがよくあります。それはインデックスが無効になっているからです。

インデックスが無効になる一般的な原因

  1. インデックス列に関数を使用している
  2. 暗黙的な型変換が発生している
  3. LIKEクエリが%で始まっている
  4. OR条件(一部のケース)
  5. 複合インデックスが最左プレフィックスルールを満たしていない

落とし穴1:インデックス列に関数を使用

sql
-- 誤り:インデックス列に関数を使用、インデックスを利用できない
SELECT * FROM users WHERE YEAR(created_at) = 2024;

-- 正しい:範囲クエリに書き直すことでインデックスを利用可能
SELECT * FROM users
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

落とし穴2:暗黙的な型変換

sql
-- user_idがint型だと仮定
-- 誤り:文字列を渡すと暗黙的変換が発生し、インデックスを利用できない
SELECT * FROM users WHERE user_id = '123';

-- 正しい:対応する型を渡す
SELECT * FROM users WHERE user_id = 123;

落とし穴3:LIKEが%で始まる

sql
-- 誤り:%で始まるため、インデックスを利用できない
SELECT * FROM users WHERE name LIKE '%張三%';

-- 正しい:固定プレフィックスで始まると、インデックスを利用可能
SELECT * FROM users WHERE name LIKE '張三%';

-- または全文インデックスを使用(テキスト検索に適している)
SELECT * FROM users WHERE MATCH(name) AGAINST('張三');

6.2 SQL最適化の実践テンプレート

テンプレート1:ページネーションの最適化(深いページネーション問題)

問題と解決策を表示
sql
-- 問題:OFFSETが大きいと、クエリがますます遅くなる
SELECT * FROM orders
ORDER BY created_at DESC
LIMIT 10 OFFSET 1000000;

-- 最適化案1:前回のクエリのタイムスタンプをカーソルとして使用
SELECT * FROM orders
WHERE created_at < '2024-01-15 12:00:00'
ORDER BY created_at DESC
LIMIT 10;

-- 最適化案2:主キーの範囲クエリを使用
SELECT * FROM orders
WHERE order_id > 1000000
ORDER BY order_id
LIMIT 10;

テンプレート2:バッチ挿入の最適化

sql
-- 非効率:複数回の単一行挿入(ネットワークの往復が複数回)
INSERT INTO users (name, age) VALUES ('張三', 25);
INSERT INTO users (name, age) VALUES ('李四', 30);
INSERT INTO users (name, age) VALUES ('王五', 28);

-- 効率的:単一SQLでのバッチ挿入(ネットワークの往復は1回のみ)
INSERT INTO users (name, age) VALUES
('張三', 25),
('李四', 30),
('王五', 28);

テンプレート3:SELECT * を避ける

sql
-- 非効率:すべての列を返す(不要な大きなフィールドを含む)
SELECT * FROM users WHERE user_id = 1;

-- 効率的:必要な列だけを返す
SELECT user_id, name, email FROM users WHERE user_id = 1;

6.3 高同時アクセスシナリオへの対応戦略

シナリオ問題解決策
ホットデータ特定の行が頻繁に読み書きされ、ロック競合が発生キャッシュ(Redis)+ 読み書き分離を使用
フラッシュセール瞬間的な高同時在庫引き当て楽観的ロック + 在庫の事前ウォーミング + メッセージキューによるピークカット
スロークエリ複雑なクエリがデータベースを圧迫インデックス最適化 + クエリ分割 + 読み書き分離
接続数枯渇同時リクエストが多すぎて接続プールが枯渇接続プール最適化 + レート制限 + サービス低下

重要なポイント

パフォーマンス最適化の基本原則:

  1. まず測定し、その後に最適化するEXPLAINでクエリ計画を分析し、真のボトルネックを見つける
  2. インデックスを優先:パフォーマンス問題の80%はインデックスの最適化で解決可能
  3. データベースの負荷を減らす:キャッシュを使えるところではキャッシュを使い、非同期にできるところは非同期にする
  4. 分割して統治る:大きなテーブルは小さなテーブルに分割し、大きなクエリは小さなクエリに分割する
查询优化演示常见错误与正确做法对比
很多时候,查询慢不是因为数据库性能差,而是因为 SQL 写错了。下面这些错误,你可能每天都在犯。
1
在索引列上使用函数
SELECT * FROM users WHERE YEAR(created_at) = 2024;
⚠️ 索引失效,全表扫描
SELECT * FROM users WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
💡 可以使用索引,查询速度提升 1000 倍
原理:当对列使用函数时,数据库必须先计算每一行的函数值,无法使用索引。把函数移到等号右边,或用范围查询代替。
2
隐式类型转换
3
LIKE 以 % 开头
4
SELECT * 返回所有列
📝 优化建议清单
为 WHERE、JOIN、ORDER BY 的列创建索引
避免在索引列上使用函数或表达式
用 EXPLAIN 分析查询执行计划
只查询需要的列,避免 SELECT *
批量操作代替单条操作
考虑使用覆盖索引减少回表
🎯核心原则:不要让数据库做"多余的工作"。索引失效、全表扫描、返回不必要的数据,这些都是最常见的性能杀手。写出高效 SQL 的关键,是理解数据库如何执行你的查询

7. まとめと学習パス

データベースの中核概念を表で振り返りましょう:

概念一言で説明解決する問題重要なポイント
テーブル、行、列データの整理方法構造化データをどう保存するかテーブル = Excelシート、行 = レコード、列 = フィールド
主キー各行の一意の識別子特定の行のデータを正確に見つける方法一意、非NULL、不変
外部キーテーブルをつなぐ架け橋異なるテーブルのデータを関連付ける方法別のテーブルの主キーを指す
SQLデータベースと対話する言語データのCRUD操作方法SELECT、INSERT、UPDATE、DELETE
インデックスクエリを高速化するデータ構造データを素早く見つける方法B+木、ディスクI/Oを削減
トランザクションデータの安全性を保証するメカニズム同時実行の競合とデータ損失を防ぐ方法ACID:原子性、一貫性、隔離性、永続性

最後に

データベースは奥深いテーマであり、この記事は入門に過ぎません。さらに深く学びたい場合は、以下のパスに沿って進めることをお勧めします:

次のステップ

  1. 実践:MySQLまたはPostgreSQLをインストールし、テーブルを作成し、データを挿入し、SQLクエリを書く
  2. ORMフレームワーク:コード内でデータベースを使用する方法を学ぶ(SQLAlchemy、Prisma、TypeORMなど)
  3. インデックス最適化:複合インデックス、カバリングインデックス、インデックスプッシュダウンなどの高度なトピックを深く学ぶ
  4. トランザクションの原理:MVCC(多版同時実行制御)、ロックメカニズム、隔離レベルの実装について理解する
  5. 分散データベース:テーブルシャーディング、読み書き分離、マスタースレーブレプリケーションなどのアーキテクチャを学ぶ

覚えておいてください:理論 + 実践 = 真の理解