MySQLのトランザクション分離レベルのトランザクションの関係と春の紹介(転載)

転送:https://blog.csdn.net/hellozhxy/article/details/81081187

非常に良い記事、学びます

はじめにトランザクション分離レベル

分離レベル ダーティー読み取り 非反復可能読み取り マジック読書
非コミット読み取り(非コミット読み取り) 可能 可能 可能
READ COMMITTED(読み取りコミット) 不可能 可能 可能
反復可能読み取り(反復可能読み取り) 不可能 不可能 可能
シリアライズ(直列化) 不可能 不可能 不可能
  1. データ汚れは、他のセッションの変更からコミットされていないトランザクションを読みすることが可能であることを、許可されている読み:非コミット読み取り(READ UNCOMMITTED)
  2. コミット読み取り(読み取りコミット):あなたは、すでに提出されたデータを読み取ることができます。ほとんどのOracleや他のデータベースがデフォルトレベルである(ない反復可能読み取り)
  3. 反復可能読み取り(Readを繰り返し):読み取り、反復。トランザクションの同じトランザクションクエリ開始時間内では、InnoDBのデフォルトレベル一致しています。SQL標準では、分離レベルは非反復可能読み取りを排除し、そこファントムが読んだが、InnoDBはファントム読み取りの解決します
  4. シリアルリード(直列化)は:完全読み取りシリアライズ、各読み取りは、読み取りと書き込みがお互いをブロックする、共有テーブル・レベルのロックを取得する必要があります

最初にすべてのLETのビルドの各分離レベルの特性を検証する次回は、テーブルには、我々は我々のテーブルのアカウントトランザクション分離レベルをテストするための口座を開設しています。

1
2
3
4
5
6
7
(TABLEアカウントを作成します
    `id`のint(11)NOT NULL AUTO_INCREMENT、
    `customer_name`のVARCHAR(255)NOT NULL、
    `money`はint(11)NOT NULL、
    PRIMARY KEY( `id`)、
    BTREEを使用したUNIQUE `uniq_name`(CUSTOMER_NAME)
)ENGINE = `InnoDB` AUTO_INCREMENT = 10デフォルトの文字セットUTF8 COLLATE utf8_general_ci ROW_FORMAT = COMPACT CHECKSUM = 0 DELAY_KEY_WRIT

RUは、(非コミット読み取り)コミットされていない分離レベルを読み出します

まず、我々はコンソールAを開き、READ UNCOMMITTEDにセッションのトランザクション分離レベルを設定します。READ UNCOMMITTEDに設定し、同じオープンコンソールB;

1
2
3
4
5
6
7
8
9
10
MySQLの>セットセッションのトランザクション分離レベルは、コミットされていない読んで。
クエリOK、影響を受けた0行(0.03秒)
 
MySQLの> @@ session.tx_isolationを選択します。
+ ------------------------ +
| @@ session.tx_isolation |
+ ------------------------ +
| READ-UNCOMMITTED |
+ ------------------------ +
セット内の1行(0.03秒)

我々の2つのコンソールのトランザクション分離レベルは、コミットされていない読み込まれ、次のテストではレベルRUが起きます

要約:

モードは、ダーティ・リードをもたらす、RU、(コミット)別のコミットされていないデータへのリードトランザクションに見出すことができます。Bのトランザクションがロールバックされた場合は、データの不整合が発生します。RUは、トランザクション分離の最低レベルです。

RC(コミット読み取り)COMMITTED分離レベルをREAD

今、私たちは、トランザクション分離レベルは、(コミット読み取り)RCに設定されています

1
セットセッションのトランザクション分離レベルは、コミットされていない読んで。


概要

我々RCモードでは、見つけることができます。改訂された提出データがコンソールBにコミットしないがある場合、読み取りが少ないRUモードで読ま汚れを回避し、変更されたデータ、よりコンソールが、私たちはコンソールAで同じトランザクションを見つける問題がありますインチ 2件の選択データが同じではありませんが、繰り返すことができないという問題があった.PSを読む:RCトランザクション分離レベルは、デフォルトの分離レベルOracleデータベースです。

RR(反復可能読み取り)レベル反復可能読み取り分離


要約:

