數據庫特性、隔離機制、各種鎖

數據庫事務ACID四個特性

  • 原子性(Atomic)

    • 事務中的多個操作,不可分割,要麼都成功,要麼都失敗; All or Nothing.
  • 一致性(Consistency)

    • 事務操作之後, 數據庫所處的狀態和業務規則是一致的; 比如a,b賬戶相互轉賬之後,總金額不變;
  • 隔離性(Isolation)

    • 多個事務之間就像是串行執行一樣,不相互影響;
  • 持久性(Durability)

    • 事務提交後被持久化到永久存儲。

髒讀(Dirty Read)

  • 髒讀意味着一個事務讀取了另一個事務未提交的數據,而這個數據是有可能回滾的
  • 例如:app中的充值,充值事務進行的時候,日誌事務報錯回滾,錢已經回退了,但是代幣還是看到了。

不可重複讀(Unrepeatable Read)

  • 不可重複讀意味着,在數據庫訪問中,一個事務範圍內不同位置的兩個相同的查詢卻返回了不同數據。
  • 例如:事務B中對某個查詢執行兩次,當第一次執行完時,事務A對其數據進行了修改。事務B中第二次查詢數據發生了改變
  • 因爲其他事務update了相關記錄
  • 如果要防止不可重複讀,只需要把相關數據鎖住即可,別人就無法update了

幻讀(phantom read)

  • 同一個事務中多次執行同一個select, 讀取到的數據行發生改變。也就是行數減少或者增加了(被其它事務delete/insert並且提交)。
  • 例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,以後就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。
  • 如果防止幻讀,要鎖住滿足條件的記錄及所有這些記錄之間的gap,也就是需要 gap lock,比不可重複讀要複雜。

隔離機制

  • READ UNCOMMITTED

    • 可以讀取未提交的數據
    • RU 模式允許髒讀、幻讀、不可重複讀
  • READ COMMITTED

    • 只能讀取已經提交的數據
    • RC 允許幻讀和不可重複讀,不允許髒讀
  • REPEATABLE READ

    • 同一個事務中多次執行同一個select,讀取到的數據沒有發生改變;
    • RR 允許幻讀,不允許不可重複讀、髒讀
  • SERIALIZABLE:

    • 均不允許

數據庫對應隔離級別

  • Oracle 實現了RC 和 SERIALIZABLE隔離級別
    • 默認採用RC隔離級別,不允許髒讀,允許不可重複讀和幻讀。
    • SERIALIZABLE 均不允許
  • mysql
    • 默認採用RR隔離級別,SQL標準是要求RR解決不可重複讀的問題,但是因爲MySQL採用了gap lock,所以實際上MySQL的RR隔離級別也解決了幻讀的問題
    • MySQL的SERIALIZABLE採用了經典的實現方式,對讀和寫都加鎖。
  • 因爲使用mysql較多,這裏對mysql進行相關小總結
    • RC的併發效果好於RR,因爲RC在where條件後將不符合條件的行解鎖了
    • RC的select能讀取到所有已經提交的數據;RR的select只能讀取到第一次select之前提交的數據。
    • RC已經能滿足絕大部分的功能。

死鎖

  • 死鎖的發生與否,並不在於事務中有多少條SQL語句,死鎖的關鍵在於:兩個(或以上)的事務加鎖的順序不一致。(一個拿着id=1的鎖,處理id=5的delete或update,另一個拿着id=5的鎖,處理id=1的delete或update)
  • Select …forupdate語句是我們經常使用手工加鎖語句,如果使用of子句會對特定數據加鎖,否則會對錶加鎖
  • 所以在springmvc整合mybatis的時候有個事務處理機制,我一般會設置成處理完一個再處理另一個。

樂觀鎖

  • 一般用在讀很多的情況
  • 指樂觀的認爲,在自己操作的時候別人不會update或delete數據,是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制

悲觀鎖

  • 一般用在update或insert多的情況
  • 每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關係型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
  • eg:

    select * from user where name=”zp” for update

    這條 sql 語句鎖定了 user 表中所有符合檢索條件( name=”zp” )的記錄。 本次事務提交之前(事務提交時會釋放事務過程中的鎖),外界無法修改這些記錄。
  • mysql悲觀鎖案例
    • //我們可以使用命令設置MySQL爲非autocommit模式:
      set autocommit=0;
    • //0.開始事務
      begin;/begin work;/start transaction; (三者選一就可以)
      //1.查詢出商品信息
      select status from t_goods where id=1 for update;
      //2.根據商品信息生成訂單
      insert into t_orders (id,goods_id) values (null,1);
      //3.修改商品status爲2
      update t_goods set status=2;
      //4.提交事務
      commit;/commit work;
發佈了57 篇原創文章 · 獲贊 29 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章