MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

04-23 1631阅读

2)被kill线程响应kill命令

被kill线程感知(响应)kill命令主要有两种方式:

  • 主动检查: c onne ction1在一些 代 码处会去主动检查killed状态 ;

  • 被通过信号量唤醒: connection1在执行某些命令时(如引擎层去做一些操作),会主动去await一个condition,释放掉相应的锁,connection2执行kill命令时,会通 过锁和condition唤醒connection1,执行终止操作;

    killed状态:

    enum killed_state

    {

    NOT_KILLED=0, KILL_BAD_DATA=1,

    KILL_CONNECTION=ER_SERVER_SHUTDOWN, KILL_QUERY=ER_QUERY_INTERRUPTED, KILL_TIMEOUT=ER_QUERY_TIMEOUT,

    KILLED_NO_VALUE /* means neither of the states */

    };

    3)connection真正被kill掉

    真正被kill指的是show processlist看不到这个线程的时机。mysql在新建一个connection之后,会不断的去监听连接(do_command),前面提到kill时会主动把连接的socket关闭(shutdown_active_vio)。所以真正连接终止的逻辑是在此处,判断thd_connection_alive的状态是待杀死之后,会关闭connection,并且release_resources,此时再去show processlist,则killed的线程才会消失。相应的pthread也会等待其他连接复用。

    killed状态 :

    if (thd_prepare_connection(thd))

    handler_manager->inc_aborted_connects();

    else

    {

    while (thd_connection_alive(thd))

    {

    if (do_command(thd)) break;

    }

    end_connection(thd);

    }

    close_connection(thd, 0, false, false);

    thd->get_stmt_da()->reset_diagnostics_area();

    thd->release_resources();

    // Clean up errors now, before possibly waiting for a new connection.

    #if OPENSSL_VERSION_NUMBER

    ERR_remove_thread_state(0);

    #endif /* OPENSSL_VERSION_NUMBER

    thd_manager->remove_thd(thd);

    Connection_handler_manager::dec_connection_count();

    channel_info= Per_thread_connection_handler::block_until_new_connection();

    //等待新连接复用线程

    3、被KILL线程主动检查点和唤醒机制分析

    由前面kill过程分析,大致可以分为两种情况,一种是connection1代码一直在执行中(占据cpu),那么总会执行到某些地方可以检查thd->killed状态,另外一种是connection1线程wait状态,需要其他线程通过信号量唤醒connection1的线程,实现kill中断目的。具体地,这两类又可以分为下面4种情况:

    1)第一类:主动检查断点

    ① connection2发送kill命令时,connection1已执行完命令 (主动检查)

    此时connection1阻塞在socket_read上,由于前面提到connection2会去shutdown_active_vio,connection1很容易感知到,执行后续操作,如回滚等。

    if (thd_prepare_connection(thd))

    handler_manager->inc_aborted_connects();

    else

    {

    while (thd_connection_alive(thd))

    {

    if (do_command(thd))

    break;

    }

    end_connection(thd);

    }

    close_connection(thd, 0, false, false);

    thd->get_stmt_da()->reset_diagnostics_area();

    thd->release_resources(); //clean_up

    ②内部innodb引擎在获取记录时,也会去判断thd->killed状态,决定是否中断操作,进行返回。

    这一类检查点很多。如下面两处:

    • select每行读取都会检查;

      int rr_sequential(READ_RECORD *info)

      {

      int tmp;

      while ((tmp=info->table->file->ha_rnd_next(info->record)))

      {

      /*

      ha_rnd_next can return RECORD_DELETED for MyISAM when one thread is reading and another deleting without locks.

      */

      if (info->thd->killed || (tmp != HA_ERR_RECORD_DELETED))

      {

      tmp= rr_handle_error(info, tmp); break;

      }

      }

      return tmp;

      }

      MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

      • 内部innodb引擎在获取记录时,也会去判断thd->killed状态,决定是否中断操作,进行返回。

      • frame #0: 0x0000000106629a7e mysqld`trx_is_interrupted(trx=0x00007f8266dbd8a0) at ha_innodb.cc:3234:9

        frame #1: 0x000000010686d901 mysqld`row_search_mvcc(buf=", mode=PAGE_CUR_G, prebuilt=0x00007f826b81faa0,

        match_mode=0, direction=0) at row0sel.cc:5245:6

        frame #2: 0x0000000106636cda mysqld`ha_innobase::index_read(this=0x00007f825c8c1430, buf=", key_ptr=0x0000000000000000, key_len=0, find_flag=HA_READ_AFTER_KEY) at ha_innodb.cc:8768:10

        frame #3: 0x000000010663798c mysqld`ha_innobase::index_first(this=0x00007f825c8c1430, buf=") at ha_innodb. cc:9186:14

        frame #4: 0x0000000106637c2a mysqld`ha_innobase::rnd_next(this=0x00007f825c8c1430, buf=") at ha_innodb.cc: 9284:11

        frame #5: 0x000000010563f7a5 mysqld`handler::ha_rnd_next(this=0x00007f825c8c1430, buf=") at handler.cc:2963:

        3

        frame #6: 0x0000000105d954d4 mysqld`rr_sequential(info=0x00007f826780d208) at records.cc:517:34

        frame #7: 0x0000000105e85c78 mysqld`join_init_read_record(tab=0x00007f826780d1b8) at sql_executor.cc:2504:10

        frame #8: 0x0000000105e82a1c mysqld`sub_select(join=0x00007f825e37a4d0, qep_tab=0x00007f826780d1b8,

        end_of_records=false) at sql_executor.cc:1284:14

        frame #9: 0x0000000105e7f299 mysqld`do_select(join=0x00007f825e37a4d0) at sql_executor.cc:957:12

        frame #10: 0x0000000105e7ec26 mysqld`JOIN::exec(this=0x00007f825e37a4d0) at sql_executor.cc:206:10

        frame #11: 0x0000000105f5fe90 mysqld`handle_query(thd=0x00007f825e35ec00, lex=0x00007f825e361058,

        result=0x00007f825e379cf8, added_options=0, removed_options=0) at sql_select.cc:191:21

        frame #12: 0x0000000105f006f7 mysqld`execute_sqlcom_select(thd=0x00007f825e35ec00,

        all_tables=0x00007f825e3796b0) at sql_parse.cc:5155:12

        frame #13: 0x0000000105ef6527 mysqld`mysql_execute_command(thd=0x00007f825e35ec00, first_level=true) at sql_parse.cc:2826:12

        frame #14: 0x0000000105ef3d62 mysqld`mysql_parse(thd=0x00007f825e35ec00, parser_state=0x00007000097d5340) at sql_parse.cc:5584:20

        frame #15: 0x0000000105ef0bf0 mysqld`dispatch_command(thd=0x00007f825e35ec00, com_data=0x00007000097d5e78, command=COM_QUERY) at sql_parse.cc:1491:5

        frame #16: 0x0000000105ef2e70 mysqld`do_command(thd=0x00007f825e35ec00) at sql_parse.cc:1032:17

        frame #17: 0x0000000106081976 mysqld`::handle_connection(arg=0x00007f82681d84f0) at

        connection_handler_per_thread.cc:313:13

        frame #18: 0x0000000106a4c74c mysqld`::pfs_spawn_thread(arg=0x00007f8268012fc0) at pfs.cc:2197:3

        frame #19: 0x00007fff734b6109 libsystem_pthread.dylib`_pthread_start + 148

        frame #20: 0x00007fff734b1b8b libsystem_pthread.dylib`thread_start + 15

        MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

        2)第二类:需要其他线程通过信号量唤醒

        ① connection2发送kill命令时,connection1处于innodb层wait行锁状态

        主要通过awake中的下面这行触发唤醒 (也可能由系统的后台线程lock_wait_timeout_thread唤醒)

        /* Interrupt target waiting inside a storage engine. */

        if (state_to_set != THD::NOT_KILLED)

        ha_kill_connection(this);

        参 考 下 面 debug 分 析 的 case3 。

        ② connection2发送kill命令时,connection1处于msyql层wait状态(由connection2唤醒)

        主要通过下面的方法实现唤醒:

        /* Broadcast a condition to kick the target if it is waiting on it. */

        if (is_killable)

        {

        mysql_mutex_lock(&LOCK_current_cond);

        if (current_cond && current_mutex)

        {

        DBUG_EXECUTE_IF(“before_dump_thread_acquires_current_mutex”,

        {

        const char act[]=

        “now signal dump_thread_signal wait_for go_dump_thread”;

        DBUG_ASSERT(!debug_sync_set_action(current_thd,

        STRING_WITH_LEN(act)));

        }😉;

        mysql_mutex_lock(current_mutex);

        mysql_cond_broadcast(current_cond); mysql_mutex_unlock(current_mutex);

        }

        mysql_mutex_unlock(&LOCK_current_cond);

        参考下面debug分析的case4。

        三、原因总结


        通过上面代码分析可以得知,kill之后会进行回滚操作(大事务)或清理临时表(比如较慢的ddl),都有可能导致长时间处于killed状态。

        具体回滚是上面提到的handle_connection中的thd->release_resources()中执行clean_up进行回滚或者在sql_parse中trans_rollback_stmt中。

        MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

        除开回滚操作的影响,如果本身mysql机器负载较高,一样会导致主动检查thd->killed会有延迟或者影响线程的唤醒调度。

        四、案例复现


        1、本地debug复现

        1)case 1

        connection1已执行完命令,connection2去kill连接1

        (较为简单,略)

        2)case2

        connection1正在执行(如parse阶段,还没有真正到innodb层),connection2去kill连接1

        (较为简单,略)

        3)case3(innodb层唤醒)

        新起一个session作为connection0开启事务,update某一行row1

        再起一个session作为connection1 update同一行row1

        • thread #29, stop reason = breakpoint 127.1

        • frame #0: 0x00000001060f87cb mysqld`os_event::wait(this=0x00007fcce2b16598) at os0event.cc:180:4

          frame #1: 0x00000001060f8773 mysqld`os_event::wait_low(this=0x00007fcce2b16598, reset_sig_count=1) at

          os0event.cc:366:3

          frame #2: 0x00000001060f91ed mysqld`os_event_wait_low(event=0x00007fcce2b16598, reset_sig_count=0) at os0event.cc:611:9

          frame #3: 0x00000001060a9332 mysqld`lock_wait_suspend_thread(thr=0x00007fccd90936f0) at lock0wait.cc:315:2

          frame #4: 0x00000001061b4f77 mysqld`row_mysql_handle_errors(new_err=0x0000700009f3af6c,

          trx=0x00007fcce1cc0ce0, thr=0x00007fccd90936f0, savept=0x0000000000000000) at row0mysql.cc:783:3

          frame #5: 0x0000000106212fa6 mysqld`row_search_mvcc(buf=", mode=PAGE_CUR_GE, prebuilt=0x00007fccd9092ea0, match_mode=1, direction=0) at row0sel.cc:6292:6

          frame #6: 0x0000000105fd8cda mysqld`ha_innobase::index_read(this=0x00007fccd9091430, buf=“, key_ptr=”\x1e", key_len=514, find_flag=HA_READ_KEY_EXACT) at ha_innodb.cc:8768:10

          frame #7: 0x0000000104ff2c67 mysqld`handler::index_read_map(this=0x00007fccd9091430, buf=“, key=”\x1e", keypart_map=1, find_flag=HA_READ_KEY_EXACT) at handler.h:2824:13

          frame #8: 0x0000000104fe1f14 mysqld`handler::ha_index_read_map(this=0x00007fccd9091430, buf=“, key=”\x1e", keypart_map=1, find_flag=HA_READ_KEY_EXACT) at handler.cc:3047:3

          frame #9: 0x0000000104feeb62 mysqld`handler::read_range_first(this=0x00007fccd9091430, start_key=0x00007fccd9091518, end_key=0x00007fccd9091538, eq_range_arg=true, sorted=true) at handler.cc:7412:13

          frame #10: 0x0000000104fec01b mysqld`handler::multi_range_read_next(this=0x00007fccd9091430, range_info=0x0000700009f3bc10) at handler.cc:6477:15

          frame #11: 0x0000000104fed4ba mysqld`DsMrr_impl::dsmrr_next(this=0x00007fccd9091698, range_info=0x0000700009f3bc10) at handler.cc:6869:24

          frame #12: 0x0000000105fee7f6 mysqld`ha_innobase::multi_range_read_next(this=0x00007fccd9091430, range_info=0x0000700009f3bc10) at ha_innodb.cc:20585:18

          frame #13: 0x00000001056ec7c8 mysqld`QUICK_RANGE_SELECT::get_next(this=0x00007fcce2b15fa0) at opt_range.cc: 11247:21

          frame #14: 0x00000001057371ad mysqld`rr_quick(info=0x0000700009f3c320) at records.cc:405:29

          frame #15: 0x0000000105984e35 mysqld`mysql_update(thd=0x00007fcce585f400, fields=0x00007fcce5863618, values=0x00007fcce58643d8, limit=18446744073709551615, handle_duplicates=DUP_ERROR, found_return=0x0000700009f3cb48, updated_return=0x0000700009f3cb40) at sql_update.cc:819:14

          frame #16: 0x000000010598cc87 mysqld`Sql_cmd_update::try_single_table_update(this=0x00007fcce58643c8, thd=0x00007fcce585f400, switch_to_multitable=0x0000700009f3cc07) at sql_update.cc:2927:21

          frame #17: 0x000000010598d457 mysqld`Sql_cmd_update::execute(this=0x00007fcce58643c8, thd=0x00007fcce585f400) at sql_update.cc:3058:7

          frame #18: 0x000000010589b475 mysqld`mysql_execute_command(thd=0x00007fcce585f400, first_level=true) at sql_parse.cc:3616:26

          frame #19: 0x0000000105895d62 mysqld`mysql_parse(thd=0x00007fcce585f400, parser_state=0x0000700009f40340) at sql_parse.cc:5584:20

          frame #20: 0x0000000105892bf0 mysqld`dispatch_command(thd=0x00007fcce585f400, com_data=0x0000700009f40e78, command=COM_QUERY) at sql_parse.cc:1491:5

          frame #21: 0x0000000105894e70 mysqld`do_command(thd=0x00007fcce585f400) at sql_parse.cc:1032:17

          frame #22: 0x0000000105a23976 mysqld`::handle_connection(arg=0x00007fcce2555370) at

          connection_handler_per_thread.cc:313:13

          frame #23: 0x00000001063ee74c mysqld`::pfs_spawn_thread(arg=0x00007fcce2555f80) at pfs.cc:2197:3

          frame #24: 0x00007fff71032109 libsystem_pthread.dylib`_pthread_start + 148

          frame #25: 0x00007fff7102db8b libsystem_pthread.dylib`thread_start + 15

          可以看到connection1 在等待row1的行锁

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          开启新的session作为connection2执行kill co nnection1的 命令,走到前面代码中分析到的ha_close_connection时,会去中断innodb行锁的等待。堆栈为

          thread #30

          frame #0: 0x00000001060f910e mysqld`os_event::set(this=0x00007fcce2b16598) at os0event.cc:93:2

          frame #1: 0x00000001060f90b5 mysqld`os_event_set(event=0x00007fcce2b16598) at os0event.cc:560:9

          frame #2: 0x00000001060aa06e mysqld`lock_wait_release_thread_if_suspended(thr=0x00007fccd90936f0) at lock0wait.cc:411:3

          frame #3: 0x0000000106089a99 mysqld`lock_cancel_waiting_and_release(lock=0x00007fccd8866c18) at lock0lock. cc:6789:3

          frame #4: 0x0000000106096679 mysqld`lock_trx_handle_wait(trx=0x00007fcce1cc0ce0) at lock0lock.cc:6972:3

          frame #5: 0x0000000105ff93a6 mysqld`innobase_kill_connection(hton=0x00007fccd7e094d0,

          自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

          深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

          因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

          由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

          如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          总目录展示

          该笔记共八个节点(由浅入深),分为三大模块。

          高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这4个方面重点介绍。

          一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。

          高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。


          篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          由于内容太多,这里只截取部分的内容。

          一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

          MySQL进阶垫脚石:线程长时间处于killed状态怎么破?,dubbo常见面试题

          样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。

          高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。


          篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)

          [外链图片转存中…(img-kmTQXcIR-1712754385936)]

          [外链图片转存中…(img-BeLntuSo-1712754385936)]

          由于内容太多,这里只截取部分的内容。

          一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

          [外链图片转存中…(img-LnnYiVfZ-1712754385936)]

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]