SQL同時実行制御とトランザクション入門!初心者でも失敗しないデータの守り方
生徒
「データベースって、世界中の人が同時に使っても中身がめちゃくちゃにならないんですか?」
先生
「鋭いですね。実は、何も対策をしないと中身が壊れてしまうことがあります。それを防ぐのが『同時実行制御』という仕組みです。」
生徒
「同時に動く仕組み…難しそうですね。プログラミングの知識がゼロでも分かりますか?」
先生
「安心してください。今回は『銀行の口座』や『限定商品の予約』など、身近な例えを使って、データがどうやって守られているかを丁寧に解説します。」
1. SQLとは何か?
SQLは、データベースと呼ばれる「大量のデータを整理して保存する箱」に対して指示を出すための言語です。例えば、会員名簿の中から特定の人を探したり、新しい人を追加したりするときに使います。今回のテーマである「同時実行制御」を理解する上でも、このSQLを使ってデータベースに命令を出すことが基本となります。
2. 同時実行制御とは?データの衝突を防ぐ守護神
データベースは、通常一人だけで使うものではありません。例えば、大人気のアーティストのライブチケット予約サイトを想像してみてください。数万人の人が、一斉に「予約ボタン」を押しますよね。もし、残りの座席が「1つ」しかないときに、2人の人が同時にボタンを押したらどうなるでしょうか。
何も対策をしていないと、システムが混乱して「1つしかない席に2人が座っている」というおかしなデータが保存されてしまうかもしれません。こうした、複数の人が同時に同じデータを書き換えようとしたときに起こるトラブルを防ぎ、データの整合性(つじつまが合っていること)を保つための仕組みを「同時実行制御」と呼びます。
この制御のおかげで、私たちの銀行残高が勝手に減ったり、予約したはずの席が他人のものになっていたりすることを防いでいるのです。まさに、インターネットの世界でデータを守る守護神のような役割を果たしています。
3. トランザクション:一連の作業を「セット」にする
同時実行制御を語る上で欠かせないのが「トランザクション」という概念です。トランザクションとは、「これ以上は分けられない一連の処理のまとまり」のことです。
例えば、あなたが友人のAさんに銀行振込で1,000円を送るとします。このとき、銀行のシステムの中では以下の2つの処理が行われます。
- あなたの口座から1,000円を引く(マイナスする)
- Aさんの口座に1,000円を入れる(プラスする)
もし、あなたの口座からお金が引かれた直後にシステムが故障して、Aさんの口座にお金が入らなかったらどうなるでしょう?あなたの1,000円は消えて無くなってしまいます。これは絶対に避けなければなりません。
そこで、「引き落とし」と「入金」を一つの「セット(トランザクション)」として扱います。「どちらも成功するか、どちらも失敗して元の状態に戻るか」のどちらかしか認めないというルールを作るのです。
4. 実際にSQLでデータを操作してみよう
まずは、現在の銀行口座の状態を確認しましょう。ここでは、銀行の利用者が4人いるテーブル(表)を例にします。
id | name | balance | last_update
---+----------+---------+-------------------
1 | 山田太郎 | 50000 | 2026-01-01
2 | 佐藤花子 | 30000 | 2026-01-01
3 | 鈴木一郎 | 10000 | 2026-01-01
4 | 田中節子 | 20000 | 2026-01-01
ここで、「山田太郎さん」が「佐藤花子さん」に5,000円を送金するSQLを書いてみます。処理を一つにまとめるために「BEGIN(開始)」と「COMMIT(確定)」というキーワードを使います。
-- 取引の開始
BEGIN;
-- 山田さんの残高を5,000円減らす
UPDATE bank_accounts
SET balance = balance - 5000
WHERE name = '山田太郎';
-- 佐藤さんの残高を5,000円増やす
UPDATE bank_accounts
SET balance = balance + 5000
WHERE name = '佐藤花子';
-- 全ての処理が正しければ確定させる
COMMIT;
実行後のテーブルは以下のようになります。
id | name | balance | last_update
---+----------+---------+-------------------
1 | 山田太郎 | 45000 | 2026-01-01
2 | 佐藤花子 | 35000 | 2026-01-01
3 | 鈴木一郎 | 10000 | 2026-01-01
4 | 田中節子 | 20000 | 2026-01-01
山田さんの残高が減り、佐藤さんの残高が増えているのが分かりますね。
5. 同時実行制御の要「ロック」の仕組み
複数の人が同時にデータを触る際、データベースは「ロック(鍵)」という機能を使います。これは、「今、私がこのデータを書き換えている最中だから、他の人は終わるまで待っていてね」という予約札のようなものです。
例えば、スーパーの在庫管理システムを考えてみましょう。在庫が残り5個の「卵」を、2人の店員が同時に更新しようとしています。
店員Aさんが「卵の在庫を10個に増やそう」として、卵のデータに「鍵」をかけます。その直後に店員Bさんが「賞味期限が切れたから2個減らそう」と思っても、鍵がかかっているので店員Bさんは待たされます。店員Aさんの更新が終わって鍵が開いた後に、ようやく店員Bさんの処理が始まります。
これを防がないと、後から書き込んだ人のデータで上書きされてしまい、片方の作業が無効になってしまうのです。この現象を「ロストアップデート(更新の紛失)」と言います。
次に、商品の在庫数を変更する別のSQLを見てみましょう。
id | product_name | stock | category
---+--------------+-------+----------
1 | リンゴ | 50 | フルーツ
2 | バナナ | 20 | フルーツ
3 | 牛乳 | 15 | 飲料
4 | 食パン | 10 | 食品
5 | 卵 | 30 | 食品
-- 在庫更新のトランザクション
BEGIN;
-- リンゴの在庫を10個売れたので減らす
UPDATE products
SET stock = stock - 10
WHERE product_name = 'リンゴ';
-- 牛乳の在庫を5個売れたので減らす
UPDATE products
SET stock = stock - 5
WHERE product_name = '牛乳';
COMMIT;
実行後の結果です。
id | product_name | stock | category
---+--------------+-------+----------
1 | リンゴ | 40 | フルーツ
2 | バナナ | 20 | フルーツ
3 | 牛乳 | 10 | 飲料
4 | 食パン | 10 | 食品
5 | 卵 | 30 | 食品
6. 複数処理が同時に動く際のトラブル例
同時実行制御がうまく働かないと、どのような困ったことが起きるのでしょうか?代表的なものをいくつか紹介します。パソコンに触れたことがない方でも、日常の場面に置き換えると理解しやすいはずです。
① ダーティリード(Dirty Read)
「汚れた読み取り」という意味です。誰かがデータを書き換えている途中の、まだ確定していない「仮」の状態のデータを、他の人が読み取ってしまうことです。 例えば、銀行で振込作業中の「残高が減ったけれど、相手にまだ届いていない」という一瞬の隙間を誰かに見られてしまうようなものです。もしその振込がキャンセルされたら、見たデータは嘘の情報になってしまいます。
② ノンリピータブルリード(Non-repeatable Read)
「同じ処理を繰り返せない読み取り」です。1回目の読み取りと2回目の読み取りで、結果が変わってしまう現象です。 例えば、あなたが「1,000円のケーキがあるな」と確認して、レジでお金を払おうとした瞬間に、店員さんが「今日から1,200円に値上げです」と価格表を書き換えてしまったような状態です。
③ ファントムリード(Phantom Read)
「幽霊(ファントム)の読み取り」です。最初は存在しなかったデータが、2回目の確認では突然増えて見える現象です。 会員数を数えた直後に、別の担当者が新しい会員を1名登録して確定させると、さっき数えた数と合わなくなります。まるで幽霊が現れたかのようにデータが増えるのでこう呼ばれます。
7. 分離レベル:厳しさを設定する
データベースには、「どれくらい厳しく同時実行を制限するか」という設定があります。これを「分離レベル(アイソレーションレベル)」と呼びます。
制限をガチガチに厳しくすれば、データの正確さは完璧になります。しかし、その分、みんなが順番待ちをすることになり、動作がとても遅くなってしまいます。逆に制限をゆるくすれば、動作は速くなりますが、データのミスが起きる可能性が高まります。
プログラミングやシステムの設計では、そのシステムが「絶対に間違いが許されない銀行系」なのか、「多少のズレは後で直せばいいSNSのいいね数」なのかによって、この厳しさを使い分けています。
最後に、会員名簿のデータを整理するSQLを紹介します。
id | user_name | rank | point
---+-----------+--------+-------
1 | 田中一郎 | Silver | 1200
2 | 鈴木次郎 | Gold | 5500
3 | 佐藤三郎 | Bronze | 300
4 | 高橋四郎 | Gold | 8900
5 | 伊藤五郎 | Silver | 2100
-- 会員情報の更新
BEGIN;
-- ポイントが5000以上の人を自動的にGoldランクにする
UPDATE users
SET rank = 'Gold'
WHERE point >= 5000;
-- 特定のユーザーを削除する(退会処理)
DELETE FROM users
WHERE user_name = '佐藤三郎';
COMMIT;
実行結果です。
id | user_name | rank | point
---+-----------+--------+-------
1 | 田中一郎 | Silver | 1200
2 | 鈴木次郎 | Gold | 5500
4 | 高橋四郎 | Gold | 8900
5 | 伊藤五郎 | Silver | 2100
8. まとめ:同時実行制御は信頼の要
データベースが複数の処理を同時にこなせるのは、裏側で「同時実行制御」という高度な管理が行われているからです。 「トランザクション」で一連の動作をひとまとめにし、「ロック」を使って他の人の割り込みを防ぐ。そして、用途に合わせて「分離レベル」で厳しさを調整する。
私たちが毎日当たり前のようにスマホで買い物をしたり、アプリを使ったりできるのは、このSQLとデータベースの仕組みが、一瞬の狂いもなく正確に動いているおかげなのです。 一見難しそうに感じる用語も、こうして日常のルールに当てはめてみると、意外とシンプルで合理的なものだと感じていただけたのではないでしょうか。