关于MySQL count(distinct) 逻辑的一个bug【转】
本文来自:http://dinglin.iteye.com/blog/1976026#comments
背景
客户报告了一个count(distinct)语句返回结果错误,实际结果存在值,但是用count(distinct)统计后返回的是0。将问题简化后复现如下,影响已知的所有版本。
这里的 set tmp_table_size=1024; 一定是在插入前设置,这样下面的操作就是按照这个大小进行的,最终出现错误的结果,解决办法:
1,开始前设置足够大的tmp_table_size(推荐);
2,设置成1024,在不修改tmp_table_size 的前提下,设置 set sql_big_tables=1。
测试:
drop table if exists tb; set tmp_table_size=1024;
#set sql_big_tables=1;
create table tb(id int auto_increment primary key, v varchar(32)) charset=gbk; insert into tb(v) values("aaa"); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); update tb set v=concat(v, id); select count(distinct v) from tb; 返回0
上述中update语句的目的是将所有的v值设为各不相同。
原因分析
Count(distinct f)的语义就是计算字段f的去重总数,计算流程大致如下:
流程一:
1、 构造一个unique集合A1(用tree实现)
2、 对每个值都试图插入集合A1中
3、 若和A1中现有item重复则直接跳过,不重复则插入并+1
4、 完成后计算集合中元素个数。
细心的同学会看到上面的语句中有一个set tmp_table_size的过程,集合A1并不能无限扩大,大小上限为tmp_table_size。若超过则上述流程变为
流程二:
1、 构造一个unique 集合A1
2、 插入item过程中若大小超过tmp_table_size,则将A1暂时写到文件中,再构造集合A2
3、 重复步骤2直到所有的item插入完成。因此若item很多则可能重复生成多个集合A1~An。
4、 对A1~An作合并操作。由于只是每个集合A保证unique,因此需要做类似归并排序的操作(实际上不需要排序,只是扫一遍)
5、 因此合并操作需要一个临时内存,长度为n,单元大小为key_length (key大小)。这个临时内存,用的也是tmp_table_size定义的大小。实际上在合并过程中还需要长为key_length的预留空间作临时内存保存。因此需要的空间为 (n+1)*key_length。
6、 在进行合并前会判断tmp_table_size >=(n+1)*key_length, 不满足则直接放弃合并。其结果就是返回为0。
案例分析
以上面这个case为例。字段v的单key大小为65 (65 = 32*2+1) 加上tree节点字占空间24字节共89字节。单个集合只能放11个item (1024/89), 因此n为 24 (24>=256/11), 在合并时需要 (24+1)*65= 1625字节的临时空间,大于1024,放弃合并。
Sql_big_tables
实际上在最初处理这个问题时,俊达同学发现社区也有人讨论这个bug,并且指出在set sql_big_tables=on的时候,执行count(distinct)就能正确返回结果。原因就是在sql_big_tables=on的情况下,构造集合的方式是直接生成一个临时表,全部插入后直接计算临时表的大小作为结果,整个过程与tmp_table_size无关。
解决方法
运维上,set sql_big_tables是一个方法,不过会影响性能。调高tmp_table_size算是正招。当然本质上这是一个bug。代码上,对于已经走到合并操作的这个逻辑,如果tmp_table_size不够,应该直接申请新的临时空间用于合并,完成后释放。虽然会造成临时征用内存,不过以现有的逻辑来看,临时征用的内存已经不少了-_-
另外一种时间换空间的方法,就是作多次合并。 相比之下第一种改造比较简单安全,提交的patch用第一种思路完成。后面看看社区有没有别的方案。
关于MySQL count(distinct) 逻辑的一个bug【转】的更多相关文章
- mysql count distinct 统计结果去重
1.使用distinct去重(适合查询整张表的总数)有多个学校+教师投稿,需要统计出作者的总数select count(author) as total from files每个作者都投稿很多,这里有 ...
- MySQL 5.7.13 的一个BUG
mysql今天从5.6切到5.7,在测试环境中,日志是全部打印的,发现打了一个警告: Incorrect string value: '\xD6\xD0\xB9\xFA\xB1\xEA...' for ...
- MySQL关于exists的一个bug
今天碰到一个很奇怪的问题,关于exists的, 第一个语句如下: SELECT ) FROM APPLY t WHERE EXISTS ( SELECT r.APPLY_ID FROM RECORD ...
- Mysql中count(*),DISTINCT的使用方法和效率研究
在处理一个大数据量数据库的时候 突然发现mysql对于count(*)的不同处理会造成不同的结果 比如执行 SELECT count(*) FROM tablename 即使对于千万级别的数据mysq ...
- 记录Window系统下myeclipes连接linux下mysql所出现的一个bug
记录myeclipes远程连接mysql所出现的一个bug 今天在玩框架hibernate时,出现一个非常费解的bug,话不多说,先看bug Access denied for user 'root' ...
- mysql查询不重复的行内容,不重复的记录数.count,distinct
有这么一个表 记录了id, p_id, p_name , p_content , p_time 1 343 aaa aaaaaa 2012-09-01 2 ...
- php查询mysql时,报超出内存错误(select count(distinct))时
学时服务器查询教练所带人数时,使用select count(distinct(u_STRNO))时报超出内存错误.后参考“mysqld-nt: Out of memory解决方法”http://jin ...
- MySQL游标循环取出空值的BUG
早上同事要我写个MySQL去除重复数据的SQL,想起来上次写过一篇MySQL去除重复数据的博客,使用导入导出加唯一索引实现的,但是那种方式对业务影响较大,所以重新写一个存储过程来删重复数据,这一写就写 ...
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
随机推荐
- xinetd
最简安装centos6.4时,xinetd服务是没有安装的,只是在/etc下有xinetd.d目录, 没有xinetd.conf这个配置文件 xinetd is a secure replacemen ...
- Kmeans算法的K值和聚类中心的确定
0 K-means算法简介 K-means是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一. K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类.通过迭代的 ...
- 第二章平稳时间序列模型——AR(p),MA(q),ARMA(p,q)模型及其平稳性
1白噪声过程: 零均值,同方差,无自相关(协方差为0) 以后我们遇到的efshow如果不特殊说明,就是白噪声过程. 对于正态分布而言,不相关即可推出独立,所以如果该白噪声如果服从正态分布,则其还将 ...
- MyEclipse 自动提示设置
window --> Perferences--> General--> keys Content Assist默认的是Ctrl +space Content Assist快捷键设置 ...
- php数据结构与算法
php面试题之二--数据结构和算法(高级部分) 二.数据结构和算法 1.使对象可以像数组一样进行foreach循环,要求属性必须是私有.(Iterator模式的PHP5实现,写一类实现Iterator ...
- Codeforces Round #335 Sorting Railway Cars 动态规划
题目链接: http://www.codeforces.com/contest/606/problem/C 一道dp问题,我们可以考虑什么情况下移动,才能移动最少.很明显,除去需要移动的车,剩下的车, ...
- iOS开发——UI基础-UIButton、UIImageView、UILabel的选择
1.UILabel - UILabel的常见属性 @property(nonatomic,copy) NSString *text; 显示的文字 @property(nonatomic,retain) ...
- Github如何删除repository(仓库)
首先就是你的Github主页了. 第二步点击进入一个repository(仓库) 第三步点击右上的setting 将此页面滑动到最下面找个这个 点击删除即可!
- pro*c添加SQLCHECK后编译报错PLS-S-00201
如果在pro*c中调用数据库了里的函数,就需要在proc的cfg配置文件中添加一行: SQLCHECK=SEMANTICS 但是添加之后又会出现PLS-S-00201错误,原因在与添加SQLCHECK ...
- 取数据的前N行
用awk中csv文件中取前1000行出来,代码虽少,很容易出错 BEGIN{ FS=","; OFS=","; i=; } { i++; )exit; prin ...