在此借用 Oracle 的 SHUTDOWN LEVEL 分析

Oracle SHUTDOWN LEVEL 共有四种:ABORT、IMMEDIATE、NORMAL、TRANSACTIONAL

ABORT
  • 立即结束所有SQL
  • 回滚未提交事务
  • 断开所有用户连接
  • 下次启动实例时,需要recovery
IMMEDIATE
  • 允许正在运行的SQL执行完毕
  • 回滚未提交事务
  • 断开所有用户连接
NORMAL
  • 不允许建立新连接
  • 等待当前连接断开
  • 下次启动实例时,不需要recovery
TRANSACTIONAL
  • 等待事务提交或结束
  • 不允许新建连接
  • 事务提交或结束后断开连接

MySQL 中的 SHUTDOWN 实际相当于 Oracle 中的 SHUTDOWN IMMEDIATE,重启实例时无需recovery,但回滚事务的过程可能耗时很长

MySQL SHUTDOWN过程分析

  • mysql_shutdown 发送SHUTDOWN命令
  • dispatch_command() 接受到 COM_SHUTDOWN command,调用kill_mysql()
  • kill_mysql()创建 kill_server_thread
  • kill_server_thread 调用 kill_server()
  • kill_server()
    • close_connections()
      • 关闭端口
      • 断开连接
      • 回滚事务(可能耗时很长)
    • unireg_end
      • clean_up
        • innobase_shutdown_for_mysql
        • delete_pid_file
  • 0: 最慢,需等待purge完成,change buffer merge完成
  • 1: default, 不需要等待purge完成和change buffer merge完成
  • 2: 不等待后台删除表完成,row_drop_tables_for_mysql_in_background 不等刷脏页,如果设置了innodb_buffer_pool_dump_at_shutdown,不需要去buffer dump.

kill_server() 先调用 close_connections(),再调用 unireg_end()

  1. {
  2. ......
  3. if (sig != MYSQL_KILL_SIGNAL &&
  4. sig != 0)
  5. unireg_abort(1); /* purecov: inspected */
  6. else
  7. unireg_end();

结束线程的主要逻辑在 mysqld.cc:close_connections() 中

close_connection() 中调用 THD::disconnect() 断开连接 连接断开后开始回滚事务

  1. bool do_command(THD *thd)
  2. {
  3. ......
  4. packet_length= my_net_read(net); // thd->disconnect() 后此处直接返回
  5. ......
  6. }
  7. void do_handle_one_connection(THD *thd_arg)
  8. {
  9. ......
  10. while (thd_is_connection_alive(thd))
  11. if (do_command(thd)) //do_command 返回 error,跳出循环
  12. break;
  13. }
  14. end_connection(thd);
  15. end_thread:
  16. close_connection(thd);
  17. /* 此处调用one_thread_per_connection_end() */
  18. if (MYSQL_CALLBACK_ELSE(thd->scheduler, end_thread, (thd, 1), 0))
  19. return; // Probably no-threads
  20. ......
  21. }

unireg_end 调用 clean_up()

  1. void clean_up(bool print_message)
  2. {
  3. /* 这里是一些释放内存和锁的操作 */
  4. ......
  5. 这里调用 innobase_shutdown_for_mysql
  6. merge change buffer (innodb_fast_shutdown = 0)
  7. flush dirty page (innodb_fast_shutdown = 0,1)
  8. flush log buffer
  9. 都在这里面做
  10. */
  11. plugin_shutdown();
  12. /* 这里是一些释放内存和锁的操作 */
  13. ......
  14. /*
  15. 删除 pid 文件,删除后 mysqld_safe不会重启 mysqld,
  16. 不然会认为 mysqld crash,尝试重启
  17. */
  18. delete_pid_file(MYF(0));
  19. /* 这里是一些释放内存和锁的操作 */

innodb shutdown 分析

innodb shutdown 的主要操作在 logs_empty_and_mark_files_at_shutdown() 中

  • 等待后台线程结束
    • srv_error_monitor_thread
    • srv_lock_timeout_thread
    • srv_monitor_thread
    • buf_dump_thread
    • dict_stats_thread
  • 等待所有事物结束 trx_sys_any_active_transactions
  • 等待后台线程结束
    • worker threads: srv_worker_thread
    • master thread: srv_master_thread
    • purge thread: srv_purge_coordinator_thread
  • 等待 buf_flush_lru_manager_thread 结束
  • 等待 buf_flush_page_cleaner_thread 结束
  • 等待 Pending checkpoint_writes, Pending log flush writes 结束
  • 等待 buffer pool pending io 结束
  • if (innodb_fast_shutdown == 2)
    • flush log buffer 后 return
  • log_make_checkpoint_at
    • flush buffer pool
    • write checkpoint
  • 关闭所有文件

logs_empty_and_mark_files_at_shutdown() 结束后,innobase_shutdown_for_mysql() 再做一些资源清理工作即结束 shutdown 过程