`
jzy996492849
  • 浏览: 128864 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

MySQL • 源码分析 • SHUTDOWN过程

 
阅读更多
摘要: ORACLE 中的SHUTDOWN MySQL SHUTDOWN LEVEL 暂时只有一种,源码中留了 LEVEL 的坑还没填 在此借用 Oracle 的 SHUTDOWN LEVEL 分析 Oracle SHUTDOWN LEVEL 共有四种:ABORT、IMMEDIATE、NORMAL、TRANSACTIONAL ABORT 立即结束所有SQL 回滚未提交事务 断开所有用户连

ORACLE 中的SHUTDOWN
MySQL SHUTDOWN LEVEL 暂时只有一种,源码中留了 LEVEL 的坑还没填

在此借用 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
InnoDB shutdown 速度取决于参数 innodb_fast_shutdown

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.
  case COM_SHUTDOWN: // 接受到SHUTDOWN命令
  {
    if (packet_length < 1)
    {   
      my_error(ER_MALFORMED_PACKET, MYF(0));
      break;
    }   
    status_var_increment(thd->status_var.com_other);
    if (check_global_access(thd,SHUTDOWN_ACL)) // 检查权限
      break; /* purecov: inspected */
    /*  
      If the client is < 4.1.3, it is going to send us no argument; then
      packet_length is 0, packet[0] is the end 0 of the packet. Note that
      SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
      packet[0].
    */
    enum mysql_enum_shutdown_level level; // 留的坑,default以外的LEVEL都没实现
    if (!thd->is_valid_time())
      level= SHUTDOWN_DEFAULT;                                                                                                                                                                             
    else
      level= (enum mysql_enum_shutdown_level) (uchar) packet[0];
    if (level == SHUTDOWN_DEFAULT)
      level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
    else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
    {   
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level");
      break;
    }   
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
    general_log_print(thd, command, NullS); // 记录general_log
    my_eof(thd);
    kill_mysql(); // 调用kill_mysql()函数,函数内部创建 kill_server_thread 线程
    error=TRUE;
    break;
  }
 
kill_server() 先调用 close_connections(),再调用 unireg_end()

static void __cdecl kill_server(int sig_ptr)
{
......
close_connections();
   if (sig != MYSQL_KILL_SIGNAL &&
        sig != 0)                                     
      unireg_abort(1);        /* purecov: inspected */
    else
      unireg_end();

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

  static void close_connections(void)

  ......
   
  /* 下面这段代码结束监听端口 */
  /* Abort listening to new connections */
  DBUG_PRINT("quit",("Closing sockets"));
  if (!opt_disable_networking )
  {
    if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET)
    {
      (void) mysql_socket_shutdown(base_ip_sock, SHUT_RDWR);
      (void) mysql_socket_close(base_ip_sock);
      base_ip_sock= MYSQL_INVALID_SOCKET;
    }
    if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET)
    {
      (void) mysql_socket_shutdown(extra_ip_sock, SHUT_RDWR);
      (void) mysql_socket_close(extra_ip_sock);
      extra_ip_sock= MYSQL_INVALID_SOCKET;
    }
  }
 
  ......

  /* 第一遍遍历线程列表 */
  sql_print_information("Giving %d client threads a chance to die gracefully",
                        static_cast<int>(get_thread_count()));

  mysql_mutex_lock(&LOCK_thread_count);
 
  Thread_iterator it= global_thread_list->begin();
  for (; it != global_thread_list->end(); ++it)
  {
    THD *tmp= *it;
    DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
                       tmp->thread_id));
    /* We skip slave threads & scheduler on this first loop through. */
   
    /* 跳过 slave 相关线程,到 end_server() 函数内处理 */
    if (tmp->slave_thread)
      continue;
    if (tmp->get_command() == COM_BINLOG_DUMP ||
        tmp->get_command() == COM_BINLOG_DUMP_GTID)
    {
      ++dump_thread_count;
      continue;
    }
   
    /* 先标记为 KILL 给连接一个自我了断的机会 */
    tmp->killed= THD::KILL_CONNECTION;
   
    ......
   
  }
  mysql_mutex_unlock(&LOCK_thread_count);

  Events::deinit();

  sql_print_information("Shutting down slave threads");
  /* 此处断开 slave 相关线程 */
  end_slave();
 
  /* 第二遍遍历线程列表 */
  if (dump_thread_count)
  {                                                                                                                                                                                                        
    /*
      Replication dump thread should be terminated after the clients are
      terminated. Wait for few more seconds for other sessions to end.
     */
    while (get_thread_count() > dump_thread_count && dump_thread_kill_retries)
    {
      sleep(1);
      dump_thread_kill_retries--;
    }
    mysql_mutex_lock(&LOCK_thread_count);
    for (it= global_thread_list->begin(); it != global_thread_list->end(); ++it)
    {
      THD *tmp= *it;
      DBUG_PRINT("quit",("Informing dump thread %ld that it's time to die",
                         tmp->thread_id));
      if (tmp->get_command() == COM_BINLOG_DUMP ||
          tmp->get_command() == COM_BINLOG_DUMP_GTID)
      {
      /* 关闭DUMP线程 */
        tmp->killed= THD::KILL_CONNECTION;
       
        ......
       
      }
    }
    mysql_mutex_unlock(&LOCK_thread_count);
  }
 
  ......
 
  /* 第三遍遍历线程列表 */
  for (it= global_thread_list->begin(); it != global_thread_list->end(); ++it)
  {
    THD *tmp= *it;
    if (tmp->vio_ok())
    {
      if (log_warnings)
        sql_print_warning(ER_DEFAULT(ER_FORCING_CLOSE),my_progname,
                          tmp->thread_id,
                          (tmp->main_security_ctx.user ?
                           tmp->main_security_ctx.user : ""));
      /* 关闭连接,不等待语句结束,但是要回滚未提交线程 */
      close_connection(tmp);
    }
  }
                                        
close_connection() 中调用 THD::disconnect() 断开连接
连接断开后开始回滚事务

bool do_command(THD *thd)
{
......
packet_length= my_net_read(net); // thd->disconnect() 后此处直接返回
......                       
}

void do_handle_one_connection(THD *thd_arg)
{
......
while (thd_is_connection_alive(thd))
{
  if (do_command(thd)) //do_command 返回 error,跳出循环
  break;
}
    end_connection(thd);

end_thread:
    close_connection(thd);
    /* 此处调用one_thread_per_connection_end() */
    if (MYSQL_CALLBACK_ELSE(thd->scheduler, end_thread, (thd, 1), 0))
      return;                                 // Probably no-threads


......
}

事务回滚调用链

trans_rollback(THD*) ()
THD::cleanup() ()
THD::release_resources() ()
one_thread_per_connection_end(THD*, bool) ()
do_handle_one_connection(THD*) ()
handle_one_connection ()
unireg_end 调用 clean_up()

void clean_up(bool print_message)
{
/* 这里是一些释放内存和锁的操作 */
......

/*
这里调用 innobase_shutdown_for_mysql
purge all (innodb_fast_shutdown = 0)
merge change buffer (innodb_fast_shutdown = 0)
flush dirty page (innodb_fast_shutdown = 0,1)
flush log buffer
都在这里面做
*/
  plugin_shutdown();
 
  /* 这里是一些释放内存和锁的操作 */
  ......
 
  /*
  删除 pid 文件,删除后 mysqld_safe不会重启 mysqld,
  不然会认为 mysqld crash,尝试重启
  */
  delete_pid_file(MYF(0));
 
  /* 这里是一些释放内存和锁的操作 */
  ......
                                                                                                                                                                                      
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
将 lsn 落盘 fil_write_flushed_lsn_to_data_files()
关闭所有文件
logs_empty_and_mark_files_at_shutdown() 结束后,innobase_shutdown_for_mysql() 再做一些资源清理工作即结束 shutdown 过程

本文为云栖社区原创内容,未经允许不得转载,如需转载请发送邮件至yqeditor@list.alibaba-inc.com;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@service.aliyun.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
分享到:
评论

相关推荐

    武侠2源码分析

    ### 武侠2源码分析 #### 一、全局对象与模块 在深入解析武侠2源码之前,我们首先需要了解整个项目的架构设计以及各主要模块的功能定位。这对于理解源码逻辑至关重要。 ##### 1.1 Game `Game` 类是整个游戏的核心...

    Mysql 5.7.9 shutdown 语法实例详解

    之前如果想关闭一个mysql数据库可以通过kill 命令、mysqladmin shutdown 、service mysqld stop 等这样的方式。然而在mysql-5.7.9之后mysql终于提供了SQL接口的shutdown语法啦

    Shutdown源码.zip

    《Shutdown源码分析:探索易语言与电脑软件的实践应用》 在计算机技术领域,开发实用的工具软件是提升效率和便利性的重要手段。本文将深入探讨一款名为"Shutdown"的小型工具,它具备定时定点关机的功能。通过分析其...

    Shutdown工具附带源码.zip

    "Shutdown工具附带源码.zip" 是一个包含电脑软件开发资源的压缩包,主要用于学习如何创建一个定时关机的应用程序。这个工具集成了一个已经完成的"准点定时关机"程序,以及相关的源代码,使得用户能够查看并理解其...

    CentOS安装MySQL 5.5

    本文档详细介绍了在CentOS 5.5 x86_64系统上安装MySQL 5.5的过程,包括但不限于软件环境检查、编译工具安装、MySQL源码编译、数据库服务配置等环节。 #### 1.2 目标 - **用户及其目录**:创建mysql组和mysql用户,...

    Android 关机和重启(reboot and shutdown)源码.rar

    本压缩包文件包含的源码为我们揭示了这些过程背后的细节。以下是关于Android关机和重启源码的一些关键知识点: 1. **广播接收器**: - 当用户执行关机或重启操作时,系统会发送一个BroadcastIntent,通常为`ACTION...

    mysql5.5.9 安装文档

    初始化 MySQL 数据库是安装过程中的重要一步,通过执行 `mysql_install_db` 脚本来完成数据库的初始化工作。 **步骤一:** 使用 `mysql_install_db` 初始化数据库,确保指定了正确的 `--basedir` 和 `--datadir` ...

    MYSQL

    14.1.4 编译并安装用户定义函数 14.2 增加一个新的原生(native)函数 15 为MySQL增加新过程 15.1 analyse过程 15.2 编写一个过程 16 MySQL对 ODBC 支持 16.1 MyODBC 支持的操作系统 ...

    Mysql小版本升级指南

    如果你的MySQL实例使用InnoDB存储引擎,你应该在升级前关闭`innodb_fast_shutdown`选项。这可以确保在关闭数据库时进行完整的InnoDB数据和事务清理,避免潜在的数据不一致。 3. **安全地停止MySQL服务**: 在升级...

    oracle 中shutdown命令的实用

    Oracle 中 shutdown 命令的实用 Oracle 数据库中的shutdown命令是非常重要的命令之一,它可以帮助DBA关闭数据库,以便进行维护、升级或解决问题。但是,shutdown命令并不是一个简单的命令,它有多种方式和考虑因素...

    mysql诊断分析.pdf

    综上所述,MySQL的诊断分析是一个涉及多个层面的过程,包括基准测试以了解系统性能,调整参数以优化性能,监控关键指标以发现问题,以及对可能出现的问题进行总结和处理。通过这些方法,可以确保MySQL数据库高效稳定...

    MySQL中文参考手册.chm

    14.1.2 参数处理 14.1.3 返回值和出错处理 14.1.4 编译并安装用户定义函数 14.2 增加一个新的原生(native)函数 15 为MySQL增加新过程 15.1 analyse过程 15.2 编写一个过程 ...

    mysql安装手册

    ### MySQL 5.5 源码安装详细指南 ...以上就是MySQL 5.5的源码安装全过程。通过这些步骤,你可以获得一个完全定制化的MySQL环境。在整个过程中,注意每一步的细节,确保所有配置都符合自己的需求。

    mysql升级教程文档

    - **sys**: 提供了一组预定义的视图和存储过程,帮助分析服务器性能。 #### 三、MySQL升级的方式 MySQL提供了两种不同的升级方式: ##### 1. In-place Upgrade(就地升级) - **适用场景**: 小版本之间的升级。 - ...

    shutdown

    "shutdown"命令是操作系统中的一个核心功能,用于安全地关闭、重启或休眠计算机。在Windows系统中,它是一个非常实用的工具,尤其对于需要定时执行关机操作或者需要无人员值守时关闭计算机的情况。下面将详细介绍...

    MySQL 6 绿色精简BAT版 2.24 MB

    默认字符集为GBK的方式来启动 Mysql. &lt;br&gt;mysql_stop.bat &lt;br&gt;@echo off REM Mysql shutdown ... bin\mysqladmin --user=root --password= shutdown &lt;br&gt;以 root 用户连接到 MySQL 执行 shutdown ...

    mysql5.1升级mysql5.5操作步骤

    特别注意的是,必须将 `innodb_fast_shutdown` 参数设置为 0,以确保在关闭过程中不会跳过任何重要的清理工作,这可以通过执行 `mysql&gt; set global innodb_fast_shutdown=0;` 来实现。 - **停止当前服务**:通过...

    学习MySQL.pdf

    4. MySQL的管理命令:包括使用`mysqladmin`命令来管理MySQL服务器,如关闭服务器`mysqladmin -uroot shutdown`,以及使用`mysqlshow`来显示数据库、表、列等信息。 5. MySQL启动、停止和重启服务的命令:包括在...

    shutdown定时关机源码

    【标题】"shutdown定时关机源码"涉及的是在Delphi编程环境中实现计算机定时关机功能的技术。这个项目可能是一个小型实用程序,用于帮助用户设置一个特定的时间点或倒计时,让计算机自动关闭,以方便管理和安排电脑的...

Global site tag (gtag.js) - Google Analytics