Django 事務詳解

weixin_33912445發表於2017-08-04

Django 1.4 事務預設模式是autocommit模式,每個查詢都相當於一個事務,每次查詢都直接提交commit事務,
除非事務被禁止

Django’s default behavior is to run in autocommit mode. Each query is immediately committed to the database, unless a transaction is active.

Transaction

  • @transaction.autocommit() 使用django預設事務模式,就是每個查詢都相當於一個事務,提交後自動commit
  • @transactioncommit_on_success() ((django 1.8: transaction.atomic() ) 自己控制view或者其他需要事務的程式碼
  • @transaction.commit_manually() 自己控制commit和rollback,所以程式碼裡面必須顯式呼叫commit 或者 rollback,哪怕只有讀的查詢

在view裡面的使用:

  1. 裝飾器
@transaction.commit_on_success
def view():
    a.save()
    b.save()
    return ...
  1. with用法
def func()
    with transaction.commit_on_success():
        a.save()
        b.save()
    return ...

執行檢視mysql 的genernal_log

        # django 根據db讀寫路由,使讀和寫各生成了一個connect 連結資料庫

        # 32455 這個是負責查詢
        32455 Connect   root@172.16.30.17 on owan
        32455 Query SET NAMES utf8mb4
        32455 Query set autocommit=0
        32455 Query SET SQL_AUTO_IS_NULL = 0
        32455 Query SELECT ... from a WHERE `id` = 20

        # 32456 這個是負責更新
        32456 Connect   root@172.16.30.17 on owan
        32456 Query SET NAMES utf8mb4
        32456 Query set autocommit=0
        32456 Query SET SQL_AUTO_IS_NULL = 0
        32456 Query SELECT (1) AS `a` FROM `user_community_signin` WHERE `user_community_signin`.`id` = 20  LIMIT 1
        32456 Query UPDATE a SET `user_id` = ....

        # 32455 這個是負責查詢 ,第二個查詢,依然使用32455這個連結
        32455 Query SELECT ...from b where .`id` = 1

        # 32456 依然使用這個32456連結來更新資料
        32456 Query SELECT (1) AS `a` FROM `community_point` WHERE `community_point`.`id` = 1  LIMIT 1
        32456 Query UPDATE b set `id` = ...

        # 32456  更新操作commit
        32456 Query commit    
        # (rollback 如果兩個更新有任何出錯)
        # 32455 Query   rollback    
        # 32456 Query   rollback   
        32455 Quit  
        32456 Quit

如果沒有加 transaction.commit_on_success裝飾器

        32678 Query UPDATE  a ...
        32678 Query commit

        32678 Query UPDATE b ...
        32678 Query commit

讓我們理清這個流程:

  • django 預設模式是每個查詢都是一個事務,預設模式會在a.save(), b.save()分別提交commit一次,這樣就沒有兩個操作包在一起做事務的功能.所以我們使用了 transaction.commit_on_success 裝飾器, 意味著全部操作成功才提交commit,而不是每個查詢各自commit
  • 在view裡面使用 transaction.commit_on_success, 使這個view的所有操作都是事務執行的
  • 由於django的讀寫db有設定db路由,使得讀寫db分別使用了不同的DB連結,看general_log看出來,讀和寫都有一個connect的操作 讀id: 32455, 寫id: 32466
  • id: 32466連結負責更新操作,成功之後commit完成事務,如果中途某個更新操作出錯,將會回滾事務rollback

Savepoints

savepoint是一個可以讓事務回滾到指定位置的標記, 預設的事務回滾時整個事務所有操作都回滾,這樣開銷會大,如果想回滾一部分,那就用savepoint標記某個位置,回滾的時候回到某個點

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid)
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)
      # open transaction now contains only a.save()

  transaction.commit()

Mysql 事務

MySQL預設操作模式就是autocommit自動提交模式。這就表示除非顯式地開始一個事務,否則每個查詢都被當做一個單獨的事務自動執行。我們可以通過設定autocommit的值改變是否是自動提交autocommit模式。
通過以下命令可以檢視當前autocommit模式

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.04 sec)
從查詢結果中,我們發現Value的值是ON,表示autocommit開啟。我們可以通過以下SQL語句改變這個模式
1
mysql> set autocommit = 0;
值0和OFF都是一樣的,當然,1也就表示ON。通過以上設定autocommit=0,則使用者將一直處於某個事務中,直到執行一條commit提交或rollback語句才會結束當前事務重新開始一個新的事務。

相關文章