经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MS SQL Server » 查看文章
一例数据同步异常问题分析
来源:cnblogs  作者:携程DBA  时间:2019/4/24 9:58:17  对本文有异议

【问题描述】

开发反馈,有一个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,我们定位到,当时在数据库服务器端真正执行的语句如下,比开发反馈的更多一些条件:

  1. 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的。

【问题重现】

为了能够更好的分析问题,我们定点还原数据库,还原到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?? ???? ??

针对上面的数据,我们执行查询:

  1. 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没有返回。经过简单的调试,我们很快发现,是由于这个查询条件所致:

  1. 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判断:

  1. SELECT * from table where name != test or name IS NULL

至此,问题真相大白,解决方案也就很简单:调整查询语句,加一个IS NULL判断即可。

  1. 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的情况。

原文链接:http://www.cnblogs.com/CtripDBA/p/10755110.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号