Search on the blog

2013年2月17日日曜日

トランザクション内のselectについて

 勉強不足のため間違ったことを書いているかもしれません。間違いがあれば、ご指摘いただければと思います。

それでは本題に入ります。

常識なのかもしれないですが、MySQL(engine = InnoDB)でトランザクションを使っていて気付いたこと。

「トランザクション内でselectを使う場合、もしselectしたデータを変更するのであれば、for updateを必ずつけないといけない。」

 トランザクション内のselect文にfor updateをつけないと、dirty readと呼ばれる現象が発生することがあります。例えば、

id account_no name deposit
1 12345678 yamada taro 1000
2 77777777 sato hanako 400000

のようなテーブルがあったとします。

今id=1の口座の預金額(deposit)が1, 000円だったとします。
トランザクションAが開始され、id = 1の口座から1, 000円引きます。トランザクションAは、まだcommitされていない状態とします。

このとき別のトランザクションBがid = 1の口座の預金額を参照すると、1, 000円になります。トランザクションAはコミットされていないので、他のトランザクションから見ると、預金額は0円ではなく1,000円となります。

ここでトランザクションBが、「id=1の口座から500円引く」とう処理を行うと1,000 - 500 = 500円がid=1の口座の預金額になりおかしなことになってしまいます。(トランザクションAの処理が無かったことになる。)

このようにコミットされる前の古いデータを読んでしまうことをdirty readと言います。

(↑この部分は自信がありません。
MySQLのInnoDBのデフォルトのisolation levelはrepeatable readでこのisolation levelではdirty readは発生しない[参照1]。とあるので、上の現象はdirty readと呼ばないのか?それとも更新のために使うselectにはfor updateをつける前提で話をしているのか?)

これを防止するためには、トランザクションBでデータを取得するときにselect for update文を使います。for updateを付けると、トランザクションAがcommit/rollbackされるまでデータは取得されません。



0 件のコメント:

コメントを投稿