并發(fā)環(huán)境下mysql插入檢查方案
業(yè)務(wù)背景:
基本業(yè)務(wù)場景是這樣的,請求數(shù)據(jù)(車輛vin信息)進(jìn)入到接口中,需要先判斷其在數(shù)據(jù)庫中的狀態(tài),如果庫中不存在該vin,或者該vin狀態(tài)位為“1(已完成)”,則執(zhí)行一些檢查操作后,將數(shù)據(jù)插入到數(shù)據(jù)庫中,此時(shí)新增vin狀態(tài)為0,調(diào)用人工處理接口,十分鐘后返回結(jié)果,將狀態(tài)置為1。如果其狀態(tài)位為“0(正在處理)”則駁回操作,返回提示信息。
在單線程環(huán)境下,這樣的業(yè)務(wù)沒有問題,然而當(dāng)并發(fā)訪問接口時(shí),會(huì)出現(xiàn)同時(shí)進(jìn)入兩條vin相同的請求AB,正常情況應(yīng)該插入一條A,駁回一條B。然而并發(fā)環(huán)境下,B執(zhí)行檢查狀態(tài)時(shí)A還沒有插入,因此AB都進(jìn)入到了數(shù)據(jù)庫中,數(shù)據(jù)就錯(cuò)誤了。
解決方案一:
首先想到的是使用sql處理,對數(shù)據(jù)庫對應(yīng)字段加唯一索引,保證一致性。如果插入重復(fù)的數(shù)據(jù),則catch該異常,做出提示。
ALTER tableName ADD UNIQUE [indexName] ON (tableColumns(length))
但是由于業(yè)務(wù)限制,vin在庫中是可以重復(fù)的,多條重復(fù)數(shù)據(jù)查詢最新,所以不能再vin上添加唯一索引。
解決方案二:
使用mysql事務(wù)操作,將檢查是否存在和插入作為一個(gè)事務(wù)進(jìn)行處理,當(dāng)檢查失敗的時(shí)候,不進(jìn)行插入。從網(wǎng)上搜索了一下,大致思路如下:
public static void StartTransaction(Connection con, String[] sqls) throws Exception {
try {
// 事務(wù)開始
con.setAutoCommit(false); // 設(shè)置連接不自動(dòng)提交,即用該連接進(jìn)行的操作都不更新到數(shù)據(jù)庫
sm = con.createStatement(); // 創(chuàng)建Statement對象
//依次執(zhí)行傳入的SQL語句
for (int i = 0; i < sqls.length; i++) {
sm.execute(sqls[i]);// 執(zhí)行添加事物的語句
}
con.commit(); // 提交給數(shù)據(jù)庫處理
// 事務(wù)結(jié)束
//捕獲執(zhí)行SQL語句組中的異常
} catch (SQLException e) {
try {
System.out.println("事務(wù)執(zhí)行失敗,進(jìn)行回滾!\n");
con.rollback(); // 若前面某條語句出現(xiàn)異常時(shí),進(jìn)行回滾,取消前面執(zhí)行的所有操作
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
sm.close();
}
}
但是這樣實(shí)際上還是沒有解決并發(fā)的問題,這樣只是把兩個(gè)操作變成了一個(gè)原子的sql操作,可以用于同時(shí)插入兩條數(shù)據(jù)一致性的情況,但并不適合需求。
既然sql層面沒有解決問題,就考慮從java的并發(fā)編程方向解決。
解決方案三:
java解決并發(fā)問題,首先想到的是使用內(nèi)置鎖或者可重入鎖,基本語法如下:
·內(nèi)置鎖:
由于是在Servlet中進(jìn)行的處理,因此使用synchronized(this)直接處理業(yè)務(wù)代碼,使得并發(fā)情況下,只能有一個(gè)線程訪問到該段業(yè)務(wù)代碼:
synchronized(this){
//todo1:檢查vin是否存在
//todo2:如果不存在插入vin
}
·可重入鎖:
相當(dāng)于更靈活的內(nèi)置鎖,在這里與內(nèi)置鎖基本相同
public class DashengCallBack extends HttpServlet {
private static ReentrantLock lock= new ReentrantLock();
protected void doGet(HttpServletRequest request, HttpServletResponse response){
lock.lock();
try{
//todo1:檢查vin是否存在
//todo2:如果不存在插入vin
}finally{
lock.unlock();
}
}
}
經(jīng)過測試,這個(gè)方案是可行的,最終沒有采用的原因是因?yàn)橹苯邮褂眠@種方式加鎖,加鎖的代碼太多,影響效率。
解決方案四:
設(shè)置一個(gè)查詢Map,插入前存儲(chǔ)數(shù)據(jù),插入后刪除數(shù)據(jù),代碼如下:
ConcurrentHashMap<String, String> vinMap=new ConcurrentHashMap<String,String>();
if(vinMap.containsKey(vin)){
// todo1: vin 請求完畢后, 從vinInRequestMap里刪掉這個(gè)vinNo
// todo2: 返回正在查詢
}
vinMap.put(vin, "");
//todo3:插入vin到數(shù)據(jù)庫
vinMap.remove(vin);
}
這個(gè)方案基本滿足了業(yè)務(wù)需求,唯一的問題是要求接口的更新時(shí)間要與業(yè)務(wù)時(shí)間錯(cuò)開,否則更新接口會(huì)清空vinMap,導(dǎo)致庫中數(shù)據(jù)混亂,出現(xiàn)錯(cuò)誤。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。
相關(guān)文章
MySQL系列數(shù)據(jù)庫設(shè)計(jì)三范式教程示例
這篇文章主要為大家介紹了MySQL系列之?dāng)?shù)據(jù)庫設(shè)計(jì)三范式的教程示例講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10
mysql之關(guān)于CST和GMT時(shí)區(qū)時(shí)間轉(zhuǎn)換方式
這篇文章主要介紹了mysql之關(guān)于CST和GMT時(shí)區(qū)時(shí)間轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
phpmyadmin中為站點(diǎn)設(shè)置mysql權(quán)限的圖文方法
在一個(gè)服務(wù)器上一般來講都不止一個(gè)站點(diǎn),更不止一個(gè)MySQL(和PHP搭配之最佳組合)數(shù)據(jù)庫。2011-03-03
關(guān)于mysql中innodb的count優(yōu)化問題分享
關(guān)于mysql中innodb的count優(yōu)化問題,有需要的朋友可以參考下2013-02-02
mysql中字符串截取與拆分的實(shí)現(xiàn)示例
mysql 字符串截取與拆分在很多地方都可以用得到,本文主要介紹了mysql中字符串截取與拆分的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
SQL實(shí)戰(zhàn)演練之網(wǎng)上商城數(shù)據(jù)庫用戶信息數(shù)據(jù)操作
一直認(rèn)為,扎實(shí)的SQL功底是一名數(shù)據(jù)分析師的安身立命之本,甚至可以稱得上是所有數(shù)據(jù)從業(yè)者的基本功。當(dāng)然,這里的SQL絕不單單是寫幾條查詢語句那么簡單,接下來請跟著小編通過案例項(xiàng)目進(jìn)一步提高SQL的能力吧2021-10-10

