MySQL源码学习:关于 'A' =='A '的问题

  昨天一位同事问到一个问题,他的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) values("A ");

在执行第二个insert的时候,会报主键冲突。

原因:

gbk字符串判断时,用的是这个函数 my_strnncollsp_gbk (strings/ctype-gbk.c)

简单逻辑如下

size_t length= min(a_length, b_length);

int res= my_strnncoll_gbk_internal(&a, &b, length);

#ifndef VARCHAR_WITH_DIFF_ENDSPACE_ARE_DIFFERENT_FOR_UNIQUE
  diff_if_only_endspace_difference= 0;
#endif

  if (!res && a_length != b_length)
{
    if (diff_if_only_endspace_difference)
       res= 1;
    ....
}
return  res;

从代码上看,没有什么问题,逻辑是先用比较小的算出长度,然后作字符串对比,此时认为相同。然后根据宏定义,如果没有加编译参数VARCHAR_WITH_DIFF_ENDSPACE_ARE_DIFFERENT_FOR_UNIQUE ,则diff_if_only_endspace_difference为0,因此当两个字符串只区别于末尾的空格个数时,认为字符串相同。

(不用担心'A' 和 'AB'对比的情况, 省略号部分作了这个判断的)

但是这个在5.0中新增的编译参数并没有用!!

原因是InnoDB在调用这个函数的时候,diff_if_only_endspace_difference传的就是0。

也就是说,你就算加了这个编译参数,行为并没有改变, 没有把这个值设置为1的代码 。

方案:

a) 比较优美的方案是是把InnoDB中调用处传入的diff_if_only_endspace_difference, 也改成按照VARCHAR_WITH_DIFF_ENDSPACE_ARE_DIFFERENT_FOR_UNIQUE 来决定0还是1。

b) 比较简单的方案是将上面代码中的宏定义这段改写成意思相同的ifdef写法,

#ifdef VARCHAR_WITH_DIFF_ENDSPACE_ARE_DIFFERENT_FOR_UNIQUE
  diff_if_only_endspace_difference= 1;
#endif
时间: 2025-01-15 20:24:53

MySQL源码学习:关于 'A' =='A '的问题的相关文章

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

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

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源码学习: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源码学习: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源码学习:索引使用统计功能

今天刚刚知道Oracle有个索引统计的功能,可以统计每个索引的使用次数.作为一个Oracle的门外汉,我还是再次感叹人家做的是真细致.第二个想法就是给MySQL也加上. Percona版本的information_schema.innodb_index_stats 已经有索引的统计信息,我们就在巨人的肩膀上踩一脚了. 先来看下原来的表结构. CREATE TEMPORARY TABLE `INNODB_INDEX_STATS` ( `table_schema` varchar(192) NOT

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

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

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 t

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->