21415


私のサーバーシステムでは READ UNCOMMITTED をむしろ使用したいような状況が発生致しました。  
私の考えは正しいでしょうか？

掲示板アプリのサーバーにて、掲示板に投稿したとして以下の処理が発生します。

  * トランザクション 
    * トークテーブルに投稿を格納（insert）
    * 掲示板テーブルの トータル投稿数更新（サブクエリではなくcount:=count+１で） と 最終投稿時間を更新（update）
    * 掲示板テーブルから トータル投稿数 をセレクト（select）
  * このトランザクション実行後、３番めのSQLで取得した投稿数が１０００未満ならコミット  
そうではないならロールバック（投稿失敗）

要するに、投稿格納後に投稿数上限の１０００を超えていないかのチェックを行います。  
一見、 SERIALIZABLE がいいように思えますが（もちろんそうなのですが）  
処理性能のことも考えると、むしろここは一番最弱な READ UNCOMMITTED を使用したほうがいい気がしてきました。

READ UNCOMMITTED なら  
２つの投稿トランザクションが同時に処理された場合、どちらかが先に２つめのSQLを実行した時点で、もう片方はトランザクション中に３番めのSQLにてそのコミットされていない値を取得して、  
最大投稿数以上投稿してしまうことを防げると思ったからです。

この場合、上記の場合は

  * ２つとも投稿処理が失敗する可能性（お互い、更新後にセレクトしたら）  
→ そのぐらいべつにおｋ

  * 最新投稿時間がもう片方の投稿の時間になる可能性（セレクト順だけ逆転する）  
→ とは言えど、同時に発生した処理のためその差は数ミリ秒程度なのでおｋ

  * 片方だけロールバック発生したらカウントがおかしくなることがある（現在インクリメント系 count=count+1なので）  
→ 重要な不具合なのですがそもそもこのカラムをなくし、毎回取得時にカウントすることにする

が発生するかと思われますが、それは妥協とします。  
このようにあえて READ UNCOMMITTED の弱点を利用するってのはやめるべきでしょうか？


CC BY-SA 3.0
2016-01-25T04:10:16.457
21277
2016-01-28T04:03:39.787
2016-01-27T06:37:41.813
10346
10346
question
sql mariadb
READ UNCOMMITTED をむしろ使いたい状況？


データベースとは大量のデータを正確に扱うことを目的としています。

>   * ２つとも投稿処理が失敗する可能性（お互い、更新後にセレクトしたら）  
>  → そのぐらいべつにおｋ
>   * 最新投稿時間がもう片方の投稿の時間になる可能性（セレクト順だけ逆転する）  
>  → とは言えど、同時に発生した処理のためその差は数ミリ秒程度なのでおｋ
>

のように不正確でも構わないという条件ですと、`READ UNCOMMITTED`かどうかという議論の前提に立っていないと思います。というのも`READ
UNCOMMITTED`においてもデータベースエンジンは正確性を保証するよう動作するからです。

* * *

>   * トランザクション
>     * トークテーブルに投稿を格納（insert）
>     * 掲示板テーブルの トータル投稿数更新（サブクエリではなくcount:=count+１で） と 最終投稿時間を更新（update）
>     * 掲示板テーブルから トータル投稿数 をセレクト（select）
>   * このトランザクション実行後、３番めのSQLで取得した投稿数が１０００未満ならコミット  
>  そうではないならロールバック（投稿失敗）
>

という動作を不正確でも構わないというのであれば、

  * 投稿時 
    * 投稿数が1000以上であればエラー
    * トークテーブルに投稿を格納（insert）
    * その他の更新
  * 閲覧時 
    * 先頭の1000行のみ取り出し

とでもすればトランザクション自体も必要なくなります。また、元の案のような`INSERT`後にロールバックするよりも、上記のようなそもそも不用意に`INSERT`を行わない方がデータベースへの負担は少ないはずです。


CC BY-SA 3.0
2016-01-28T02:41:04.590
21415
2016-01-28T02:41:04.590
4236
21277
answer


> READ UNCOMMITTED なら  
>
> ２つの投稿トランザクションが同時に処理された場合、どちらかが先に２つめのSQLを実行した時点で、もう片方はトランザクション中に３番めのSQLにてそのコミットされていない値を取得して、  
>  最大投稿数以上投稿してしまうことを防げると思ったからです。

この前提は誤りです。

READ UNCOMMITTEDは「他のトランザクションによって変更されCOMMITされていないデータを **読む可能性がある**
」のであって、「COMMITされていないデータを **必ず読む** 」ではないのです。

999件のデータが登録された状態で2つのトランザクションが同時に走ったときに、

> 掲示板テーブルの トータル投稿数更新（サブクエリではなくcount:=count+１で） と 最終投稿時間を更新（update）

・想定通り999+1、1000+1となる  
・両方のトランザクションが999+1を行う

のどちらになるかはわかりません。


CC BY-SA 3.0
2016-01-28T04:03:39.787
21419
2016-01-28T04:03:39.787
5793
21277
answer