カテゴリ: データベース 更新日: 2026/04/04

データベースの「ノンリピータブルリード」とは?初心者向けに原因と対策を徹底解説!

ノンリピータブルリードとは?発生条件と対策を初心者向けに解説
ノンリピータブルリードとは?発生条件と対策を初心者向けに解説

先生と生徒の会話形式で理解しよう

生徒

「先生、データベースの勉強をしていたら『ノンリピータブルリード』っていう難しい言葉が出てきました。これって一体どういう意味なんですか?」

先生

「直訳すると『繰り返し読めない』という意味ですね。一言でいうと、さっき見たはずのデータが、もう一度見たら勝手に変わってしまっている現象のことですよ。」

生徒

「えっ、勝手に変わっちゃうんですか?それは困りますね。銀行の残高とかだったら大変なことになりそう…。」

先生

「その通りです。だからデータベースには、そうならないための『同時実行制御』という仕組みがあるんです。今日は初心者の方にも分かりやすく、その仕組みと対策を解説しますね。」

1. SQLとは何か?

1. SQLとは何か?
1. SQLとは何か?

SQL(エスキューエル)は、データベースと呼ばれる「大量のデータを整理して保存する箱」に対して指示を出すための言語です。例えば、会員名簿の中から特定の人を探したり、新しい人を追加したり、すでにある情報を書き換えたりするときに使います。パソコンに詳しくない方でも、「Excelのような大きな表を操作するための専用の合言葉」だと考えればイメージしやすいでしょう。

エンジニアの必須スキル「SQL」を、 図解と豊富な練習問題でゼロから体系的に学びたい人へ。 MySQLやPostgreSQLなど、各種データベースに対応した不朽の入門書です。

SQL 第2版 ゼロからはじめるデータベース操作をAmazonで見る

※ Amazon広告リンク

2. トランザクションと同時実行制御の基本

2. トランザクションと同時実行制御の基本
2. トランザクションと同時実行制御の基本

データベースを扱う上で欠かせないのが「トランザクション」という考え方です。これは、関連する複数の処理を「ひとまとめ」にしたものです。例えば、銀行振込では「自分の口座からお金を引く」処理と「相手の口座にお金を足す」処理がセットで行われます。このセットがトランザクションです。

そして、データベースは世界中の人が同時にアクセスします。複数の人が同じデータを同時に書き換えようとしたときに、矛盾が起きないように交通整理をすることを「同時実行制御(どうじじっこうせいぎょ)」と呼びます。この交通整理がうまくいかないと、今回解説するノンリピータブルリードのような不具合が発生してしまいます。

3. ノンリピータブルリードとは?発生する仕組み

3. ノンリピータブルリードとは?発生する仕組み
3. ノンリピータブルリードとは?発生する仕組み

ノンリピータブルリード(不可説読:ふかせつどく)とは、一つのトランザクションの中で、同じデータを2回読み込んだとき、1回目と2回目で内容が異なってしまう現象を指します。名前の通り「リピート(繰り返し)して読むことができない」状態です。

これは、自分がデータを読んでいる最中に、別の誰かがそのデータを書き換えて「確定(コミット)」させてしまった場合に発生します。具体的な例で考えてみましょう。

身近な例:在庫チェック
1. あなたがネットショップの在庫(10個)を確認しました。
2. 確認している数秒の間に、別の誰かがその商品を1個購入し、在庫が9個に更新されました。
3. あなたがもう一度在庫を確認すると、さっきまで10個だったはずが9個になっています。

4. 具体的なSQLでの発生パターンを見てみよう

4. 具体的なSQLでの発生パターンを見てみよう
4. 具体的なSQLでの発生パターンを見てみよう

まずは、現在の「商品在庫テーブル(products)」の状態を確認しましょう。ここでは「高級リンゴ」の在庫を管理していると仮定します。


