`
丁林.tb
  • 浏览: 798065 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

关于MySQL count(distinct) 逻辑的一个bug

阅读更多
 背景

 

         客户报告了一个count(distinct)语句返回结果错误,实际结果存在值,但是用count(distinct)统计后返回的是0

         将问题简化后复现如下,影响已知的所有版本。

 

drop table if exists tb;

set tmp_table_size=1024;

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很多则可能重复生成多个集合A1An

4、  A1An作合并操作。由于只是每个集合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字节。单个集合只能放11item 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用第一种思路完成。后面看看社区有没有别的方案。

2
0
分享到:
评论
3 楼 cwqcwqmax9 2013-12-02  
丁林.tb 写道
cwqcwqmax9 写道
n为 24 (24>=256/11  ,请问这个256怎么得来的啊


就是case中的256行记录


原来如此啊  ,也就是说记录超过256就会复现该Bug啊!谢谢分享啊!
2 楼 丁林.tb 2013-12-01  
cwqcwqmax9 写道
n为 24 (24>=256/11  ,请问这个256怎么得来的啊


就是case中的256行记录
1 楼 cwqcwqmax9 2013-11-29  
n为 24 (24>=256/11  ,请问这个256怎么得来的啊

相关推荐

    分析MySQL中优化distinct的技巧

    在MySQL数据库中,优化`DISTINCT`操作是一个关键的性能提升策略,特别是在处理大量数据时。上述场景中,用户遇到了一个问题:对一个10G以上的单表`user_access_xx_xx`执行`SELECT COUNT(DISTINCT nick)`以统计唯一...

    MongoDB教程之聚合(count、distinct和group)

    在MongoDB中,你可以直接调用`db.collection.count()`来获取一个集合中的所有文档数量,或者提供一个查询对象作为参数来统计满足该条件的文档数。例如,以下代码展示了如何使用`count`: ```bash # 查询空集合的...

    MySQL中distinct和count(*)的使用方法比较

    对于 `GROUP_CONCAT` 函数,它在 MySQL 4.1 及更高版本中可用,可以将多列的不重复值组合为一个字符串,但请注意,这将不再返回单个记录,而是组合后的结果。 其次,`COUNT(*)` 用于统计表中的行数。这个函数对全表...

    【DISTINCT】优化之MySQL官方文档翻译

    当`DISTINCT`与`ORDER BY`一起使用时,MySQL通常需要创建一个临时表来存储中间结果。这个过程可能会消耗大量的内存资源,并且可能导致查询执行时间变长。为了提高效率,MySQL会尽可能地避免创建临时表,特别是在以下...

    mysql中distinct用法【SQL中distinct的用法】.docx

    下面是一个简单的示例: ``` SELECT DISTINCT name FROM A; ``` 这条语句将返回表 A 中 name 列的所有唯一值。如果表 A 中有重复的 name 值,这条语句将忽略这些重复值,返回的结果将是 name 列的所有唯一值。 ...

    MySQL中索引优化distinct语句及distinct的多字段操作

    本文涉及一个不能利用索引完成DISTINCT操作的实例.   实例1 使用索引优化DISTINCT操作 create table m11 (a int, b int, c int, d int, primary key(a)) engine=INNODB; insert into m11 values (1,1,1,1),(2,2,2,...

    用Distinct在MySQL中查询多条不重复记录值,绝对的物有所值

    可以看到,虽然`name`列中有重复值,但通过`COUNT(DISTINCT name)`,我们可以准确地知道每个名字的出现次数为1,这是`DISTINCT`与`GROUP BY`完美配合的一个实例。 ### 结合`GROUP_CONCAT`处理多个字段 在复杂的...

    小度写范文【SQL中distinct的用法】mysql中distinct用法模板.pdf

    在SQL查询中,`DISTINCT`关键字是一个非常重要的部分,它允许我们从查询结果中去除重复的记录,只返回唯一的、不同的值。在MySQL中,`DISTINCT`的使用方式非常灵活,可以从单个列到多个列进行操作,也可以与聚合函数...

    完美解决distinct中使用多个字段的方法

    完美解决distinct中使用多个字段的方法,完美解决distinct中使用多个字段的方法完美解决distinct中使用多个字段的方法完美解决distinct中使用多个字段的方法完美解决distinct中使用多个字段的方法

    count_distinct:在PostgreSQL中扩展以COUNT(DISTINCT ...)聚合的替代方法

    总计COUNT_DISTINCT 此扩展提供了COUNT(DISTINCT ...)的替代方法,该方法对于大量数据通常会以排序和不良性能而告终。 职能 有两个多态聚合函数,用于处理按值传递的固定长度数据类型(即,在64位计算机上最多为...

    小度写范文【SQL中distinct的用法】mysql中distinct用法模板.docx

    但需要注意的是,`COUNT(DISTINCT)`仅能用于一个字段,不是所有数据库系统都支持对多个字段进行统计,如Access。如果需要对多列进行统计,可以使用嵌套查询,如下: ```sql SELECT COUNT(*) FROM (SELECT ...

    alibaba-developer#MaxCompute#解决MaxCompute SQL count distinct多个字段

    解决MaxCompute SQL count distinct多个字段的方法按照惯性思维,统计一个字段去重后的条数我们的sql写起来如下:Distinct的作用

    MySQL DISTINCT 的基本实现原理详解

    例如,当查询只包含DISTINCT关键字和一个被索引的列时,MySQL会遍历索引,对每个不同值形成一个组,然后返回每个组的第一个记录。在EXPLAIN计划中,`Extra`字段会显示"Using index for group-by",这意味着MySQL利用...

    解析mysql不重复字段值求和

    其原因是distinct只能返回它的目标字段,而无法返回其它字段,这个问题让我困扰了很久,用distinct不能解决的话,我只有用二重循环查询来解决,而这样对于一个数据量非常大的站来说,无疑是会直接影响到效率的。...

    vc 小技巧 mysql distinct 语句

    `DISTINCT`是SQL语言中的一个关键字,用于从查询结果中消除重复的行。当你执行一个`SELECT DISTINCT`语句时,数据库将返回所有不重复的唯一值。例如,假设我们有一个包含员工信息的`employees`表,其中`name`字段...

    Mysql中distinct与group by的去重方面的区别

    例如,如果你有一个包含员工姓名的表,`SELECT DISTINCT name FROM employees` 将返回所有不同的员工姓名,忽略重复。对于多列去重,`DISTINCT` 同样有效,如 `SELECT DISTINCT name, department FROM employees` 将...

    mysql-常见问题,索引优化

    1. 关于MySQL `count(distinct)` 的逻辑bug 在执行`count(distinct)`查询时,MySQL可能会遇到性能问题,尤其是在处理大量数据时。这是因为MySQL在计算唯一值时没有利用索引,导致全表扫描。为优化此操作,可以尝试...

    MySQL关键字Distinct的详细介绍

    例如,如果你有一个包含员工姓名的表,而你想找出所有不重复的员工姓名,你可以这样写: ```sql SELECT DISTINCT name FROM employees; ``` 上述语句会返回表`employees`中所有唯一的`name`值,重复的名字只会...

Global site tag (gtag.js) - Google Analytics