MySQL源码学习:关于整型判断的一个bug

问题:

这个bug来源于官方的一个bug报告,感谢@印风_小希 . 现象很容易描述,直接上例子. 5.1以后的版本都有此问题.

CREATE TABLE `tb` (

`a` int(11) DEFAULT NULL,

`b` int(11) DEFAULT NULL,

KEY `a` (`a`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into tb values (1,2),(2,5),(3,8),(4,6);

select * from tb force index (a) where a >=0.5;

+------+------+

| a | b |

+------+------+

| 2 | 5 |

| 3 | 8 |

| 4 | 6 |

+------+------+

4 rows in set (0.00 sec)


(1,2)这个记录没有返回.

说明一下,select语句中用force index是以防全表扫描. (不走索引a就正常了).


原因分析:

MySQL使用索引时,调用innoDB的index_read接口, 需要传入三个信息: 查找的值\索引\查找方向, 由于查询条件是>=, 因此查询方向可选的是HA_READ_AFTER_KEY和 HA_READ_KEY_OR_NEXT,分别对应>和>=.

但是传入之前还要作查询优化.

在这个例子中的流程:

1) MySQL决定使用索引a后, 根据输入的值(0.5),取索引[1, MAX). 之所以取1,是因为a是一个int类型.;

2) 判断 0.5和1的大小, 0.5<1, 因此设置 tree->min_flag= NEAR_MIN; 表示实际要查找的值,小于传入索引范围的最小值.因此决定了使用HA_READ_AFTER_KEY, 也就是>0.5, 这没问题.

3) 不幸的是,由于字段a是整型,真正传入的是1, 逻辑变成>1.

所以查找时a=1这个记录被忽略了.

简单修改:

5.0版本没这个问题,那时候没有HA_READ_AFTER_KEY 和 HA_READ_KEY_OR_NEXT这些东西.

这两个值的差别只是在innoDB内部查找的时候要不要判断相等的那个值,简单的修改可以都处理为HA_READ_KEY_OR_NEXT. 不用担心MySQL把>变成>=以后的后果,MySQL层会再作过滤.

当然上面是偷懒的做法, 比较正规的做法应该是在1)判断大小的时候, 把0.5取整为1,这样判断到1=1, 就会标记为HA_READ_KEY_OR_NEXT.

上一篇:迎接数据中心新纪元!AMD携手产业链合作伙伴召开EPYC™(霄龙)技术峰会


下一篇:深入理解计算机系统cp1:存储单位与编码