一例データ同期異常問題分析

2957 ワード

【問題の説明】
開発フィードバックによると、SQL Serverデータ同期の作業があり、Table 1からデータを引き出し、メインキーはID、ロットデータを引き出すSQL文はselect top(15)*from Table 1(NOLOCK)where ID>?前回の同期ロットの最後のID番号を表します.一度にプルされたデータは、ID:81011021218101103081の2つのデータです.調査表によると、この2つの記録の間には、ID=8101102855という記録があり、この記録は引き出されていないことが分かった.非常に困惑して、どうして1本の記録を少なくして、引き抜くかどうかの時、この記録は作成されていませんか?
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  
    
処理済み
【問題分析】
この問題を最初から見ても、とてもおかしいと思います.このクエリ文にはルールがあり、アプリケーションログから見ると、2つの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)のデータベースでクエリーを行い、確かに3つのレコードを返すことができます.
データベースの詳細レコードにより、この文のデータベースでの実行時間は、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を読み取ることができる.
【問題再現】
問題をよりよく分析するために、定点でデータベースを復元し、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は挿入直後に記録され、そのColumn 4の値はNULLであり、意味的には確かに「(特殊な需要)」という文字列は含まれていない.しかし、SQLの処理では、私たちの理解とは違います.
SQLはIS NULLとNOT NULL以外はNULLが出ていればFALSEとなります.簡単に言えばSELECT*from table where name!='test’,name値がNULLであれば,name=’test’でもname!='test'は、この行に戻ることはできません.戻る場合は、IS NULL判定を追加する必要があります.
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の場合に注意してください.