RRレベルでは、我々は(あなたが私たちのデータを変更するその他の事項を持っている場合でも)同じデータを取得することができるように保証することができ、そのトランザクションで、この分離レベルであること、非反復可能読み取りの問題に対処します。しかし、ファントム読み取り避けることができない、時々プラスまたはギャップロック、ファントムは、保証の一貫性のないデータを取得するために二回は存在しない、簡単な説明が時間内に新しいデータがあるということです読みますが、別のデータベースには、RRの異なるレベルに対して異なる実装を持っています避けるために、ファントムを読み込みます。

InnoDBはファントム読み取りの解決します

RRレベルの以前の定義は、これはRRレベルの従来の定義が表示されますされ、農産物ファントムリードする可能性があります。しかし、マルチバージョン同時実行制御MVCCの使用は、InnoDBエンジンでこの問題を解決し


ファントムはそれを読むことですか?標準RR分離レベルの定義では、ファントムは、我々はデータの変更を防止し、私たちの結果セットの範囲内で(1と11の間の)ロックを追加することができますので、たとえば、私は、反復可能読み取りを保証したい、問題を読みました。しかし、我々は解決できませんすべての後に、実際に、テーブルをロックし、我々は彼が挿入されていないことを保証することはできませんので、データを挿入しません。だから、ファントム読み取りの問題の存在があります。しかし、InnoDBエンジンはMVCC(マルチバージョン同時実行制御)に基づいて、ファントムリードの問題を解決:ではInnoDBを、これら2つの値、MVCCを達成するために、データレコードの行をデータの各行の後に二つの追加の隠し値を追加しますこの行が期限切れ(または削除)するときには、データレコードに加えて、作成されたとき。実際には、ストレージは時間ではなく、トランザクションのバージョン番号、それぞれが新しいトランザクションを開いて、トランザクションのバージョン番号がインクリメントされます。私たちは、更新を実行する際に、現在のトランザクションのバージョン番号が更新されましたか?それはファントム読み取りと考えることができそう?(疑わしい)主にギャップギャップファントム読み込みロック+ MVCCは、問題を解決しますか?

シリアル分離レベル:

シリアルすべて、分離の最高レベル、最悪のパフォーマンス

問題?

RRモデルでは、我々はファントムを避けるものの読みますが、問題がある、それは非現実的であるリアルタイムデータサービスに敏感であるならば、我々は、リアルタイムデータのデータの代わりに、データを取得します。
これは過去のデータの道を読んで、私たちは現在の読み取り(現在の読み取り)と呼ばれる、読書スナップショット(スナップショットの読み取り)を呼び出し、そして道にデータベース内のデータの現在のバージョンをお読みください。これは、そのMVCC中で明確です:

  • スナップショットを読む:選択しています
    • テーブルSELECT * FROM ...;
  • 現在の読書:特別には、現在の読書に属し、挿入/更新/削除操作を読んで、現在のデータが処理され、ロックする必要があります。
    • どこのテーブルから選択*?共有モードでロック。
    • どこのテーブルから選択*?更新のための;
    • インサート;
    • 更新;
    • 削除します。

トランザクション分離レベルは、実際に現在の読書のレベルを定義し、MySQLは、時間(別のロ​​ックを待っているを含む)、ロック処理を減らすこと並行性を高めるために、そのロックされて選択しないでください、スナップショット読書の概念を導入しました。「現在の読書」の挿入、更新は、解決するために追加のモジュールが必要です。

たとえば、我々は次の順序のビジネスシナリオ、当社の営業チームの単一商品を持って、我々は注文数が出て、その後、次のシングルどのくらいの最初のチェックを持っています。

トランザクション1:

1
2
ID = 1 t_goodsからNUMを選択します。
更新t_goodsが設定NUM = num- $ mynumどこのid = 1;

トランザクション2:

1
2
ID = 1 t_goodsからNUMを選択します。
更新t_goodsが設定NUM = num- $ mynumどこのid = 1;

同時トランザクション1の場合にはそこに次のシングルのために準備するための単一のクエリであるが、今回は、トランザクション2が送信されている場合は、この数一度だけ、私たちの次のシングルが唯一の1であると仮定すると。ご注文は、このトランザクションの実行アップデートで0.1になり、それが事故の原因となります。

  1. ロック解除が完了した後に商品取引に対する更新ロックのための使用である:問題の1つの溶液(悲観的ロック)。条件のインデックスがある場合、それ以外の場合は、テーブル全体をロックすることを忘れないでください。
  2. 解決策2(楽観的ロック):データベーステーブルは、バージョンフィールドを追加します。その後、SQLを書き換え:
