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.

时间: 2024-09-14 03:29:18

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

MySQL源码学习: concat + outfile的bug 原因分析

项目中碰到一个bug,需要将MySQL表中的数据导出,字段中间用逗号隔开. 1.复现 步骤: 版本 5.1.48 a) 准备数据 CREATE TABLE `test` ( `id` int(11) DEFAULT NULL, `data` char(10) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=gbk; insert into tad2 values (1,'丁\\奇'); b) select concat(id, data) from te

Mysql源码学习笔记 偷窥线程_Mysql

感觉代码有些凌乱,注释代码都写的比较随意,好像没有什么统一的规范,不同的文件中代码风格也有差异,可能Mysql经过了很多牛人的手之后,集众牛人之长吧.也可能是我见识比较浅薄,适应了自己的代码风格,井底之蛙了,总之还是怀着敬畏的心情开始咱的源码之旅吧.本人菜鸟,大神轻拍. Mysql可以启动起来了,应该怎么学习呢?总不能从main开始一步一步的看吧,Mysql作为比较底层的大型软件,涉及到数据库实现的方方面面,没有厚实的数据库理论基础和对Mysql各个模块相当的熟悉,从main开始势必会把自己引入

MySQL源码学习:ib_logfile、bin-log与主从同步

今天研究MySQL主从同步的同事问了一个问题,如果InnoDB写完ib_logfile后,服务异常关闭.会不会由于主库能够根据ib_logfile恢复数据,而由于bin-log没写导致从库同步时少了这个事务?或者反之,bin-log写成功,而ib_logfile没有写完,导致从库执行事务,而主库不执行? 这会导致主从不一致. 本文简要说明下这个问题. 1. 写入流程 源码sql/handler.cc: ha_commit_trans { - if ((err= ht->prepare(ht, t

MySQL源码学习:关于慢查询日志中的Rows_examined=0

最近在一个项目中DBA同学问了一个问题:为什么很多慢查询日志中显示 Rows_examined : 0? 需要说明的是, 这类慢查询语句都是类似 select count(*) from (-)t; 在说明这个问题之前,我们先指出两个相关背景: 1.MySQL的临时表,都是MyISAM的. 2.MyISAM表中的记录总数是额外存储的,count(*)的时候不需要遍历数据. 3.把count(*)转换为取一个const值这件事情,是在优化(optimize)阶段作的. 问题分析: 这个值对应于代码

MySQL源码学习:MySQL中禁止跨库访问的实现

 先说一下这里"跨库"的意思:当前use的是db1, 仍可以使用select * from db2.table1来访问table1表. 这样使得我们需要访问同一个MySQL下的其他表时不需要多一次use,也使得多个库间的表join这样的操作成为可能. 1. 问题背景 但有些使用场景下是有禁掉这种功能的需求.比如一些开放应用托管服务,一般给一个应用指定使用一种类型的db, 多个用户使用相同的应用,但每个用户访问自己的db.由于有复用连接的需求,使得不能给连接的mysqluser作库权限限

MySQL源码学习:关于 &#039;A&#039; ==&#039;A &#039;的问题

  昨天一位同事问到一个问题,他的MySQL中导入数据的时候,发现唯一索引冲突,原因是有两行记录,区别只是有一条记录多了最后的一个空格. 希望有方法将他们设置不同. 复现: CREATE TABLE `t` ( `c` varchar(20) NOT NULL DEFAULT '', PRIMARY KEY (`c`) ) ENGINE=InnoDB DEFAULT CHARSET=gbk; insert into t(c) values("A"); insert into t(c)

MySQL源码学习:简述InnoDB的BP LRU策略

本文简要说明InnoDB的Buffer Pool(BP)的结构.基本运行方式和策略. 1.LRU的基本形态 由于涉及到淘汰机制,Buffer Pool (BP)内需要一个LRU链.这个LRU链表的基本形态如下: 从图中看到,LRU是一个链表(双向,图中没有画出反向指针). 同时有一个LRU_old(buf_pool->LRU_old)指针指向链表中间的一个page. LRU_old指向的page及之后直到end的page,都被称为"old page", 内存中bpage->

MySQL源码学习:InnoDB的ib_logfile写入策略

ib_logfile是InnoDB的事务日志文件.本文简要说明其写入时机.写入策略及如何保证数据安全. 1. 基本概念 a) ib_logfile文件个数由innodb_log_files_in_group配置决定,若为2,则在datadir目录下有两个文件,命令从0开始,分别为ib_logfile0和ib_logfile. b) 文件为顺序写入,当达到最后一个文件末尾时,会从第一个文件开始顺序复用. c) lsn: Log Sequence Number,是一个递增的整数. Ib_logfil

MySQL源码学习:InnoDB关于group commit的简单QA

    前天同事问了个问题,今天又再翻了下group commit.关于这个话题Kristian Nielsen有一个很详尽的系列文章(http://kristiannielsen.livejournal.com/12254.html), 有四个页面,文中有链接.这里列出一些细节,主要是对上面文章补充一下. Q:什么是group commit. A:1) 简单说就是:好几个线程写文件,然后一个线程fsync: 2) 只有事务日志(ib_logfile)用到: 3) 注意是多个线程(多用户).一个