1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Django + mysql: transaction.atomic() で select_for_update() を使ってレコードをロックしたときのメモ

Posted at

結論

以下のコードでmysqlでもtransaction.atomic()でレコードのロックができた

from django.db import transaction, connections

with transaction.atomic(using="database_name"):
    connections["database_name"].cursor().execute("BEGIN") # <-- ここ
    target_user = User.objects.select_for_update().get(id=1)
    target_user.name = "name"
    target_user.save()

やりたかったこと

djangoでデータベースのレコードをロックして悲観的排他制御を実装したかった

環境

Django 3.2.12
Python 3.6.15
Mysql 5.7.41

内容

まずデータベースでレコードをロックするために sql では、

$ BEGIN                   <-- トランザクション開始
$ SELECT **** FOR UPDATE  <-- テーブル、レコードのロック
$ UPDATE ****             <-- 更新
$ COMMIT                  <-- トランザクション終了

のように BEGINで始まりCOMMITで終わるトランザクション内で、FOR UPDATEを実行する必要がある

そして、Djangoでレコードをロックするには以下のようにすれば良いといろんな記事に書いてあった。

from django.db import transaction

with transaction.atomic(using="database_name"): # <--トランザクション開始
    target_user = User.objects.select_for_update().get(id=1) # <--ロック
    target_user.name = "name"
    target_user.save() # <--更新
# <-- トランザクション終了

しかし、このコードを実行してもレコードはロックされず、データベースのsqlのログを確認すると、以下のようにBEGINが実行されていなかった。

$ SELECT **** FOR UPDATE
$ UPDATE ****
$ COMMIT

つまりwith transaction.atomic(using="database_name"):BEGINが実行されていなかった。

ライブラリ(django.db.transaction)のコードを読むと、transaction.atomic()ではsettings.pyで指定したデータベースエンジンごとにトランザクションを開始しようとしていることがわかった。

django.db.backends.base.base の BaseDatabaseWrapper の set_auto_commit() メソッドでそれぞれのデータベースエンジンごとのトランザクション開始方法に従って処理を実行するようだった。

django.db.backends.*** (***はmysql, sqlite3など)をみてみると BaseDatabaseWrapper を継承しており、mysqlのクラスにはBEGINを実行するコードがなかった(sqlite3にはあった)

なので、transaction.atomic()で使われているconnection.cursor()を使ってBEGINを実行すれば問題なさそう。

connections["database_name"].cursor().execute("BEGIN")を追加し、解決。

思ったこと

そもそも、mysqlではBEGINが利用できるはずなのに、なぜ実行されていなかったのかわからない。
何か他に正しい解決方法がありそう。。。。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?