站保站

服务市场
  • 网站市场
  • 单机游戏
  • 平台大厅
  • 转让市场
  • 发卡市场
  • 广告市场
  • 下载市场
  • 收录市场
  • 本站平台
    平台客服
    微信Q群



    平台微博/weibo    平台微信/公众号    平台抖音/快手   
    曝光台    保障    地图   
    上传资源 快速赚钱
    站保站    登录      |  注册  |  

    只需一步,快速开始!

     找回密码   |   协议
    热门搜索: 网站开发 App报毒 挖矿源码 代办资质

    Searching rows for update状态初探

    • 时间:2019-08-26 17:49 编辑:老叶茶馆_ 来源: 阅读:839
    • 扫一扫,手机访问
    摘要:

    导读

    作者徐晨亮,MySQL DBA,知数堂学员。热衷于数据库优化,自动化运维及数据库周边工具开发,对MySQL源码有一定的兴趣。


    一、背景说明

    最近有位朋友咨询说为何如此多线程处于Searching rows for update,当时看到这个状态的第一反应就是锁,这里暂且抛开锁不谈,谈一谈为何出现Searching rows for update

    二、实验环境:

    1. root@mysqldb 10:15: [xucl]> show create table test1\G
    2. *************************** 1. row ***************************
    3. Table: test1
    4. Create Table: CREATE TABLE `test1` (
    5. `a` int(11) NOT NULL,
    6. `b` varchar(20) DEFAULT NULL,
    7. PRIMARY KEY (`a`),
    8. KEY `b` (`b`)
    9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    10. 1 row in set (0.00 sec)
    11. root@mysqldb 10:15: [xucl]> select * from test1;
    12. +---+------+
    13. | a | b |
    14. +---+------+
    15. | 2 | b |
    16. | 3 | c |
    17. | 1 | ccc |
    18. +---+------+
    19. 3 rows in set (0.00 sec)

    大概出现的状态如下所示:

    640?wx_fmt=jpeg

    三、初探过程

    简单做了pstack,其中id=2的线程堆栈如下:

    640?wx_fmt=jpeg

    从堆栈很明显可以看出,该线程处于锁等待的状态,但为何处于Searching rows for update并没有给出明确暗示,可以看到调用的顺序是

    1. mysql_parse

    2. mysql_execute_command

    3. Sql_cmd_update::execute_command

    4. mysql_update

    5. ...

    6. ha_innobase::index_read

    7. ...

    8. lock_wait_suspend_thread

    9. ...

    废话不多说,咱们直接从mysql_update切入,该入口函数位置在sql_update.cc

    1. bool mysql_update(THD *thd,
    2. List<Item> &fields,
    3. List<Item> &values,
    4. ha_rows limit,
    5. enum enum_duplicates handle_duplicates,
    6. ha_rows *found_return, ha_rows *updated_return)
    7. {
    8. ...
    9. if (used_key_is_modified || order)
    10. {
    11. /*
    12. When we get here, we have one of the following options:
    13. A. used_index == MAX_KEY
    14. This means we should use full table scan, and start it with
    15. init_read_record call
    16. B. used_index != MAX_KEY
    17. B.1 quick select is used, start the scan with init_read_record
    18. B.2 quick select is not used, this is full index scan (with LIMIT)
    19. Full index scan must be started with init_read_record_idx
    20. */
    21. if (used_index == MAX_KEY || (qep_tab.quick()))
    22. error= init_read_record(&info, thd, NULL, &qep_tab, 0, 1, FALSE);
    23. else
    24. error= init_read_record_idx(&info, thd, table, 1, used_index, reverse);
    25. if (error)
    26. goto exit_without_my_ok;
    27. THD_STAGE_INFO(thd, stage_searching_rows_for_update);
    28. ha_rows tmp_limit= limit;
    29. }
    30. ...

    debug结果如下:

    640?wx_fmt=jpeg

    这里判断条件为used_index是否等于MAX_KEY,其中MAX_KEY为常量,used_index的定义如下:

    used_index= get_index_for_order(order, &qep_tab, limit, &need_sort, &reverse);
    

    这里的判断就比较复杂了,本人水平有限,暂时未深入理解优化器的部分,也不展开说明,源码位置在sql_select.cc,有兴趣的同学可以深入研究一下

    函数is_key_used定义如下:

    1. bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields)
    2. {
    3. bitmap_clear_all(&table->tmp_set); //清空tmp_set位图
    4. table->mark_columns_used_by_index_no_reset(idx, &table->tmp_set); //这里设置位图
    5. const bool overlapping= bitmap_is_overlapping(&table->tmp_set, fields); //比较索引位置和修改位置是否重合
    6. // Clear tmp_set so it can be used elsewhere
    7. bitmap_clear_all(&table->tmp_set);
    8. if (overlapping)
    9. return 1; //如果重合返回1
    10. ...

    640?wx_fmt=jpeg

    看到debug的结果变量,used_key_is_modified为true,那么进入如下判断

    640?wx_fmt=jpeg

    然后就进入stage_searching_rows_for_update状态,也就是我们一开始在show processlist中看到的状态

    640?wx_fmt=jpeg

    而如果我们修改的是其他字段,那么进入的状态便是Updating,对应的源码为

    1. if (used_key_is_modified || order)
    2. {
    3. ...
    4. }
    5. ...
    6. thd->count_cuted_fields= CHECK_FIELD_WARN;
    7. thd->cuted_fields=0L;
    8. THD_STAGE_INFO(thd, stage_updating);
    9. ...

    废话不多说,我们来测试验证一下THD_STAGE_INFO(thd, stage_updating);处打上断点,然后更新数据

    640?wx_fmt=jpeg

    实验结果符合预期

    其他测试结果:

    case结果
    update主键直接进入stage_updating
    update唯一索引直接进入stage_updating
    update普通二级索引+limit进入stage_searching_rows_for_update,完成后进入stage_updating

    四、总结

    最后总结一下:

    1. Searching rows for update状态出现的要求比较严格,当进行数据更新时,如果更新的字段为当前执行计划用到的索引,并且该索引属于普通二级索引(不能是主键也不能是唯一索引),那么就会出现Searching rows for update状态,正因为出现了锁等待,所以能看到这种状态

    2. 如果不是,那么直接进入Updating状态,这个状态也就是我们经常看到的状态

    3. 出现如上两种状态并且持续时间长,并不是造成数据库性能下降的根本原因,而应该考虑其他原因,如本案例中的锁等待问题

    作者寄语:由于本人对优化器部分研究并不深入,并未列出全部情况,待后续慢慢补充,也欢迎各位在留言区补充

    END


    640?wx_fmt=png

    640?wx_fmt=png

    640?wx_fmt=gif

    扫码加入MySQL技术Q群

    (群号:529671799)

       

    640?wx_fmt=jpeg

    • 全部评论(0)
    • 最新

    信息加载中,请等待

    微信客服(速回)

    微信客服(慢回)



    企业微信客服二维码
    联系我们
    平台客服: 平台QQ客服

    平台电话:400电话迁移中!

    平台邮箱:28292383@qq.com

    工作时间:周一至周五:早10:00 晚:18:00

    营业执照     网站ICP备案:鲁ICP备20027607号-1     鲁公网安备:37068702000078号     增值电信业务经营许可证、在线数据与交易处理业务许可证:鲁B2-20200681      © 2016-2024 站保站  https://www.zhanbaozhan.com/ 版权所有!      平台规范:   关于我们   广告合作   隐私条款   免责声明   法律声明   服务条款   网站地图   平台工单!