1 |
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart. |
经检查,这个报错,调用的是下面的一个监控:
1 2 3 4 |
select count(*) as cnt from sys.sysprocesses where DateDiff(ss,last_batch,getDate())>=10 and lastwaittype Like 'PAGE%LATCH_%' And waitresource Like '2:%' |
这个监控脚本,是用来监控发生在temp上的pagelatch_up的争用。监控脚本中,包含了datediff函数。datediff的返回值如果overflow,将导致上面的报错。
我们来看看,datediff这个值溢出的情况。在官方文档中,datediff函数定义返回的是int值,int值的取值范围是 (-2,147,483,648 to +2,147,483,647)。所以,第一步的怀疑,是抓取的起始时间和结束时间之差,溢出了。
那么,什么时候会溢出? 如果进程是刚刚发起的,那么之间的差值,应该会很短,不会溢出。那么离目前时间最远的进程,会不会溢出?
在SQL Server中进程分为客户端进程和系统进程,一般情况下,客户端进程都是最近发起的,所以时间差不会溢出。是否是系统进程导致时间差溢出的呢?
因为系统进程不是客户端发起的,所以系统进程的last_batch时间,就是数据库的启动时间,我们检查了一下数据库的启动时间:
1 |
SELECT sqlserver_start_time FROM sys.dm_os_sys_info; |
发现是2018-04-14 22:02:46.377。这个时间是否有可能导致溢出?
可以看到,如果是到秒级,即datediff(ss),中间的时间差是可以长达68年19天3小时14分7秒的。而我们的数据库启动时间,远远没有超过68年。
去掉where条件之后,重新运行了几次上述的SQL语句,没有发现早于2018-04-14的。
正当束手无策的时候,想起在这个数据库上部署过msawr,会定期snapshot各项性能指标,那么可以从msawr中去找找线索。
确实,我们在msawr中发现了有些进程的last_batch早于数据库启动的时间,这个时间,是1900-01-01 00:00:00.000。
last_batch是个datetime的值,在官方文档中说明中,datetime类型默认值是1900:
而last_batch的这个字段,是not null:
也就是说,在为null的情况下,这个datetime类型的值,将有默认值来填充,所以也就出现了1900-01-01 00:00:00.000。
那么sysprocesses的last_batch会出现控制,进而被替代成1900-01-01 00:00:00.000 ?这个在网上找很多文章,都归结到微软的这个文章:”INF: Last Batch Date is Seen as 1900-01-01 00:00:00.000″ at http://support.microsoft.com/?kbid=306625 , 但是点进去你会发现,这个文章已经404找不到了。
幸好,还有另外的一个文章启发了我:
它说:
1 |
However, it's possible to create a connection to SQL Server without issuing any RPC calls at all. In this case, the value of last_batch will never have been set and master..sysprocesses will display the value as 1900-01-01 00:00:00.000. |
也就是说,由非远程调用(RPC,remote procedure call)发起的进程,其last_batch是null值,而null值继而会被1900-01-01 00:00:00.000所替代。
我们进而看lastwaittype:发现其大部分的,是CXPACKET的并发等待。
所以,应该是并发进程,不是有RPC远程调用的,而是直接在本地调用的。在第一次的时候,last_batch没有被更新,只是留有了null值,进而被替换成了1900年。从而导致了我们的溢出报错。
解决方式也很简单。因为1900年的固定的值,加个条件and last_batch<>‘1900-01-01 00:00:00.000’ 就可以了。
1 2 3 4 5 |
select count(*) as cnt from sys.sysprocesses where DateDiff(ss,last_batch,getDate())>=10 and lastwaittype Like 'PAGE%LATCH_%' And waitresource Like '2:%' and last_batch<>'1900-01-01 00:00:00.000' |