1. 程式人生 > >資料庫 資料丟失問題 及解決方案

資料庫 資料丟失問題 及解決方案

什麼是資料丟失

兩個執行緒基於同一個查詢結果進行修改,後修改的人會將先修改人的修改覆蓋掉.
讓我們先來看這麼個小案例:
我們給遊戲充值100,支付成功後,銀行會向遊戲伺服器傳送支付成功資訊,有一個訂單支付資訊表(order)和一個賬戶資訊表(account),首先要去order表中查詢該訂單支付狀態state(select state from order where id=1)如果是0未支付,接下來要做的就是更新state為已支付,並且向你的賬戶account中加上100. 但是網路是有延遲的銀行為了確保資訊能送達可能會再次向伺服器傳送支付成功資訊。這時第二次請求也是先去查詢state,若此次請求和第一次正好是併發執行,他查詢到的state也是0,所以也會給你的account加100塊錢。也就是後來的修改覆蓋掉了前邊的修改,這就是資料丟失。
這裡寫圖片描述


操作流程如下:
這裡寫圖片描述

怎麼防止資料丟失

先來看一下資料庫中的鎖機制:
共享鎖:在非Serializable隔離級別做查詢不加任何鎖,而在Serializable隔離級別下做的查詢加共享鎖,
共享鎖的特點:共享鎖和共享鎖可以共存,但是共享鎖和排他鎖不能共存
排他鎖:在所有隔離級別下進行增刪改的操作都會加排他鎖,
排他鎖的特點:和任意其他鎖都不能共存

悲觀鎖:

悲觀鎖悲觀鎖悲觀的認為每一次操作都會造成更新丟失問題,在每次查詢時就加上排他鎖。
select state from order for update


這樣第二個請求在查詢時發現已經有個排他鎖,就在那等著,等第一個事務操作完成後才輪到他,不過此時state已經變成1已支付,這樣就避免再次加100塊。

樂觀鎖:

樂觀鎖會樂觀的認為每次查詢都不會造成更新丟失.利用一個版本欄位進行控制。
為order表加一個欄位version
第一次請求查詢出未支付後,在加100塊錢之前,將更新state操作修改為:
update order set state=1 and version =version+1 where id=1 and version=0
這樣第二個請求也執行這句是version就不是0了,執行失敗。從而也防止了第一次資料的丟失。

兩種方案對比

  • 查詢非常多,修改非常少,使用樂觀鎖(悲觀查詢都加排他鎖 效率會降低)
  • 修改非常多,查詢非常少,使用悲觀鎖(多管理員修改時,某種一個可能一直等待,所以用悲觀)