場景:導入會員數據,出現了重復數據在庫里面。導入過程中有身份證號的唯一檢查,怎么還會重復呢。百思不得其解。。。
萬惡的網絡延遲。。。。
仔細查了遍代碼,發現身份證號檢查速度特別慢,造成了網絡阻塞。。。
對於用戶來說不知道怎么回事(以為沒反應),點擊了導入。。。兩個線程之間幾乎沒有時間差。所以即使有檢查,也沒管用。
解決案
1.優化身份證號的檢查,並在表中為身份證號加索引
2.mybatis上做的手腳,重復時候不插入。
- 1.如果某些關鍵字段已經在Mysql中存在了,不要重復插入,而是改為更新某些字段。
- 2.如果某些關鍵字段已經在Mysql中存在了,不要重復插入,也不需要做更新操作,直接忽略即可。
1、針對上面的第一種,mysql提供了insert into ... on duplicate key update ... 語法(Mysql自己的語法,不屬於標准SQL)來實現。這種場景相對常見。使用的前提是,需要定義主鍵或者 唯一性索引,用來告訴Mysql哪些字段的重復會造成主鍵沖突或者違背唯一性約束條件。當這些情況出現時,就放棄insert操作,轉而執行后面的update操作。上一個具體的例子,在ibatis 中寫的一個上面邏輯的sql:
- <statement id="testInsertOnDuplicateKeyUpdate"
- parameterClass="com.alibaba.ais.bdc.person.dataobject.UserInfo">
- <!--Here the employ_id has been defined as a primary key-->
- insert into T_USER_INFO (employ_id,fans_count,follow_count)
- values (#employId#,#fansCount#,#followCount#)
- ON DUPLICATE KEY UPDATE follow_countfollow_count=follow_count+1
- </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。比如:
-
- <insert id="testSelectKey">
- insert into T_TEST(WATCHER, WATCHEE) values (#watcher#,#watchee#)
- <selectKey resultClass="int" keyProperty="id">
- select last_insert_id() as ID from dependency limit 1
- </selectKey>
- lt;/insert>
- 通過上面語句進行插入操作之后,可以通過如下方式在Java代碼中獲得這個DB自增產生的ID:
-
- Integer id = (Integer) sqlMapClient.insert("testSelectKey", param);
-
- row_count()是Mysql提供可以獲得insert、update、delete這幾種操作成功處理行數的一個內置函數,有了這個函數,再結合上面給出的selectKey標簽的例子,就可以做到獲得insert ignore時,成功insert的行數。有了這個行數,也就知道了到底有沒有發生insert操作。(發生了插入操作該函數返回值會大於0),於是改編一下上面的sql-map中的sql(注意其中的ignore關鍵字):
-
- <insert id="testInsertIgnore">
- insert ignore into T_TEST(WATCHER, WATCHEE) values (#watcher#,#watchee#)
- <selectKey resultClass="int" keyProperty="id">
- select row_count() as ID from T_USER_RELATION limit 1
- </selectKey>
- lt;/insert>
-
- Integer affectedRows = (Integer)sqlMapClient.insert("testInsertIgnore", param);
-
- 關於ibatis的selectKey標簽,更為常見的用法是在插入對象到DB之后,返回DB自增產生的主鍵ID。比如:
- 利用ibatis的update語句:上面之所以要費勁周折的修改insert語句,時因為ibatis本身的insert不是默認支持返回影響的行數的。但是update語句支持!於是,另外一個思路,就是利用update語句來完成這個insert操作。
-
- <update id="testInsertIgnore">
- insert ignore into T_USER_RELATION(WATCHER, WATCHEE) values (#watcher#,#watchee#)
- <!--Without selectKey tag, it looks mush more concise-->
- </update>
-
- Integer affectedRows = (Integer)sqlMapClient.update("testInsertIgnore", param);
-
教訓:可能開發人員為了省事,喜歡共用sql文。但是一個小小的檢查其實是不需要關聯那么多表的。共用后,導致檢查時間過慢。
特別是表數據量較大的時候非常明顯。建議,新寫個檢查的sql