1
2
NUM、ID = 1 t_goodsからバージョンを選択します。
NUM = NUM​​-1セット更新t_goods、バージョン= verison + 1、ID = 1、バージョン= $ {バージョン}

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

処理された一連の取引のための春

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

時間だけ1つのトランザクションデータ処理の統一を確保するために、複数のトランザクションでデータをロックして処理すると、異なったロックの異なる状況があります

DEFAULT(デフォルト):使用基礎となるデータベースのトランザクション分離レベルは、MySQLの使用現在の分離レベルを照会することができ@@ tx_isolationを選択

READ_COMMITTED:読むダーティリード防ぐことができ、すでに提出されたデータを、読んで、コミットが、非反復可能読み取りとファントム読み取り

READ_UNCOMMITTED:非コミット読み取り、データが少ないと、提出され、読むことができません

REPEATABLE_READ:反復は自動ロックを読み出した後、他のトランザクションが汚れに対処するために変更することができない、読んで読み込み、非反復可能読み取り

SERIALIZABLE:シリアライゼーション、トランザクション実行の行、次の完了後にトランザクションの実行

トランザクション伝播メカニズム

サービスでのデータベース操作は、どのように二つの操作の業務を管理します

REQUIRED(デフォルト):トランザクションが現在開いて取引されていない場合、任意のトランザクションは自動的に追加されます

REQUIRES_NEWは:呼び出し側のために、関係なく、トランザクションがあるかどうかの呼び出し側は、呼び出し側は新たなビジネスを作成します。

MANDATORY:現在のトランザクションを使用して、トランザクションなした場合、例外をスローします。

NESTED:トランザクションは、ネストされたトランザクションがない場合は、新しいがある場合に実行しました

サポートされています。現在のトランザクションのサポート、何のトランザクションが非トランザクションの方法で実行されていない場合。

NOT_SUPPORTEDは:非トランザクションウェイに操作を実行し、現在のトランザクションが存在する場合は、保留中の現在のトランザクションを置きます。

現在のトランザクションが存在する場合は、非トランザクションの方法を実行するために、例外がスローされません:NEVER。

春のファッションの管理サービス。

プログラムによるトランザクション

プログラムによるトランザクションは、このアプローチは、より複雑であり、長ったらしいが、より柔軟で(個人的な好み)を制御することができ、ビジネスロジックに関連したマニュアルコーディング事務の使用であり、

1
2
3
4
5
6
7
8
9
10
11
12
13
ます。public void testTransactionTemplate(){
 
  TransactionTemplate transactionTemplate =新しいTransactionTemplate(txManager)。 
  transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); //設定するトランザクション分離レベル
  transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);必要なレベルの広がりに//セット
  ....
  transactionTemplate.execute(新しいTransactionCallbackWithoutResult(){ 
      @オーバーライド 
      保護された無効doInTransactionWithoutResult(TransactionStatus状態){//事务块
         jdbcTemplate.update(INSERT_SQL、 "テスト")。 
  }})。 
  
}

宣言的トランザクション

  1. 手動で私たちは、各メソッドのプロキシを中心に春AOPの方法を使用してコードを書くたびに回避するために、使用XML構成は、コードを書く避けるために。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<TX:アドバイスID = "txAdvice" トランザクション・マネージャー= "txManager"> 
<送信:属性> <! - すべてのマッチング方法のための設定をした後、トランザクション分離レベルと広がりを設定 - >
           <tx:method name="save*" propagation="REQUIRED" /> 
           <tx:method name="add*" propagation="REQUIRED" /> 
           <tx:method name="create*" propagation="REQUIRED" /> 
           <tx:method name="insert*" propagation="REQUIRED" /> 
           <tx:method name="update*" propagation="REQUIRED" /> 
           <tx:method name="merge*" propagation="REQUIRED" /> 
           <tx:method name="del*" propagation="REQUIRED" /> 
           <tx:method name="remove*" propagation="REQUIRED" /> 
           <tx:method name="put*" propagation="REQUIRED" /> 
           <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> 
           <tx:method name="count*" propagation="SUPPORTS" read-only="true" /> 
          <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> 
          <tx:method name="list*" propagation="SUPPORTS" read-only="true" /> 
          <tx:method name="*" propagation="SUPPORTS" read-only="true" /> 
       </tx:attributes> 
</tx:advice> 
<aop:config> 
       <aop:pointcut id="txPointcut" expression="execution(* org.transaction..service.*.*(..))" /> 
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" /> 
</aop:config>
  1. 同时也可以用注解的方式
