1. ホーム
  2. spring

SpringトランザクションとMySQLトランザクション詳細インタビュー

2022-02-14 17:59:09
<パス

記事目次

データベーストランザクション

トランザクションとは

トランザクションとは、データベース操作の最小の作業単位で、1つの論理的な作業単位として実行される一連の操作のことです。これらの操作は、全部または全部をシステム上にまとめてコミットされます。トランザクションは、不可分の操作の集合体です。

トランザクションの4大特徴

  1. アトミック性
    トランザクションは、データベースの論理的な作業単位であり、トランザクションに含まれる各操作は、そのすべてを行うか、まったく行わないかのいずれかである

  2. 一貫性
    トランザクションの実行結果は、データベースをある一貫性の状態から別の状態に変更することでなければなりません。

  3. 分離
    トランザクションの実行は、他のトランザクションによって妨害されることはありません。つまり、トランザクション内の操作とそれが使用するデータは、他の同時実行トランザクションから分離されており、同時実行トランザクションは互いに干渉することができません。

  4. 永続性
    永続性とも呼ばれ、トランザクションがコミットされると、データベース内のデータに対するその変更が永続的に行われることを意味します。その後に続く他の操作や障害は、その実行結果に影響を及ぼしてはならない。

MySQL トランザクションの分離レベル

<テーブル 絶縁レベル アイソレーションレベルの値 による問題 リード・アンコミットメント 0 ダーティリードの原因 リード・コミット 1 ダーティリードの回避、非再現性、ファントム・リードの許可 反復可能な読み取り 2 MySQL のデフォルトの分離レベル。ダーティリードの回避、重複読み込みの禁止、ファントムリードの許可 シリアライザブル 3 シリアルリード、トランザクションは1つずつしか実行できないため、ダーティリード、イレギュラーリード、ファントムリードを回避することができます。実行効率が落ちるので、注意して使用すること

1.ダーティリード

トランザクションは、データの追加、削除、チェックを行うが、トランザクションをコミットしない。別のものがコミットされていないデータを読むことができ、最初のトランザクションがロールバックを行うと、2番目のトランザクションがダーティデータを読みます。

リーダーがZhang Sanに支払いを行い、Zhang Sanの口座に10,000ドルが入金されましたが、Zhang Sanが給与明細を確認しに行き、10,000ドルが届いていることを知った時、まだ取引はコミットされていません。この時点で、リーダーはZhang Sanの給与が5,000ドル余分にカウントされていることを発見し、取引をロールバックして金額を修正し、取引をコミットする。結局、Zhang Sanの実際の到着は5,000ドルだけだった。

2. 非再現性

あるトランザクションが2回の読み取り操作を行い、1回目と2回目の読み取りが一致しない場合、別のトランザクションが2回の読み取り操作の間にデータを修正する。

既約性の焦点は、データの更新と削除にあり、これには 行レベルロック 繰り返しの読み取り隔離レベルを実現することができます。

Zhang Sanは1000ドルを転送する必要があり、システムはカード残高が2000ドルと読み取り、Zhang Sanの妻は正確に2000ドルを転送する必要があり、Zhang Sanが取引を提出する前に2000ドルを転送すると、Zhang Sanが転送を提出するとき、システムは残高不足をプロンプト表示します。

3. ファントムリーディング

ファントム・リードとは、あるトランザクションがある範囲の行を読み取った後、別のトランザクションがその範囲に新しい行を挿入し、前のトランザクションがその範囲の行を再び読み取ったときにファントム・ローが発生することをいいます。

ファントム・リードは、繰り返し不可能な読み取りよりも、他のトランザクションによって追加された新しいデータに関するものです。行レベルのロックで非再現性の読み取りを回避することは出来ますが、ファントム・リードを解決することは出来ません。ファントム・リードを解決する唯一の方法はSerializable分離レベルによるものです。

Zhang Sanの妻は、Zhang Sanの今月のクレジットカードの使用記録を印刷する準備をします。確認したところ、彼女は合計1,000ドルを2回使っていることがわかりました。一方、Zhang Sanはマッサージを終え、1,000ドルを使い、チェックアウトしようとしたところ、銀行の記録に新しい1,000ドルの支出記録が追加されました。張さんの妻が消費記録をプリントアウトすると、総額が2,000元に変わっており、張さんの妻は驚いたという。

4. 連載読み物

Serializable は最も高い分離レベルですが、パフォーマンスは非常に低く、一般的にはほとんど使用されません。このレベルではトランザクションは直列的かつ順次的に実行され、ダーティで不可逆的な読み取りだけでなくファントム・リードも回避されます。

MySQL の現在のトランザクション分離レベルを表示する

MySQL InnoDB のデフォルトのトランザクション分離レベルは

REPEATABLE-READ
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+

MySQLのデフォルトの操作モードは自動コミットモードです。

