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.

猜你喜欢

转载自dinglin.iteye.com/blog/1461470