系統(tǒng)高吞吐量下的數據庫重復寫入問題分析解決
問題分析
為了提高系統(tǒng)的吞吐量,很多環(huán)節(jié)下對于數據庫的寫入是多線程,甚至是多進程的。為了保證寫入成功,在很多情況下需要多次重試。這就會帶來一個問題,數據重復,同一條數據會被記錄多次。有些情況下數據重復無傷大雅,但是很多情況系統(tǒng)是無法容忍數據重復的。因此這個問題需要解決。我個人覺得解決這一問題有兩個方向:第一,從數據庫上保證數據不重復,第二,從程序上保證數據不重復。
數據庫上解決
主要包括:主鍵,唯一性索引,甚至是臨時表。程序上解決無非就是要保證同步,這兩種方式能解決很多情況下的數據重復。但是有些情況可能比較棘手,使用者兩種方法有時并不能很好的解決,或實現起來比較復雜,如下面的數據
假如下表主要字段如下:
id studentId teacherId states
狀態(tài)(states)是有多種的(0,1,2,3),狀態(tài)可以轉換,但是狀態(tài)為,0的一個只能由一個,其它字段是可以重復的。這個其實就是保證某一種狀態(tài)下的數據不重復。
首先唯一性索引不能夠起作用,因為無法建立唯一性索引。主鍵也沒有效果,沒法通過這些字段生成可區(qū)分的id。所以這兩種方法都失效了。還有一種方法就是臨時表,在臨時表中插入一條能夠區(qū)分的數據(比如studentId,teacherId組合),無論是唯一索引還是主鍵都可以。寫入時首先寫臨時表,臨時表寫入成功則插入一條數據,然后清空臨時表。這在嚴格保證數據不重復的情況下是能夠起作用的,但是比較繁瑣,需要處理一個臨時表。
另外的一個辦法就是根據我們的業(yè)務場景,在一個時間段內(比如1分鐘)不會出現兩條相同數據寫入。這樣我們可以使用studentIdTeacherid加上精確到分鐘的時間來構成一個唯一id,重試時間間隔一般都極短(秒級別),這樣通過id來保證數據的唯一性。
從程序上保證數據不重復
如果從程序上來保證數據不重復,則更加復雜。第一種辦法是對寫入過程加鎖,確保只有一次寫入成功代碼如下(偽代碼):
Lock lock =new WriteLock();
public void write(Data data){
try{
if(lock.tryLock() ){
dataRepository.save(data);
if(dataRepository.numberOf(data)>1){//在寫入的時候檢測如果數據庫中有該數據拋出異常。再次保證數據不重復。
throw new DataException
}
}
}finally{
lock.unlock();
}
}這種方式首先會存在效率問題,所有的數據都要順序寫入會導入效率下降。我們只需要保證同一條數據不能并發(fā)寫入而不是不同數據。另外這種方式還會存在一定概率的重復,因為網絡問題和數據庫或ORM框架的緩存問題,會導致寫入檢測時并不能發(fā)現數據庫的更新。比如使用hibernate,兩次線程調用write方法會使用兩個session,從而使得第一次寫入緩存的數據無法在下一次操作中看到。在write方法中多次調用numberOf方法也是不起用的,由于session的緩存,后面的查詢會使用第一次的緩存結果,在第一次查詢后的數據庫變化,后面的查詢仍然無法覺察。
針對寫入效率低的問題,這里可以采用數據鎖,即通過一種方法比如使用data的hashcode來映射來獲取鎖,這樣不同的數據會獲取到不同的鎖,解決了所有數據的順序寫入問題。但是跟第一種情況一樣仍會存在數據重復問題。
對于多進程的情況,如微服務部署多個的情況,上面的同步會失效。對于這種情況唯一的解決辦法就是使用上面所說的數據庫同步或者構造一個環(huán)節(jié)鎖,類似于令牌的方法。只有獲取到令牌才有寫入資格,寫入成功后銷毀針對該數據的“令牌“。這種實現其實也比較簡單,如使用一個redis的hashmap,每次寫入首先獲取該數據對應的value,通過value來判斷該數據是否寫入,來保證數據不重復。
總結
上面無論哪種方法,針對本文所提到的數據,解決重復問題都是不容易的。要么實現起來比較復雜,要么還是不能100%保證數據不重復。針對我們的業(yè)務場景:state為0的狀態(tài)下數據能有一條,且0的狀態(tài)不會持續(xù)太久,后面的操作會將其修改。而且多線程重試并不是每次都發(fā)生的,多進程情況下,每次數據寫入也多是只通過其中一個節(jié)點,針對這種情況其實可以采取更簡單的處理方式,不做過多的順序限制只在寫入時檢查數據庫,如果真的因為數據更新或者并發(fā)導致了多次寫入也沒有關系(這種情況很少),后續(xù)的操作時再刪除多寫入的數據。這是一種樂觀的處理方式,但是對于很多情況是可以解決數據重復問題的。
以上是我個人對于只有某種狀態(tài)的數據不能重復寫入問題的處理方法的思考。主要是從數據庫和程序上來控制及如果場景允許使用樂觀(后續(xù)補救)的方法。僅供參考,更多關于高吞吐量系統(tǒng)的數據庫重復寫入解決的資料請關注腳本之家其它相關文章!
相關文章
MySQL連接異常:Communications link failure問題及解決
這篇文章主要介紹了MySQL連接異常:Communications link failure問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11

