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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何处理高并发情况下的DB插入

發布時間:2024/9/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何处理高并发情况下的DB插入 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載以便以后學習使用,謝謝!

插入數據庫,在大家開發過程中是很經常的事情,假設我們有這么一個需求:

1、??我們需要接收一個外部的訂單,而這個訂單號是不允許重復的

2、??數據庫對外部訂單號沒有做唯一性約束

3、??外部經常插入相同的訂單,對于已經存在的訂單則拒絕處理

對于這個需求,很簡單我們會用下面的代碼進行處理(思路:先查找數據庫,如果數據庫存在則直接退出,否則插入)

package?com.yhj.test;

?

import?com.yhj.dao.OrderDao;

import?com.yhj.pojo.Order;

?

/**

?*?@Description:并發測試用例

?*?@Author?YHJ??create at 2011-7-7?上午08:41:44

?*?@FileName?com.yhj.test.TestCase.java

?*/

public?class?TestCase {

????/**

?????* data access object class for deal order

?????*/

????private?OrderDao?orderDao;

?

????/**

?????*?@Description:插入測試

?????*?@param?object?要插入的object實例

?????*?@author?YHJ create at 2011-7-7?上午08:43:15

?????*?@throws?Exception

?????*/

????public?void?doTestForInsert(Order order)?throws?Exception {

???????Order orderInDB =?orderDao.findByName(order.getOrderNo());

???????if(null?!= orderInDB)

???????????throw?new?Exception("the order has been exist!");

???????orderDao.save(order);

????}

???

}

這樣很顯然,在單線程下是沒問題的,但是多線程情況下就會出現一個問題,線程1先去訪問DB,查找沒有,開始插入,這時候線程2又來查找DB,而此時線程1插入的事務還沒有提交,線程2沒有查到該數據,也進行插入,于是,問題出現了,插入了2條一樣訂單。

對于這種情況,好像如果不用數據庫做唯一性約束又不借助外部其他的一些工具,是沒有辦法實現的。那怎么做呢?

引入緩存,我們看下面的代碼

package?com.yhj.test;

?

import?com.yhj.dao.OrderDao;

import?com.yhj.pojo.Order;

import?com.yhj.util.MemcacheUtil;

import?com.yhj.util.MemcacheUtil.UNIT;

?

/**

?*?@Description:并發測試用例

?*?@Author?YHJ??create at 2011-7-7?上午08:41:44

?*?@FileName?com.yhj.test.TestCase.java

?*/

public?class?TestCase {

????/**

?????* data access object class for deal order

?????*/

????private?OrderDao?orderDao;

?

????/**

?????*?@Description:插入測試

?????*?@param?object?要插入的object實例

?????*?@author?YHJ create at 2011-7-7?上午08:43:15

?????*?@throws?Exception

?????*/

????public?void?doTestForInsert(Order order){

???????String key=null;

???????try{

???????????Order orderInDB =?orderDao.findByName(order.getOrderNo());

???????????//查DB,如果數據庫已經有則拋出異常

???????????if(null?!= orderInDB)

??????????????throw?new?Exception("the order has been exist!");

???????????key=order.getOrderNo();

???????????//插緩存,原子性操作,插入失敗?表明已經存在

???????????if(!MemcacheUtil.add(key, order, MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))

??????????????throw?new?Exception("the order has been exist!");

???????????//插DB

???????????orderDao.save(order);

???????}catch?(Exception e) {

???????????e.printStackTrace();

???????}finally{

???????????MemcacheUtil.del(key);

???????}

????}

?

}

運行步驟如下:

1、??查找數據庫,如果數據庫已經存在則拋出異常

2、??插入緩存,如果插入失敗則表明緩存中已經存在,拋出異常

3、??如果上述2步都沒有拋出異常,則執行插入數據庫的操作

4、??刪除緩存

在并發的情況下,線程1先查找數據庫,發現沒有,繼續執行,寫緩存,這時候線程2開始查找數據庫,發現沒有,則寫緩存,結果緩存中已經存在,寫緩存失敗,拋出異常,返回已存在。線程1執行插入數據庫成功,刪除緩存。以后再來的線程發現數據庫已經存在了,則不在向下執行,直接返回.。

機器異常情況下,不能執行finally語句,但是放在memcache中的數據會在1分鐘后超時。

貌似沒有問題。使用LodeRunner測試100個并發的操作,發現仍然有重復的訂單插入,這個是為什么呢?我們再來看這段代碼!

