日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

MySQL事务隔离级别和Spring事务关系介绍

發布時間:2024/1/17 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL事务隔离级别和Spring事务关系介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

事務隔離級別介紹

隔離級別臟讀不可重復讀幻讀
未提交讀(Read uncommitted)可能可能可能
已提交讀(Read committed)不可能可能可能
可重復讀(Repeatable read)不可能不可能可能
可串行化(Serializable )不可能不可能不可能
  • 未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據
  • 提交讀(Read Committed):只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別 (不重復讀)
  • 可重復讀(Repeated Read):可重復讀。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標準中,該隔離級別消除了不可重復讀,但是還存在幻象讀,但是innoDB解決了幻讀
  • 串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞
  • 接下來一次來驗證每個隔離級別的特性,首先我們先建一張表,我們建立賬戶表account用來測試我們的事務隔離級別:

    1 2 3 4 5 6 7 CREATE TABLE account (`id` int(11) NOT NULL AUTO_INCREMENT,`customer_name` varchar(255) NOT NULL,`money` int(11) NOT NULL,PRIMARY KEY (`id`),UNIQUE `uniq_name` USING BTREE (customer_name) ) ENGINE=`InnoDB` AUTO_INCREMENT=10 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRIT

    RU (read uncommitted)讀未提交隔離級別

    首先我們開啟Console A,然后設置session事務隔離級別為read uncommitted; 然后同樣開啟Console B,設置成read uncommitted;

    1 2 3 4 5 6 7 8 9 10 mysql> set session transaction isolation level read uncommitted; Query OK, 0 rows affected (0.03 sec)mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 rows in set (0.03 sec)

    我們兩個console的事務隔離級別都是read uncommitted,下面測試RU級別會發生的情況

    小結:

    可以發現RU模式下,一個事務可以讀取到另一個未提交(commit)的數據,導致了臟讀。如果B事務回滾了,就會造成數據的不一致。RU是事務隔離級別最低的。

    RC (read committed)讀提交隔離級別

    現在我們將事務隔離級別設置成RC (read committed)

    1 set session transaction isolation level read uncommitted;


    小結

    我們在RC模式下,可以發現。在console B沒有提交數據修改的commit的時候,console A是讀不到修改后的數據的,這就避免了在RU模式中的臟讀,但是有一個問題我們會發現,在console A同一個事務中。兩次select的數據不一樣,這就存在了不可重復讀的問題.PS:RC事務隔離級別是Oracle數據庫的默認隔離級別.

    RR (Repeatable read)可重復讀隔離級別


    小結:

    在RR級別中,我們解決了不可重復讀的問題,即在這種隔離級別下,在一個事務中我們能夠保證能夠獲取到一樣的數據(即使已經有其他事務修改了我們的數據)。但是無法避免幻讀,幻讀簡單的解釋就是在數據有新增的時候,也無法保證兩次得到的數據不一致,但是不同數據庫對不同的RR級別有不同的實現,有時候或加上間隙鎖來避免幻讀。

    innoDB 解決了幻讀

    前面的定義中RR級別是可能產生幻讀,這是在傳統的RR級別定義中會出現的。但是在innoDB引擎中利用MVCC多版本并發控制解決了這個問題


    這算是幻讀嗎?在標準的RR隔離級別定義中是無法解決幻讀問題的,比如我要保證可重復讀,那么我們可以在我們的結果集的范圍加一個鎖(between 1 and 11),防止數據更改.但是我們畢竟不是鎖住真個表,所以insert數據我們并不能保證他不插入。所以是有幻讀的問題存在的。但是innodb引擎解決了幻讀的問題,基于MVCC(多版本并發控制):在InnoDB中,會在每行數據后添加兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行數據何時被創建,另外一個記錄這行數據何時過期(或者被刪除)。 在實際操作中,存儲的并不是時間,而是事務的版本號,每開啟一個新事務,事務的版本號就會遞增。所以當我們執行update的時候,當前事務的版本號已經更新了?所以也算是幻讀??(存疑)主要是gap間隙鎖+MVCC解決幻讀問題?

    串行化隔離級別:

    所有事物串行,最高隔離級別,性能最差

    存在的問題?

    在RR模型,我們雖然避免了幻讀,但是存在一個問題,我們得到的數據不是數據中實時的數據,如果是對實時數據比較敏感的業務,這是不現實的。
    對于這種讀取歷史數據的方式,我們叫它快照讀 (snapshot read),而讀取數據庫當前版本數據的方式,叫當前讀 (current read)。很顯然,在MVCC中:

    • 快照讀:就是select
      • select * from table ….;
    • 當前讀:特殊的讀操作,插入/更新/刪除操作,屬于當前讀,處理的都是當前的數據,需要加鎖。
      • select * from table where ? lock in share mode;
      • select * from table where ? for update;
      • insert;
      • update ;
      • delete;

    事務的隔離級別實際上都是定義了當前讀的級別,MySQL為了減少鎖處理(包括等待其它鎖)的時間,提升并發能力,引入了快照讀的概念,使得select不用加鎖。而update、insert這些“當前讀”,就需要另外的模塊來解決了。

    比如,我們有以下的訂單業務場景,我們隊一個商品下單的操作,我們得首先檢查這個訂單的數量還剩多少,然后下單。

    事務1:

    1 2 select num from t_goods where id=1; update t_goods set num=num-$mynum where id=1;

    事務2:

    1 2 select num from t_goods where id=1; update t_goods set num=num-$mynum where id=1;

    假設這個時候數量只有1,我們下單也是只有1.如果在并發的情況下,事務1查詢到還有一單準備下單,但是這個時候事務2已經提交了。訂單變成0.這個事務1在執行update,就會造成事故。

  • 解決問題方法1(悲觀鎖):就是利用for update對著個商品加鎖,事務完成之后釋放鎖。切記where條件的有索引,否則會鎖全表。
  • 解決方法2(樂觀鎖):給數據庫表加上個version字段。然后SQL改寫:
  • 1 2 select num,version from t_goods where id=1; update t_goods set num=num-1,version=verison+1 where id=1 and version=${version}

    Spring管理事務的方式。

    編程式事務

    編程式事務就是利用手動代碼編寫事務相關的業務邏輯,這種方式比較復雜、啰嗦,但是更加靈活可控制(個人比較喜歡)

    1 2 3 4 5 6 7 8 9 10 11 12 13 public void testTransactionTemplate() {TransactionTemplate transactionTemplate = new TransactionTemplate(txManager); transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); //設置事務隔離級別transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//設置為required傳播級別....transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { //事務塊jdbcTemplate.update(INSERT_SQL, "test"); }}); }

    聲明式事務

  • 為了避免我們每次都手動寫代碼,利用Spring 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:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <!--設置所有匹配的方法,然后設置傳播級別和事務隔離--><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 <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() {@Overrideprotected 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的邏輯,代碼執行的邏輯如下:

  • 在調用userService對象的insert方法時,此方法用的是Required傳播行為且此時Spring事務管理器發現還沒開啟邏輯事務,因此Spring管理器覺得開啟邏輯事務
  • 在此邏輯事務中調用了accountService對象的insert方法,而在insert方法中發現同樣用的是Required傳播行為,因此直接使用該已經存在的邏輯事務;
  • 返回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的區別:

  • RequiresNew每次都創建新的獨立的物理事務,而Nested只有一個物理事務;
  • Nested嵌套事務回滾或提交不會導致外部事務回滾或提交,但外部事務回滾將導致嵌套事務回滾,而 RequiresNew由于都是全新的事務,所以之間是無關聯的;
  • Nested使用JDBC 3的保存點(save point)實現,即如果使用低版本驅動將導致不支持嵌套事務。

    使用嵌套事務,必須確保具體事務管理器實現的nestedTransactionAllowed屬性為true,否則不支持嵌套事務,如DataSourceTransactionManager默認支持,而HibernateTransactionManager默認不支持,需要設置來開啟。

  • 總結

    以上是生活随笔為你收集整理的MySQL事务隔离级别和Spring事务关系介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。