並發環境下mysql插入檢查方案_Mysql

來源:互聯網
上載者:User

業務背景:
基本業務情境是這樣的,請求資料(車輛vin資訊)進入到介面中,需要先判斷其在資料庫中的狀態,如果庫中不存在該vin,或者該vin狀態位為“1(已完成)”,則執行一些檢查操作後,將資料插入到資料庫中,此時新增vin狀態為0,調用人工處理介面,十分鐘後返回結果,將狀態置為1。如果其狀態位為“0(正在處理)”則駁回操作,返回提示資訊。
在單線程環境下,這樣的業務沒有問題,然而當並發提供者時,會出現同時進入兩條vin相同的請求AB,正常情況應該插入一條A,駁回一條B。然而並發環境下,B執行檢查狀態時A還沒有插入,因此AB都進入到了資料庫中,資料就錯誤了。

解決方案一:
首先想到的是使用sql處理,對資料庫對應欄位加唯一索引,保證一致性。如果插入重複的資料,則catch該異常,做出提示。

ALTER tableName ADD UNIQUE [indexName] ON (tableColumns(length))

但是由於業務限制,vin在庫中是可以重複的,多條重複資料查詢最新,所以不能再vin上添加唯一索引。

解決方案二:
使用mysql事務操作,將檢查是否存在和插入作為一個事務進行處理,當檢查失敗的時候,不進行插入。從網上搜尋了一下,大致思路如下:

public static void StartTransaction(Connection con, String[] sqls) throws Exception {     try {       // 事務開始       con.setAutoCommit(false);  // 設定串連不自動認可,即用該串連進行的操作都不更新到資料庫       sm = con.createStatement(); // 建立Statement對象       //依次執行傳入的SQL語句       for (int i = 0; i < sqls.length; i++) {         sm.execute(sqls[i]);// 執行添加事物的語句       }       con.commit();  // 提交給資料庫處理       // 事務結束     //捕獲執行SQL語句組中的異常       } catch (SQLException e) {       try {         System.out.println("事務執行失敗,進行復原!\n");         con.rollback(); // 若前面某條語句出現異常時,進行復原,取消前面執行的所有操作       } catch (SQLException e1) {         e1.printStackTrace();       }     } finally {       sm.close();     }   }

但是這樣實際上還是沒有解決並發的問題,這樣只是把兩個操作變成了一個原子的sql操作,可以用於同時插入兩條資料一致性的情況,但並不適合需求。

既然sql層面沒有解決問題,就考慮從java的並發編程方向解決。
解決方案三:
java解決並發問題,首先想到的是使用內建鎖或者可重新進入鎖,基本文法如下:
·內建鎖:
由於是在Servlet中進行的處理,因此使用synchronized(this)直接處理業務代碼,使得並發情況下,只能有一個線程訪問到該段業務代碼:

synchronized(this){  //todo1:檢查vin是否存在  //todo2:如果不存在插入vin}

·可重新進入鎖:
相當於更靈活的內建鎖,在這裡與內建鎖基本相同

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();    }  }}

經過測試,這個方案是可行的,最終沒有採用的原因是因為直接使用這種方式加鎖,加鎖的代碼太多,影響效率。

解決方案四:
設定一個查詢Map,插入前儲存資料,插入後刪除資料,代碼如下:

    ConcurrentHashMap<String, String> vinMap=new ConcurrentHashMap<String,String>();    if(vinMap.containsKey(vin)){      // todo1: vin 請求完畢後, 從vinInRequestMap裡刪掉這個vinNo      // todo2: 返回正在查詢    }    vinMap.put(vin, "");    //todo3:插入vin到資料庫    vinMap.remove(vin);  }

這個方案基本滿足了業務需求,唯一的問題是要求介面的更新時間要與業務時間錯開,否則更新介面會清空vinMap,導致庫中資料混亂,出現錯誤。

以上就是本文的全部內容,希望對大家的學習有所協助。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.