一例數據同步異常問題分析

【問題描述】

開發反饋,有一個SQL Server數據同步的做業,從Table1 拉取數據,主鍵是ID, 每次拉取批次數據的SQL語句是 select top (15) * from Table1(NOLOCK) where ID > ?,?表明的是上次同步批次中最後一個ID號。
某一次拉取到的數據爲 ID: 8101102121,8101103081 兩條數據。查表發現,這兩條記錄中間,還有一條記錄,即ID=8101102855,這條記錄沒有被拉取到。十分困惑,爲何會少拉一條記錄,是否拉取的時候,該條記錄沒有被建立?數據庫

Createtime ID Column3 Column4
  2019-04-11 14:17:14.843     8101102121           已處理
  2019-04-11 14:17:17.190     8101102855          已處理
  2019-04-11 14:17:20.237     8101103081          已處理

【問題分析】

剛開始看這個問題,也以爲很是奇怪。這個查詢語句中規中矩,從應用日誌來看,兩個ID之間的8101102855 確實是沒有被拉取到。
爲進一步定位問題,咱們分析具體的查詢語句。因爲咱們的服務器開啓了XEvent Trace,咱們定位到,當時在數據庫服務器端真正執行的語句以下,比開發反饋的更多一些條件:
服務器

select top 15 id from table1 with (nolock) where id > 8101101700 and Column4 not like '%(特殊需求)%' and createtime > '2018-07-19 00:00:00.000' order by id asc

咱們用這個查詢,在當前時間(2019-04-12 17:23:00)數據庫上進行查詢,確實能返回三條記錄。調試

經過數據庫明細記錄, 該語句在數據庫上的執行時間是:2019-04-11 14:17:21。對於ID=8101102855的記錄,其插入的時間是2019-04-11 14:17:17.190,比咱們的查詢時間早4秒,按道理應該是能查出來的。另外,在2019-04-11 14:17:21的時候,ID=8101102855 的記錄正在被更新。難道是咱們的查詢帶了NOLOCK,因此當前正在被更新的記錄跳過了?日誌

根據咱們的理解,NOLOCK至關於Read uncommitted, 是會讀取其餘事務「修改後未提交的「數據。也就是說,是可以讀出主鍵ID的。code

【問題重現】

爲了可以更好的分析問題,咱們定點還原數據庫,還原到2019-04-11 14:17:20的時候,也就是比應用的查詢時間早1秒鐘。其數據記錄以下。在這個點上,ID=8101103081 尚未插入進來,但ID=8101102855,也就是咱們關注的ID,數據已經進來了。事務

Createtime ID Column3 Column4
  2019-04-11 14:17:14.843     8101102121           已處理
  2019-04-11 14:17:17.190     8101102855          

針對上面的數據,咱們執行查詢:
開發

select top 15 id from table1 with (nolock) where id > 8101101700 and Column4 not like '%(特殊需求)%' and createtime > '2018-07-19 00:00:00.000' order by id asc

奇怪的發現,這個查詢只返回ID=8101102121,ID=8101102855沒有返回。通過簡單的調試,咱們很快發現,是因爲這個查詢條件所致:字符串

Column4 not like '%(特殊需求)%'

ID=8101102855記錄在剛插入的時候,其Column4的值爲NULL,從語義上來說,確實是不包含"(特殊需求)"這個字符串的。但在SQL處理上,卻和咱們理解的不同。同步

SQL除了IS NULL和NOT NULL之外,只要出現NULL,值結果爲FALSE。簡單來講,對於查詢SELECT * from table where name != ‘test’,只要name值是NULL,不管用name=’test’仍是name != 'test', 都不能返回這一行。若是要返回的話,須要加IS NULL判斷:it

SELECT * from table where name != ‘test’ or name IS NULL

至此,問題真相大白,解決方案也就很簡單:調整查詢語句,加一個IS NULL判斷便可。

select top 15 id from table1 with (nolock) where id > 8101101700 and (Column4 not like '%(特殊需求)%' or Column4 IS NULL) and createtime > '2018-07-19 00:00:00.000' order by id asc

【結論】

數據庫在碰到NULL的處理時候,要當心,查詢的判斷條件並非很明顯,要注意IS NULL的狀況。

相關文章
相關標籤/搜索