各クエリは、トランザクションが開いていることが表示されない限り、自動的に個別のトランザクションとして実行されます。autocommitの値を設定することで、デフォルトのコミットモードを変更することができます。

  1. 現在のコミットモードを表示する
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+


  1. 自動投稿をオフにします。0がオフ、1がオンです。
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+

Connection connection = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;

try {
    Class.forName("com.mysql.jdbc.Driver");
    connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbname?characterEncoding=utf-8","username" , "password");

    connection.setAutoCommit(false);
    
    // others ......
    
    connection.commit();
} catch (Exception e) {
    connection.rollback();
} finally {
    connection.setAutoCommit(true);
    // close connection
}


JDBC処理トランザクション

TransactionDefinition
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;


スプリングトランザクション

Springのトランザクションは、基本的にデータベースのトランザクションをサポートするものです。もしデータベースがトランザクションをサポートしていなければ(例えばMySQLのMyISAMエンジンはトランザクションをサポートしていない)、Springのトランザクションも効力を持ちません。

Springのトランザクションプロパゲーション

トランザクションの伝播動作とは、トランザクションメソッドAが他のトランザクションメソッドBから呼び出されたときに、トランザクションAが何をすべきかということです。トランザクションAがトランザクションBで実行されるべきか、それとも別のトランザクションを開始すべきかは、トランザクションAの伝播動作で決まります。

トランザクション伝搬属性定義

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB();
    // do something
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}

<テーブル 定数名 定数説明 伝搬の必要性 現在のトランザクションをサポートし、現在のトランザクションがない場合は新しいトランザクションを作成します。これはSpringのデフォルトのトランザクションの伝搬です。 propagation_supports 現在のトランザクションをサポートし、現在のトランザクションがない場合は非トランザクションとして実行されます。 伝播義務 現在のトランザクションをサポートし、現在のトランザクションが存在しない場合は例外を投げる。 伝搬要求の新規作成 新しいトランザクションを作成し、現在のトランザクションが存在する場合はハングアップさせます。新しいトランザクションは、保留中のトランザクションと何の関係も持たず、2つの独立したトランザクションとなります。外側トランザクションがロールバックに失敗した後、内側トランザクションの実行結果をロールバックできないか、内側トランザクションが例外を投げることに失敗し、外側トランザクションがそれをキャッチするか、ロールバック操作を処理できないか、です。トランザクションマネージャとしてJtaTransactionManagerを使用する。 propagation_not_supported 非トランザクション方式で操作を実行し、現在のトランザクションが存在する場合は、それをハングアップさせる。トランザクションマネージャとしてJtaTransactionManagerを使用する。 プロパゲーション_ネバー 非トランザクション方式で実行し、現在トランザクションが存在する場合は例外を投げる。 プロパゲーション・ネステッド アクティブなトランザクションが存在する場合、ネストされたトランザクションで実行されます。アクティブなトランザクションが存在しない場合、REQUIRED属性に従って実行される。これは、ロールバック可能な複数のセーブポイントを持つ単一のトランザクションを使用します。内部トランザクションのロールバックは、外部トランザクションには影響しない。DataSourceTransactionManagerトランザクションマネージャに対してのみ機能します。

プロパゲーション必須

現在のトランザクションが存在する場合はそれをサポートし、存在しない場合はそれをオープンします。

以下の例では、メソッドBが単独で呼び出されたとき、現在のコンテキストにトランザクションが存在しないため、新しいトランザクションが開かれる。

メソッドAを呼び出すと、現在のコンテキストにトランザクションが存在しないため、新しいトランザクションが開かれる。メソッドBが実行されると、メソッドBは現在のコンテキストにトランザクションがあることを見つけるので、現在のトランザクションAに追加される。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB();
    // do something
}
 
// The transaction property is SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}


プロパゲーションサポート

トランザクションが存在する場合、現在のトランザクションがサポートされます。トランザクションが存在しない場合、非トランザクションの実行.

methodBが単独で呼び出された場合、methodBメソッドは非トランザクティブに実行されます。methdA が呼び出されると、methodB は methodA のトランザクションに追加され、トランザクティ ブに実行される。

throw new IllegalTransactionStateException("Transaction propagation 'mandatory' but no existing transaction found")

伝播義務

トランザクションがすでに存在する場合、現在のトランザクションをサポートします。アクティブなトランザクションが存在しない場合は例外をスローします。

メソッドBを単独で呼び出すと、現在アクティブなトランザクションが存在しないため、例外がスローされます。 @Transactional(propagation = Propagation.REQUIRED) public void methodA() { methodB(); // do something } @Transactional(propagation = Propagation.MANDATORY) public void methodB() { // do something }

メソッドAを呼び出すと、メソッドBがメソッドAのトランザクションに追加され、トランザクションとして実行されます。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    doSomeThingA();
    methodB();
    doSomeThingB();
    // do something else
}
 
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // do something
}


伝搬の必要性

PROPAGATION_REQUIRES_NEW を使用するには、トランザクションマネージャとして JtaTransactionManager を使用する必要があります。
新しいトランザクションが開かれます。すでにトランザクションが存在する場合は、まず既存のトランザクションをハングアップさせます。

