最近看到一个有趣的系统函数应用问题。
起因是A使用checksum(),对两行数据是否完全一致进行判断。后来使用了binary_checksum() 进行判断。
但在实际的运行过程中,发现会出现哈希碰撞的问题。程序表现不稳定。
测试了一下
SELECT CHECKSUM(9),
CHECKSUM('asdfa', 9),
CHECKSUM(123, GETDATE(), 'xyz'),
BINARY_CHECKSUM(9),
BINARY_CHECKSUM('asdfa', 9),
BINARY_CHECKSUM(123, GETDATE(), 'xyz');
- 判断两行信息是否一致的逻辑,大概是这样:
DECLARE @i TABLE(id INT,name VARCHAR(10),age TINYINT)
INSERT INTO @i
(
id,
name,
age
)
SELECT 1,'张三',12
UNION all
SELECT 2,'李四',15
UNION all
SELECT 1,'张三',12
UNION all
SELECT 1,'王五',22
SELECT CHECKSUM(*),BINARY_CHECKSUM(*) FROM @i
如果判定这两个值有相等的,即可判定这两行是信息是一致的。(如果有很多列,这个用法是不是很酷炫,但是....)
碰撞
在数学的角度来看,校验和一样,并不意味着校验前的信息是一样的。
这里涉及一个概念的理解,什么是校验和(checksum)
简单理解过来,就是校验和是将输入数据(比如长度为2048bytes)进行了缩短化处理到4 bytes(返回值是int)。
int 范围(-2147483648 ~ 2147483647)尚且有限,那么在各种输入的情况下, 缩短化处理到这个范围,也一定会出现重复(碰撞问题)
SELECT CHECKSUM(9,1.0),CHECKSUM(9,-1.0),BINARY_CHECKSUM(3,4.1),BINARY_CHECKSUM(3,-4.1)
测试一个足够长的例子
DECLARE @k nvarchar(MAX) ='你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,
今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,
今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,
今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,
今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,
今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,今天是星期一你好,
今天是星期一你好,今天是星期一你好,今天是星期一'
select binary_checksum(@k),
binary_checksum(@k + '真的吗?')
注意,最后一列,即便加上了额外的信息,计算出来的校验和也跟之前的一样。又一次碰撞了。
总结:
checksum,binary_checksum 只能用于一种情况:如果校验和不相同,那么输入一定不相同。
反之,则不成立。