數據庫並發導致的臟讀 不可重復讀 幻讀的處理方法


修正

2020-04-23 之前是剛參加工作的理解 很多問題,現在回來重新梳理

事物的特性

原子性

表示一個最小的邏輯單元,要么都執行 要么都不執行

一致性

事物處理前與處理后的狀態的要是一致的(a賬戶有200元 b賬戶有300元   共計500元  a賬戶給b賬戶轉賬100元。事物處理后2個賬戶總額也為500元)

隔離性

每個事物都有自己的數據空間,使事物的處理結果不會被別的事物所影響

持久性

事物提交 數據就永久的保存下來了

事物的隔離級別

Read uncommitted(讀未提交)

會導致臟讀,我們舉例一個下單場景:

                                              1.商品編號:a1的庫存是10。a用戶下單購買了10個商品,庫存變成了10

                            2.a用戶因為地址拍錯執行退貨操作:退貨分為 1.開啟事物 2.訂單狀態改為取消,3.庫存還原 4.執行其他業務邏輯 5.提交事物

                                              3.在執行 2.4的其他業務邏輯的時候 b.用戶發現庫存有10 執行購買 並提交事物,后續a用戶退貨操作因為某些操作失敗 事物回滾,導致超賣

扣除庫存代碼

public  boolean  reduceStock(Long id,Long purchaseCount){
        ProductStock productStock=ProductStockDao.findById(id);
        if(productStock.getCount()<purchaseCount){
            throw new BusinessException(500,"庫存不足");
        }
        //庫存充足
        productStock.setCount(productStock.getCount()-purchaseCount);
        ProductStockDao.update(productStock);
        return true;
    }

 

Read committed  讀取已經提交的

sqlserver默認的隔離級別,因為當前事物只能讀取其他數據已經提交的數據,所以我們上面例子 第3步會校驗庫存不足  

其實這里並發還是會超賣

比如10個用戶都下單10  並發都同時走到 if(productStock.getCount()<purchaseCount){ 這個時候庫存都是10 判斷庫存充足下單成功

建議通過加鎖,或者sql控制如:

public  boolean  reduceStock(Long id,Long purchaseCount){
       int updated=  productStockDao.execute("update product_stock set count=count-:purchaseCount WGERE count>=0 and id=:id",purchaseCount,id);
       return updated>0;
    }

版本號樂觀鎖

   public  boolean  reduceStock(Long id,Long purchaseCount){
        ProductStock productStock=ProductStockDao.findById(id);
        String newVersion=productStock.getVersion()+1;
        int updated=  productStockDao.execute("update product_stock set count=count-:purchaseCount,version:newVersion where id=:id and version=:version",purchaseCount,newVersion,id,productStock.getVersion());
        return true;
    }

第一種適合數值的扣除,第二種適合數據修改(體驗不是很好)

此隔離級別,可以解決我們的臟讀但是會出現不可重復讀

如:

    1.查詢用戶性別

    2.如果性別是null 則update set為男

    3.再次查詢並返回用戶性別

    在第三這個步驟時,用戶性別被其他用戶改成了女  返回女

Repeatable read  可重復讀

可以解決重復讀問題,因為這個隔離級別讀取的是快照,同時update的時候會加x 事物沒提交前別的事物不能修改 但是會出現幻讀

如:

   查詢a用戶的消費總額

   select sum(money) from pay_log where userId=a  第二次查詢的時候 log表又增加了一條數據,導致幻讀

   注:網上都是這樣說 我的理解因為有gap鎖其他事物也不能添加 我覺得這個隔離級別也可以一定程度避免幻讀

Serializable 序列化

這個隔離級別最高 能夠避免 臟讀 不可重復讀 幻影讀  但是效率低(當進行當前讀的時候 是鎖表 性能差)

 

解決可重復讀和幻讀 都是通過加鎖來解決的具體可看:《mysql deadlock、Lock wait timeout解決和分析》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM