MySQLネスト事務所で発生した問題

3304 ワード

MySQLはネストされたトランザクションをサポートしていますが、そうする人はあまりいません.この間、海外でMySQLのネストされたトランザクションの必要性を議論している外国人を見ました.私を笑わせて、このネストした鬼畜の使い方にはどんなシーンが必要ですか.   以前のdbaの同僚と話したことがありますが、どんなシーンでもMySQLネストされたトランザクションを使用しないでください.
では、MySQLネストトランザクションを使用すると、どのような問題が発生しますか?

mysql> select * from ceshi; 
+------+ 
| n  | 
+------+ 
|  1 | 
+------+ 
1 row in set (0.00 sec) 
 
mysql> start transaction ; 
Query OK, 0 rows affected (0.00 sec) 
 
mysql> insert into ceshi values(2); 
Query OK, 1 row affected (0.00 sec) 
 
mysql> start transaction ; 
Query OK, 0 rows affected (0.00 sec) 
 
mysql> insert into ceshi values(3); 
Query OK, 1 row affected (0.00 sec) 
 
mysql> commit; 
Query OK, 0 rows affected (0.00 sec) 
 
mysql> rollback; 
Query OK, 0 rows affected (0.00 sec) 

最後にrollbackでロールバックしましたが、データは  1 2 3  .    私のトランザクションはネストされた状態だと思っていたが、最後にrollbackがロールバックしたような気がしたが、実際にはサブトランザクションの実行に成功し、外層トランザクションの失敗がロールバックすることを望んでいた.  しかし、事実はそうではありません.最後の結果は  1 2 3 .

+-----+ 
| n   | 
+-----+ 
|  1 | 
|  2 | 
|  3 | 
+-----+ 
sql解釈器がstart transactionに遭遇するとcommitがトリガーされます...!!    begin_1  sql_1  begin_2  sql_2  sql_3 commit_1  rollback_1  . begin_2が実行されたとき、sql_1はすでに提出されており、commitを実行すると1の時、それではsql_2とsql_3提出されました.    この时rollbackに行ったら、きっと役に立たないに違いない....    前に提出したから、何を返してもいいですか.
前述したように、アーキテクチャ上でトランザクションをネストする人は一般的に少ないが、うっかりネストされることがある.pythonのプロジェクトでは、まず装飾器を使用してトランザクションのパッケージを実現し、データ処理def()と  def b()関数はすべてトランザクションにパッケージされ、単純にaとbで大丈夫で、すべて単一トランザクションです.  もしa論理でbが呼び出されたら、何が起こるのですか.   はい、トランザクションがネストされています...    これは数え切れないほどの業務開発に直面する問題だと思います.
では、どのようにしてこのリスクを回避しますか?  鍵をかけてもいいですよ....   グローバルロックを設定し、サブトランザクションが作成される前にロックのステータスを判断します....
flaskのフレームワークの場合、flask gグローバル変数を使用できます.  
djangoフレームワークの場合、thread localを使用してグローバル変数を使用できます.
tornado、geventのような非同期ioアーキテクチャであれば、fdを使用してコヒーレント変数の関連付けを行うことができます.

@decorator
def with_transaction(f, *args, **kwargs):
 
  db = connection.get_db_by_table("*")
  try:
    db.begin()
    ret = f(*args, **kwargs)
    db.commit()
  except:
    db.rollback()
    raise
  return ret
 
 
@with_transaction
def hide(self):
  '''    app   '''
  if self.status not in OrderStatus.allow_deletion_statuses():
    raise OrderStatusChangeNotAllowed(self.status, OrderStatus.deleted)
...
 
 
@with_transaction
def change_receipt_info(self, address, name, phone):
  region = Region.get_by_address(address)
  ...
次の文を実行すると、トランザクションが強制的にコミットされます.   もちろんここではautocommit=Trueが前提です.

ALTER FUNCTION  
ALTER PROCEDURE  
ALTER TABLE  
BEGIN  
CREATE DATABASE  
CREATE FUNCTION  
CREATE INDEX  
CREATE PROCEDURE  
CREATE TABLE  
DROP DATABASE  
DROP FUNCTION  
DROP INDEX  
DROP PROCEDURE  
DROP TABLE  
UNLOCK TABLES  
LOAD MASTER DATA  
LOCK TABLES  
RENAME TABLE  
TRUNCATE TABLE  
SET AUTOCOMMIT=1  
START TRANSACTION