それでは本題に入ります。
常識なのかもしれないですが、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 件のコメント:
コメントを投稿