1
<tx:annotation-driven transaction-manager="transactioManager" /><!--开启注解的方式-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
   @AliasFor("transactionManager")
   String value() default "";
   @AliasFor("value")
   String transactionManager() default "";
   Propagation propagation() default Propagation.REQUIRED;//传播级别
  
   Isolation isolation() default Isolation.DEFAULT;//事务隔离级别
   int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;//事务超时时间
   boolean readOnly() default false;//只读事务
   Class<? extends Throwable>[] rollbackFor() default {};//抛出哪些异常 会执行回滚
   String[] rollbackForClassName() default {};
   Class<? extends Throwable>[] noRollbackFor() default {};
   String[] noRollbackForClassName() default {};//不回滚的异常名称
 
}
//transaction注解可以放在方法上或者类上

我们在这里不对两种事务编程做过多的讲解

Spring事务传播:

事务传播行为:

Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,Spring共支持7种传播行为
为了演示事务传播行为,我们新建一张用户表

1
2
3
4
5
6
EATE TABLE user (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(255) NOT NULL,
    `pwd` varchar(255) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=`InnoDB` AUTO_INCREMENT=10 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0;

Required:

必须有逻辑事务,否则新建一个事务,使用PROPAGATION_REQUIRED指定,表示如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务,如下图所示;

测试的代码如下,在account插入的地方主动回滚

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public int insertAccount(final String customer, final int money) {
    transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//设置为required传播级别
   int re= transactionTemplate.execute(new TransactionCallback<Integer>() {
        public Integer doInTransaction( TransactionStatus status) {
            int i = accountDao.insertAccount(customer, money);
            status.setRollbackOnly();//主动回滚
            return i;
        }
    });
    return re;
}
 public int inertUser(final String username, final String password) {
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//设置为required传播级别
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                int i = userDao.inertUser(username, password);
                int hahha = accountService.insertAccount("hahha", 2222);
//                status.setRollbackOnly();
                System.out.println("user==="+i);
                System.out.println("account===="+hahha);
            }
        });
        return 0;
    }

按照required的逻辑,代码执行的逻辑如下:

  1. 在调用userService对象的insert方法时,此方法用的是Required传播行为且此时Spring事务管理器发现还没开启逻辑事务,因此Spring管理器觉得开启逻辑事务
  2. 在此逻辑事务中调用了accountService对象的insert方法,而在insert方法中发现同样用的是Required传播行为,因此直接使用该已经存在的逻辑事务;
  3. 返回userService,执行完并关闭事务

所以在这种情况下,两个事务属于同一个事务,一个回滚则两个任务都回滚。

RequiresNew:

创建新的逻辑事务,使用PROPAGATION_REQUIRES_NEW指定,表示每次都创建新的逻辑事务(物理事务也是不同的)如下图所示:

Supports:

支持当前事务,使用PROPAGATION_SUPPORTS指定,指如果当前存在逻辑事务,就加入到该逻辑事务,如果当前没有逻辑事务,就以非事务方式执行,如下图所示:

NotSupported:

不支持事务,如果当前存在事务则暂停该事务,使用PROPAGATION_NOT_SUPPORTED指定,即以非事务方式执行,如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行。

Mandatory:

必须有事务,否则抛出异常,使用PROPAGATION_MANDATORY指定,使用当前事务执行,如果当前没有事务,则抛出异常(IllegalTransactionStateException)。当运行在存在逻辑事务中则以当前事务运行,如果没有运行在事务中,则抛出异常

Never

不支持事务,如果当前存在是事务则抛出异常,使用PROPAGATION_NEVER指定,即以非事务方式执行,如果当前存在事务,则抛出异常(IllegalTransactionStateException)

Nested:

嵌套事务支持,使用PROPAGATION_NESTED指定,如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚。

Nested和RequiresNew的区别:

  1. RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;
  2. Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而 RequiresNew由于都是全新的事务,所以之间是无关联的;
  3. Nested使用JDBC 3的保存点(save point)实现,即如果使用低版本驱动将导致不支持嵌套事务。

    使用嵌套事务,必须确保具体事务管理器实现的nestedTransactionAllowed属性为true,否则不支持嵌套事务,如DataSourceTransactionManager默认支持,而HibernateTransactionManager默认不支持,需要设置来开启。

おすすめ

転載: www.cnblogs.com/libin2015/p/12556163.html