id | product_name | stock | price
---+--------------+-------+-------
1  | 高級リンゴ    | 10    | 500
2  | 完熟メロン    | 5     | 3000
3  | 甘いバナナ    | 20    | 200

ここで、トランザクションA(あなた)が「高級リンゴ」の在庫を確認します。まだこの処理は終わっていません(終了の合図であるCOMMITを出していません)。


-- トランザクションAの1回目の読み込み
SELECT stock FROM products WHERE id = 1;

stock
-----
10

この直後、別の処理であるトランザクションBが動き出し、在庫を更新してしまいました。


-- トランザクションBが勝手に更新して確定させる
UPDATE products SET stock = 9 WHERE id = 1;
COMMIT;

その後、まだ処理の途中だったトランザクションAが、再度確認のために同じSQLを実行します。


-- トランザクションAの2回目の読み込み
SELECT stock FROM products WHERE id = 1;

stock
-----
9

「さっき見たときは10だったのに、今は9になっている!」 これがノンリピータブルリードです。同じ処理の中なのに、データの整合性が取れなくなってしまいました。最終的なテーブルは以下の通りです。


id | product_name | stock | price
---+--------------+-------+-------
1  | 高級リンゴ    | 9     | 500
2  | 完熟メロン    | 5     | 3000
3  | 甘いバナナ    | 20    | 200

5. なぜノンリピータブルリードが問題なのか?

5. なぜノンリピータブルリードが問題なのか?
5. なぜノンリピータブルリードが問題なのか?

「最新のデータが見れるなら、それでいいんじゃない?」と思うかもしれません。しかし、プログラムの世界では大問題です。例えば、在庫が10個あることを確認して「10個分の発送ラベルを印刷する処理」を始めたとします。その途中で在庫が9個に変わってしまうと、存在しない10個目のラベルを作ってしまうことになり、システムがエラーで止まったり、誤出荷の原因になったりします。

お金の計算や、厳密な在庫管理が必要なシステムにおいて、処理の途中で値が変わってしまうことは、信頼性を損なう重大なバグに繋がるのです。

6. 解決策:分離レベル(アイソレーションレベル)の変更

6. 解決策:分離レベル(アイソレーションレベル)の変更
6. 解決策:分離レベル(アイソレーションレベル)の変更

この問題を解決するために、データベースには「分離レベル(Isolation Level)」という設定があります。これは「他の人の処理の影響をどれだけ受けないようにするか」という壁の厚さを決める設定です。レベルが高いほど安全ですが、動作は少し重くなります。

一般的な分離レベルは以下の4段階です:

  • READ UNCOMMITTED:一番低い。他人が書き換え中の確定していないデータまで見えてしまう(ダーティリードが発生)。
  • READ COMMITTED:一般的。確定したデータのみ見えるが、今回のような「ノンリピータブルリード」は防げない。
  • REPEATABLE READ:今回の対策!一度読んだデータは、自分の処理が終わるまで他の人が変えても「見かけ上」変わらないように保護する。
  • SERIALIZABLE:最強。順番待ちをさせて、完全に一人ずつ処理する。絶対に間違いは起きないが、非常に遅い。

ノンリピータブルリードを防ぐには、設定を「REPEATABLE READ」以上に変更する必要があります。これにより、自分が最初に見たデータが、処理が終わるまで保証されるようになります。

7. 実践:REPEATABLE READでデータを保護する

7. 実践:REPEATABLE READでデータを保護する
7. 実践:REPEATABLE READでデータを保護する

では、対策を行った場合の動きを見てみましょう。分離レベルを「REPEATABLE READ」に設定して、先ほどと同じ操作をシミュレーションします。まずは初期状態のテーブルです。


id | name     | point | rank
---+----------+-------+-------
1  | 田中さん | 100   | A
2  | 山本さん | 50    | B
3  | 木村さん | 200   | S
4  | 岡田さん | 10    | C

トランザクションA(分離レベル:REPEATABLE READ)が田中さんのポイントを確認します。


