因網絡延遲造成數據庫插入相同記錄,如何解決.


場景:導入會員數據,出現了重復數據在庫里面。導入過程中有身份證號的唯一檢查,怎么還會重復呢。百思不得其解。。。

萬惡的網絡延遲。。。。

仔細查了遍代碼,發現身份證號檢查速度特別慢,造成了網絡阻塞。。。

對於用戶來說不知道怎么回事(以為沒反應),點擊了導入。。。兩個線程之間幾乎沒有時間差。所以即使有檢查,也沒管用。

解決案

1.優化身份證號的檢查,並在表中為身份證號加索引

2.mybatis上做的手腳,重復時候不插入。

  • 1.如果某些關鍵字段已經在Mysql中存在了,不要重復插入,而是改為更新某些字段。
  • 2.如果某些關鍵字段已經在Mysql中存在了,不要重復插入,也不需要做更新操作,直接忽略即可。

 

       1、針對上面的第一種,mysql提供了insert into ... on duplicate key update ... 語法(Mysql自己的語法,不屬於標准SQL)來實現。這種場景相對常見。使用的前提是,需要定義主鍵或者           唯一性索引,用來告訴Mysql哪些字段的重復會造成主鍵沖突或者違背唯一性約束條件。當這些情況出現時,就放棄insert操作,轉而執行后面的update操作。上一個具體的例子,在ibatis           中寫的一個上面邏輯的sql:

 

         Xml代碼   收藏代碼
  1. <statement id="testInsertOnDuplicateKeyUpdate"  
  2.             parameterClass="com.alibaba.ais.bdc.person.dataobject.UserInfo">  
  3.      <!--Here the employ_id has been defined as a primary key-->  
  4.      insert into T_USER_INFO (employ_id,fans_count,follow_count)  
  5.      values (#employId#,#fansCount#,#followCount#)  
  6.      ON DUPLICATE KEY UPDATE follow_countfollow_count=follow_count+1  
  7. </statement>  

       上面的插入操作就會在employ_id發生主鍵沖突的時候,轉變為更新follow_count字段(自增一個單位)。

 

2、針對上面的第二種,mysql提供了insert ignore into ... 語法(Mysql自己的語法,不屬於標准SQL)來實現。這種場景相對要少見一些。 使用的前提是,需要定義主鍵或者唯一性索引,原因同上面一致。但不同的就是,違背唯一性約束時,直接忽略這次insert操作,然后啥也不干了,也不會報錯。。。

但這里有個問題,就是有時業務本身還是需要知道語句到底執行了insert還是沒有執行!因為需要根據這個反饋信息,做出不同的處理邏輯。這里給出使用ibatis時,兩種不同的處理方法。

 

  • 借助mysql的內置函數row_count()和ibatis的selectKey標簽:
    • 關於ibatis的selectKey標簽,更為常見的用法是在插入對象到DB之后,返回DB自增產生的主鍵ID。比如:
      • Xml代碼   收藏代碼
        1. <insert id="testSelectKey">    
        2.           insert into T_TEST(WATCHER, WATCHEE) values (#watcher#,#watchee#)  
        3.           <selectKey resultClass="int" keyProperty="id">  
        4.               select last_insert_id() as ID from dependency limit 1  
        5.           </selectKey>  
        6. lt;/insert>  
         
      • 通過上面語句進行插入操作之后,可以通過如下方式在Java代碼中獲得這個DB自增產生的ID:
      • Java代碼   收藏代碼
        1. Integer id = (Integer) sqlMapClient.insert("testSelectKey", param);    
    • row_count()是Mysql提供可以獲得insert、update、delete這幾種操作成功處理行數的一個內置函數,有了這個函數,再結合上面給出的selectKey標簽的例子,就可以做到獲得insert ignore時,成功insert的行數。有了這個行數,也就知道了到底有沒有發生insert操作。(發生了插入操作該函數返回值會大於0),於是改編一下上面的sql-map中的sql(注意其中的ignore關鍵字):
      • Xml代碼   收藏代碼
        1. <insert id="testInsertIgnore">    
        2.           insert ignore into T_TEST(WATCHER, WATCHEE) values (#watcher#,#watchee#)  
        3.           <selectKey resultClass="int" keyProperty="id">  
        4.               select row_count() as ID from T_USER_RELATION limit 1  
        5.           </selectKey>  
        6. lt;/insert>  
        通過上面語句進行插入操作之后,可以通過如下方式在Java代碼中獲得insert真正影響到的行數:
      • Java代碼   收藏代碼
        1. Integer affectedRows = (Integer)sqlMapClient.insert("testInsertIgnore", param);  
         
  • 利用ibatis的update語句:上面之所以要費勁周折的修改insert語句,時因為ibatis本身的insert不是默認支持返回影響的行數的。但是update語句支持!於是,另外一個思路,就是利用update語句來完成這個insert操作。
    • Xml代碼   收藏代碼
      1. <update id="testInsertIgnore">  
      2.        insert ignore into T_USER_RELATION(WATCHER, WATCHEE) values (#watcher#,#watchee#)  
      3.        <!--Without selectKey tag, it looks mush more concise-->  
      4.    </update>  
       通過上面語句進行插入操作之后,可以通過如下方式在Java代碼中獲得insert真正影響到的行數(這里需要注意的是,Java代碼中需要使用sqlMapClient的update的API哦):
    • Java代碼   收藏代碼
      1. Integer affectedRows = (Integer)sqlMapClient.update("testInsertIgnore", param);  

 

教訓:可能開發人員為了省事,喜歡共用sql文。但是一個小小的檢查其實是不需要關聯那么多表的。共用后,導致檢查時間過慢。

          特別是表數據量較大的時候非常明顯。建議,新寫個檢查的sql

 


免責聲明!

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



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