以下のコードからわかるように、トランザクションBとトランザクションAは、互いに独立した2つのトランザクションです。トランザクションBの成功はトランザクションAに依存しない。もしmethodBのメソッドを呼び出した後にmethodAのdoSomeThingBメソッドが失敗しても、methodBが行ったことの結果はコミットされたままである。その代わり、methodB以外のコードによって引き起こされた結果はロールバックされる

public static void main(){
    TransactionManager tm = null;
    try{
        // get a JTA transaction manager
        tm = getTransactionManager();
        tm.begin();//open a new transaction
        Transaction ts1 = tm.getTransaction();
        doSomeThing();
        tm.suspend();//hang the current transaction
        try{
            tm.begin();//reopen the second transaction
            Transaction ts2 = tm.getTransaction();
            methodB();
            ts2.commit();//commit the second transaction
        } Catch(RunTimeException ex) {
            ts2.rollback();//rollback the second transaction
        } finally {
            //free resources
        }
        //resume the first transaction after methodB is executed
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//commit the first transaction
    } catch(RunTimeException ex) {
        ts1.rollback();//rollback the first transaction
    } finally {
        //free resources
    }
}


methodA()を呼び出すと、次のように等価になります。

@Transactional(propagation = Propagation.REQUIRED)
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}
 
@Transactional(propagation = Propagation.NEWSTED)
methodB(){
    // do something
}


プロパゲーション非対応

常に非トランザクションで実行され、既存のトランザクションをハングアップさせます。

PROPAGATION_NOT_SUPPORTED を使用する場合、トランザクションマネージャとして JtaTransactionManager も使用する必要があります。

プロパゲーション_ネバー

常に非トランザクションで実行し、アクティブなトランザクションがある場合は例外を投げる。

プロパゲーション・ネスト

アクティブなトランザクションが存在する場合、ネストされたトランザクションの中で実行されます。

アクティブなトランザクションがない場合、TransactionDefinition.PROPAGATION_REQUIRED プロパティに従って実行されます。

これはネストされたトランザクションであり、JDBC 3.0 ドライバを使用する場合、トランザクションマネージャとして DataSourceTransactionManager のみがサポートされています。これは、JDBC ドライバの java.sql.DataSourceTransactionManager を必要とします。PROPAGATION_NESTED では、PlatformTransactionManager の nestedTransactionAllowed プロパティを true に設定する必要もあります (プロパティ値のデフォルトは false です)。

main(){
    Connection con = null;
    Savepoint savepoint = null;
    try{
        con = getConnection();
        con.setAutoCommit(false);
        doSomeThingA();
        savepoint = con2.setSavepoint();
        try{
            methodB();
        } catch(RuntimeException ex) {
            con.rollback(savepoint);
        } finally {
            //free resources
        }
        doSomeThingB();
        con.commit();
    } catch(RuntimeException ex) {
        con.rollback();
    } finally {
        //free resources
    }
}


methodBメソッドを単独で呼び出すと、REQUIRED属性として実行されます。methodAメソッドが呼び出された場合は、それと同等になります。

TransactionDefinition
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;


setSavepointメソッドはmethodBメソッドが呼ばれる前に呼ばれ、現在の状態をsavepointに保存します。methodBメソッドの呼び出しが失敗すると、前に保存された状態に戻ります。

なお、この時点ではトランザクションはコミットされておらず、その後のコード(doSomeThingB()メソッド)の呼び出しが失敗すると、methodBメソッドを含むすべての操作がロールバックされる。ネストされたトランザクションの非常に重要な概念は、内側のトランザクションが外側のトランザクションに依存することです。外側のトランザクションが失敗すると、内側のトランザクションによって行われたアクションがロールバックされる。そして、内側のトランザクションの操作の失敗は、外側のトランザクションのロールバックを引き起こさない。

Springトランザクションの分離レベル

トランザクションの分離レベルの定義

<テーブル 絶縁レベル 説明 アイソレーションデフォルト これはPlatfromTransactionManagerのデフォルトの分離レベルであり、データベースのデフォルトのトランザクション分離レベルを使用します。他の4つはJDBCの分離レベルに対応しています。 アイソレーション_リード_アンコミット これはトランザクションの最も低い分離レベルであり、他のトランザクションがこのトランザクションからコミットされていないデータを見ることができるようにします。この分離レベルはダーティ・リード、イレギュラー・リード、ファントムリードを生成します。 アイソレーショ ン_リード_コミット あるトランザクションによって変更されたデータが、コミットされた後にのみ、別のトランザクションによって読み取られることを保証します。別のトランザクションは、そのトランザクションによってコミットされていないデータを読み取ることはできません。アイソレーショ ン_リピータブル_リード アイソレーション・シリアライザブル これは最もコストがかかるが信頼性の高いトランザクション分離のレベルです。トランザクションは逐次実行として扱われます。

Springトランザクション基本設定サンプル