-- トランザクションA(保護レベルを上げて開始)
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT point FROM users WHERE id = 1;

point
-----
100

ここでトランザクションBが田中さんのポイントを更新して、しっかり「確定(COMMIT)」まで完了させたとします。


-- トランザクションBがポイントを更新
UPDATE users SET point = 120 WHERE id = 1;
COMMIT;

この後、トランザクションAがもう一度同じデータを確認します。ここがポイントです!


-- トランザクションAの2回目の読み込み
SELECT point FROM users WHERE id = 1;

point
-----
100

見てください!トランザクションBが120に更新したはずなのに、トランザクションAの中では「100」のままです。これがREPEATABLE READ(繰り返し読み込み可能)の効果です。データベースが「Aさんは最初に100を見たから、Aさんの処理が終わるまではずっと100として見せてあげよう」と配慮してくれているのです。

トランザクションAが自分の処理をすべて終えて終了(COMMIT)した後に、改めてデータを見れば、最新の120が反映されています。このようにして、処理の一貫性を守っているのです。

8. まとめ:正しい知識で信頼できるシステムを作ろう

8. まとめ:正しい知識で信頼できるシステムを作ろう
8. まとめ:正しい知識で信頼できるシステムを作ろう

データベースの世界には、このように「複数の人が同時に使うからこそ起きる問題」がたくさんあります。ノンリピータブルリードはその代表的なものの一つです。初心者のうちは難しく感じるかもしれませんが、「データの読み込み中に誰かに書き換えられてしまう失敗」だと覚えておけば十分です。

実際の開発現場では、どの程度の安全さ(分離レベル)が必要かを慎重に選ぶことが求められます。銀行のような絶対に間違いが許されない場所、SNSの「いいね」数のように多少の誤差は許される場所、それぞれの目的に合わせて設定を使い分けるのがプロの技です。

もしあなたがプログラムを組むことになったら、「このデータは処理の途中で変わっても大丈夫かな?」と一瞬立ち止まって考えてみてください。その視点を持つだけで、あなたの作るシステムの品質はグッと高まるはずです。基礎を一つずつ積み上げて、安全で便利なデータベース活用を目指しましょう!

カテゴリの一覧へ
新着記事
New1
データベース
SQLでデータベースを操作する流れとは?テーブル・行・列の関係を図解で理解
New2
Ruby
プロキシ環境でも安心!社内ネットワーク下でのRuby gemインストール完全ガイド【SSL対応も解説】
New3
Ruby
Rubyのリテラル総まとめ!初心者でもわかる数値・文字列・配列・ハッシュ・正規表現・範囲
New4
Rails
Railsのimportmap入門|Node不要でJavaScriptを使う方法と落とし穴をやさしく解説
人気記事
No.1
Java&Spring記事人気No1
データベース
SQLで複数テーブルを結合する方法を徹底解説!初心者でも図解でわかるJOINと集計の基本
No.2
Java&Spring記事人気No2
Ruby
WindowsでRubyをインストールする方法!RubyInstallerとMSYS2を使った完全ガイド
No.3
Java&Spring記事人気No3
Rails
Hotwire超入門:Turbo Drive / Turbo Frames / Turbo Streams を一気に理解【Rails 7 / Rails 8】
No.4
Java&Spring記事人気No4
Rails
Rails 7/8対応|RSpec入門:describe・context・it・expectを初心者向けに完全解説
No.5
Java&Spring記事人気No5
データベース
PostgreSQLが遅い原因を解決!初心者向けデータベースチューニングと高速化の基本
No.6
Java&Spring記事人気No6
Ruby
Rubyの条件分岐をスッキリ!早期リターンとガード節で読みやすいコードへ
No.7
Java&Spring記事人気No7
データベース
データベース設計の要!外部キー(Foreign Key)とは?初心者向けに徹底解説
No.8
Java&Spring記事人気No8
Ruby
Ruby配列の抽出と条件処理を完全解説!select・reject・take・dropの定番パターン