public?void?doTestForInsert(Order order){

???????String key=null;

???????try{

???????????Order orderInDB =?orderDao.findByName(order.getOrderNo());

???????????//查DB,如果數據庫已經有則拋出異常

???????????if(null?!= orderInDB)

??????????????throw?new?Exception("the order has been exist!");

???????????key=order.getOrderNo();

???????????//插緩存,原子性操作,插入失敗?表明已經存在

???????????if(!MemcacheUtil.add(key, order, MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))

??????????????throw?new?Exception("the order has been exist!");

???????????//插DB

???????????orderDao.save(order);

???????}catch?(Exception e) {

???????????e.printStackTrace();

???????}finally{

???????????MemcacheUtil.del(key);

???????}

????}

我們所預料的是2個線程同時操作,假設有更多的并發線程呢?

時刻1:

線程1到達,查數據庫,發現沒有

時刻2

線程1寫緩存

線程2到達,查數據庫發現沒有

時刻3

線程1緩存寫入成功,開始寫數據庫

線程2開始寫緩存

線程3到達,查數據庫,發現沒有

時刻4

線程1繼續插入數據庫

線程2寫緩存失敗,拋出異常,執行finally

線程3開始寫緩存

時刻5

線程1插入數據庫成功,開始構建返回結果

線程2執行finally,刪除緩存,開始構建返回結果

線程3發現緩存不存在(被線程2刪除),寫緩存

時刻6

線程1成功返回

線程2成功返回

線程3寫緩存成功,開始寫數據庫

時刻7

線程3寫數據庫成功,返回

因此上述代碼仍然有插入多條重復記錄的可能,我們在并發20的測試中發現成功插入了5筆訂單,其中4筆是不應該插入的!

那我們應該怎么解決呢?其實只要解決一個問題,只有插入DB時候的異常是可以刪除的,其他地方不應該刪除,那能不能將代碼改成下面的呢?

????public?void?doTestForInsert(Order order){

???????String key=null;

???????try{

???????????Order orderInDB =?orderDao.findByName(order.getOrderNo());

???????????//查DB,如果數據庫已經有則拋出異常

???????????if(null?!= orderInDB)

??????????????throw?new?Exception("the order has been exist!");

???????????key=order.getOrderNo();

???????????//插緩存,原子性操作,插入失敗?表明已經存在

???????????if(!MemcacheUtil.add(key, order, MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))

??????????????throw?new?Exception("the order has been exist!");

???????????//插DB

???????????orderDao.save(order);

???????????MemcacheUtil.del(key);

???????}catch?(Exception e) {

???????????e.printStackTrace();

???????}//finally{

//?????????MemcacheUtil.del(key);

//?????}

????}

這樣顯然不行,為什么呢?

這樣是保證了只有插入DB成功了才會刪除緩存,但是當插入DB的時候發生了一個異常,刪除緩存就不會再執行,雖然我們有一分鐘超時,但意味著我們一分鐘內該筆訂單是不能再被處理的,而實際上這邊訂單并沒有處理成功,所以這樣是不滿足需求的!

繼續改進

代碼如下:加一個標志位

public?void?doTestForInsert(Order order){

???????String key=null;

???????boolean?needDel=false;

???????try{

???????????Order orderInDB =?orderDao.findByName(order.getOrderNo());

???????????//查DB,如果數據庫已經有則拋出異常

???????????if(null?!= orderInDB)

??????????????throw?new?Exception("the order has been exist!");

???????????key=order.getOrderNo();

???????????//插緩存,原子性操作,插入失敗?表明已經存在

???????????if(!MemcacheUtil.add(key, order,?MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))

??????????????throw?new?Exception("the order has been exist!");

???????????needDel=true;

???????????//插DB

???????????orderDao.save(order);

???????}catch?(Exception e) {

???????????e.printStackTrace();

???????}finally{

???????????if(needDel)

??????????????MemcacheUtil.del(key);

???????}

????}

這樣是不是完美解決了呢?

在其他異常執行的時候是不會刪除緩存的,我們套在之前的代碼上,線程2判斷緩存中存在拋出異常執行finally的時候是不會刪除緩存的,因此線程3沒有機會執行寫緩存的操作,從而保證了線程1是唯一能夠插入DB的。

還有沒有其他漏洞呢?期待大家發現……

來源:https://www.cnblogs.com/yinchuan/p/4910980.html

總結

以上是生活随笔為你收集整理的如何处理高并发情况下的DB插入的全部內容,希望文章能夠幫你解決所遇到的問題。

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