伍佰目录 短网址
  当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

升级数据库小版本也不省心--发现mysql5.7.18的严重bug(续)

来源:本站原创 浏览:113次 时间:2022-09-18

   源于朋友发邮件提问,前几天意外发现mysql.5.7.18以及目前最新的版本5.7.20 都存在的一个bug,   bug现象如下:

mysql>  SELECT distinct  con_id   FROM user_task  where id in (56,57); 

+--------+

| con_id |

+--------+

|    844 |

+--------+

mysql>  SELECT distinct  con_id   FROM user_task  where id in (56,57,58);

+--------+

| con_id |

+--------+

|    844 |

|    785 |

+--------+

mysql>  SELECT distinct  con_id   FROM user_task  where id in (56,57,58,66,70,71);


Empty set (3 min 0.16 sec)

   查询范围变大, 结果反而没有了。 原因是最后面sql的执行计划跟前面sql的执行计划不一样。  详情请阅读前面两篇文章:

当在mysql5.7上发现这个bug,小心脏不好受了

小心脏平复了--发现mysql5.7的s1(最严重)级bug后第二天

在各位朋友的帮助下,确认这个bug的存在,以及需要在下一个版本5.7.21(未发布)上解决。同时,当前的解决办法是设置优化器参数,设置如下

set  optimizer_switch='use_index_extensions=off';

        到此,这个问题也算圆满解决。但作者发愁是否线上所有的mysql 实例都得修改这个参数?哪些版本有影响(不影响的版本先不动)? 这个bug产生的原因?

         抱着试试看的心态捋代码,还算比较幸运,没花太久的时间找到了下面这个函数(对代码不感兴趣的直接跳到最后看结果,免得厌烦,嘿嘿):

bool Field::is_part_of_actual_key(THD *thd, uint cur_index)

{

  return thd->optimizer_switch_flag(OPTIMIZER_SWITCH_USE_INDEX_EXTENSIONS) ?

    part_of_key.is_set(cur_index) :

    part_of_key_not_extended.is_set(cur_index);

}

  该函数的作用是用来判断当前字段是否存在当前索引中。解析函数体,里面用到了优化器参数OPTIMIZER_SWITCH_USE_INDEX_EXTENSIONS, 也就是我们为了解决遇到的这个bug而需要设置的优化器的一个参数use_index_extensions(还不了解的请阅读前面提到的两篇文章)。  如果该参数为true ,则调用函数part_of_key.is_set(cur_index) 来判断, 如果为false,则调用函数part_of_key_not_extended.is_set(cur_index)来判断。


这个两个函数的作用类似,但有点区别:

part_of_key.is_set(cur_index),该函数不排除主键,用该函数来判断主键字段,永远返回true. 因为主键一定包含在索引中(索引一定会存放主键字段)。 

part_of_key_not_extended.is_set(cur_index),该函数会排除主键字段。如果当前索引是一个二级索引,用来判断主键字段,会返回为false. 


上面信息仅仅作为补充,最关键信息还在后面。

根据这个函数,找到了下面这个代码, 这段代码存在于get_best_group_min_max(PARAM *param, SEL_TREE *tree, const Cost_estimate *cost_est) 函数中

    /*

      If the current storage manager is such that it appends the primary key to

      each index, then the above condition is insufficient to check if the

      index is covering. In such cases it may happen that some fields are

      covered by the PK index, but not by the current index. Since we can't

      use the concatenation of both indexes for index lookup, such an index

      does not qualify as covering in our case. If this is the case, below

      we check that all query fields are indeed covered by 'cur_index'.

    */

    if (pk < MAX_KEY && cur_index != pk &&

        (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))

    {

      /* For each table field */

      for (uint i= 0; i < table->s->fields; i++)

      {

        Field *cur_field= table->field[i];

        /*

          If the field is used in the current query ensure that it's

          part of 'cur_index'

        */

        if (bitmap_is_set(table->read_set, cur_field->field_index) &&

            !cur_field->is_part_of_actual_key(thd, cur_index))

        {

          cause= "not_covering";

          goto next_index;                  // Field was not part of key

        }

      }

    }

    trace_idx.add("covering", true);


该段代码的作用是(主要看for循环体里面的内容),

如果当前字段在必须需要查询的字段中(bitmap_is_set(table->read_set, cur_field->field_index) 为true) ,但该字段不在当前选择的索引中,也就是cur_field->is_part_of_actual_key(thd, cur_index) 为false, 取反后为true, 

然后进入条件体,   cause= "not_covering";   goto next_index;   也就是跳转到“选择另外一个索引“。 

      反之,如果所有的字段都不满足跳转到另外一个索引(goto next_index)的条件,则认为是一个索引覆盖查询,  trace_idx.add("covering", true); 继续走后面的流程。 

     

        而这个bug出现的时候,执行路径就是一个索引覆盖查询,走的是唯一性索引(具体见前面的文章),而后出现了错误的结果。当修改优化器参数之后,cur_field->is_part_of_actual_key(thd, cur_index) 函数返回结果将产生变化,将会选择另外一个路径(goto next_index),从而结果正确。

          这就是设置 optimizer_switch='use_index_extensions=off'; 能够规避这个bug的真正原因。


         (藐视)找到原因,我们来确认一下其他版本是否有这个问题, 因为在bug网站上mysql 研发确认要到5.7.21 才能解决,所以我们来确认一下5.7.17 会不会有这个问题。 然后比较5.7.17 与5.7.18 这段代码


5.7.17 这段代码:


5.7.18这段代码


这两段代码不同的地方:

cur_field->part_of_key_not_clustered.is_set(cur_index)    ----5.7.17调用的函数


cur_field->is_part_of_actual_key(thd, cur_index)    ----5.7.18调用的函数


 从代码上来看,5.7.17 在判断是否是索引覆盖的时候,排除了主键。而5.7.18 版本则根据参数use_index_extensions 是否为false来做排除,如果是true,则不作排除。

     

      因此,貌似这个bug 是从5.7.18开始引入了,5.7.17应该没有这个问题。 

这个信息也让人好尴尬: 好的一方面是5.7.17没有这个问题,不需要修改参数了。但从另一方面来看,就要吐槽了,发布小版本也带来一个大bug, 坑坑坑...... 让使用者不省心!!!

(因为没有现成的5.7.17测试环境,偷懒没有真正实际验证,有空验证一下,如果万一错了,请各位海涵。作者尽快实际验证)。


  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net