搜尋

首頁  >  問答  >  主體

java - mysql事务隔离的问题?多线程并发怎么保证可靠?

mysql采用默认事务隔离级别REPEATABLE-READ;
然后我分别执行以下内容:
1、


2、使用jdbc访问数据库:

3、这时候打印结果,发现线程进入阻塞,一直卡在这里

也就是说我开启的另一个事务仍然能读取到数据,只是在最后执行executeUpdate的时候才被锁定不能执行
4、命令行执行commit


5、结果命令行操作被jdbc操作替换掉,数据改为4。

这不就是说REPEATABLE-READ仍然出现了丢失更新,而且没有行锁定吗?
事务与并发到底是什么区别,我在测试多线程执行以上jdbc操作时,发现最后得到的结果总是不对。例我开十个线程,每个都对数据+1,结果可能只加了5.所以在并发情况下怎么做才能保证数据的安全?

高洛峰高洛峰2887 天前551

全部回覆(5)我來回復

  • PHPz

    PHPz2017-04-17 17:57:17

    如果只是增一的話,number的數值不透過select獲取,select獲取的可能是舊值;然後換成update test set number = number + 1 where id = ?。這裡的number會是最新的,這種方式可以避免select for update的完全串列的效能遺失。

    回覆
    0
  • 怪我咯

    怪我咯2017-04-17 17:57:17

    REPEATABLE-READ是有可能出現幻讀的
    如果你要確保絕對的安全只能把隔離等級設定成SERIALIZABLE
    這樣所有事務都只能順序執行,自然不會因為並發有什麼影響了,但是效能會下降許多。

    如果你既不想效能下降又想控制讓他不出錯,現在比較常用的做法是,使用更新的版本控制。
    維護一個欄位作為UpdateVersion,修改時updateversion也作為一個參數傳入,在條件語句中加入例如where id=? and update_version = ? 當然set裡面要update_version+1。
    這樣可以控製到每次只能有一個人更新一個版本。

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-17 17:57:17

    透過試驗,發現某種程度上已經理解了自己的問題。

    我之前被關於事務隔離等級錯誤訊息誤導,認為REPEATABLE-READ等級和SERIALIZABLE等級能解決遺失更新的問題,然而實際上是不能的。
    因為mysql的select ... from table;語句 不管哪一種隔離等級都是不會被阻塞的,

    事务A读取 id为1 number = 3 ,执行更新操作 number设为1;
    事务B读取 id为1 number = 3, 执行+1操作  更新--进入阻塞

    在隔離等級下只有在更新資料時才會等待寫鎖(排它鎖)的釋放,所以我多個執行緒可以同時讀取到number=3,基於此進行修改就必然會導致遺失更新。

    所以解決方法為程式中加入悲觀鎖 或 樂觀鎖 機制。
    悲觀鎖採用的是 select ... for update,

    事务A:  select ... for update,执行更新操作,
    事务B:  select ... for update --阻塞

    這時事務B讀取操作將無法執行,只有當事務A的commit完成,事務B才可以繼續執行,相當於採用串行一個個執行。

    樂觀鎖常用的是版本控製或時間戳控制,

    事务A:执行 此时版本为 1;执行完update ...set version=2 where version = 1;
    事务B:执行 此时版本为 1;执行完update ...set version=2 where version = 1;

    這時候事務B發現version=1已經不存在了,因為事務A先執行完更新了資料庫,把version字段設為了2,這樣就會導致該事務提交失敗,需要我們在程序中判斷異常情況下如何繼續操作。

    以上就是我目前的理解,至於mysql本身支援的MVCC(多版本並發控制)還不知道怎麼用,樂觀鎖的異常處理也不太清楚具體要怎麼實現,可能是要根據具體的業務場景採用不同的處理手段吧。

    回覆
    0
  • 怪我咯

    怪我咯2017-04-17 17:57:17

    如果是單純的Select会被看做是单纯的查询操作,所以不会被挂起。
    如果要对某行进行事务,应该先在Select时就使用Select ... For Update这种格式。
    使用For Update会对所选择的行加锁,当另外的事务再进行时,会在那个事务的Select ... For Update時就掛起,等待當前事務完成後才會繼續執行。這樣才能確保查詢和寫入之間沒有中間者。

    回覆
    0
  • 巴扎黑

    巴扎黑2017-04-17 17:57:17

    透過鎖來控制。

    回覆
    0
  • 取消回覆