MySQL のネストされたトランザクションで発生した問題の詳細なコード例

黄舟
リリース: 2017-03-06 13:26:28
オリジナル
1022 人が閲覧しました

この記事は主に、MySQL のネストされたトランザクションで発生する問題に関する関連情報を紹介します。必要な友人は参照してください。

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)
ログイン後にコピー


最後にロールバックしましたが、データは 1 2 3 と表示されていました。実際、ロールバックは最終的にロールバックされたように見えますが、実際に確認したい結果は、サブトランザクションが正常に実行され、外側のトランザクションの失敗がロールバックされることです。しかし、これは当てはまらず、最終結果は 1 2 3 です。

+-----+ 
| n   | 
+-----+ 
|  1 | 
|  2 | 
|  3 | 
+-----+
ログイン後にコピー

SQL インタプリタがトランザクション開始に遭遇すると、コミットがトリガーされます... !!!

begin_1 sql_1 begin_2 sql_2 sql_3 commit_1 rollback_1 .

begin_2 が実行されると, sql_1 すでにサブミットされています。再度 commit_1 を実行すると、sql_2 と sql_3 がサブミットされます。この時点でロールバックを行ってもダメです。すべてが以前にサブミットされているため、何をロールバックできますか。 ..

前に述べたように、アーキテクチャの観点からは、ネストされたトランザクションを使用する人はほとんどいませんが、誤ってネストされてしまうことがあります。 例として Python プロジェクトを取り上げます。まず、デコレーターを使用してトランザクションのパッケージ化を実装します。次に、データ処理関数 def a() と def b() をトランザクションによってラップします。a と b を使用するだけでも問題ありません。単一のトランザクション。ロジックが b を再度呼び出すと、何が起こるでしょうか?はい、トランザクションは入れ子になっています...これは、ほとんどのビジネス開発で遭遇する問題だと思います。

では、このリスクを回避するにはどうすればよいでしょうか? ロックを追加できます...グローバル ロックを設定すると、サブトランザクションが作成される前にロックのステータスが判断されます...

Flask フレームワークの場合、flask g グローバル変数を使用できます。

Django フレームワークの場合、スレッドローカルを使用してグローバル変数を使用できます。

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
ログイン後にコピー

上記は、MySQL のネストされたトランザクションで発生する問題のコード例の詳細な説明です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート