MySQL • Source code analysis • SHUTDOWN procedure

Summary: There is only one type of SHUTDOWN MySQL SHUTDOWN LEVEL in ORACLE for the time being. The pit left with LEVEL in the source code has not been filled in here. Borrow Oracle's SHUTDOWN LEVEL to analyze Oracle SHUTDOWN LEVEL There are four types: ABORT, IMMEDIATE, NORMAL, TRANSACTIONAL ABORT Immediately end all SQL rollback uncommitted transactions disconnect all users

SHUTDOWN MySQL SHUTDOWN LEVEL in ORACLE There
is only one type of SHUTDOWN LEVEL for the time being, and the pit of LEVEL left in the source code has not been filled

. Here, we borrow Oracle's SHUTDOWN LEVEL to analyze

Oracle SHUTDOWN LEVEL There are four types: ABORT, IMMEDIATE, NORMAL, TRANSACTIONAL

ABORT

immediately ends all SQL ,
rolls back uncommitted transactions, and
disconnects all user connections. The
next time the instance is started, recovery
IMMEDIATE is required to allow

running SQL to complete execution.
Roll back uncommitted transactions
. Disconnect all user connections . New connection Waiting for the current connection to be disconnected The next time the instance is started, there is no need for recovery TRANSACTIONAL to wait for the transaction to commit or end No new connections are allowed Disconnect after the transaction is committed or ended










SHUTDOWN in MySQL is actually equivalent to SHUTDOWN IMMEDIATE in Oracle. There is no need to recover when restarting the instance, but the process of rolling back transactions may take a long time.

MySQL SHUTDOWN process analysis
mysql_shutdown sends SHUTDOWN command
dispatch_command() receives COM_SHUTDOWN command and calls kill_mysql()
kill_mysql() creates kill_server_thread
kill_server_thread calls kill_server()
kill_server()
close_connections() to
close the port
disconnection
rollback transaction (may take a long time)
unireg_end
clean_up
innobase_shutdown_for_mysql
delete_pid_file
InnoDB shutdown speed depends on parameter innodb_fast_shutdown

0: slowest, need to wait for purge Completed, change buffer merge completed
1: default, no need to wait for purge and change buffer merge to complete
2: Do not wait for the background to delete the table to complete, row_drop_tables_for_mysql_in_background does not wait for the dirty page to be flushed, if innodb_buffer_pool_dump_at_shutdown is set, there is no need to go to buffer dump.
  case COM_SHUTDOWN: // Receive SHUTDOWN command
  {
    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)) // check permissions
      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); // record general_log
    my_eof(thd);
    kill_mysql(); // call kill_mysql() function , the function creates kill_server_thread thread
    error=TRUE;
    break;
  }
 
kill_server() calls close_connections() first, then calls 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();

The main logic for ending the thread is in 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.*/
   
    /* Skip slave related threads and process them in the end_server() function*/
    if (tmp->slave_thread)
      continue;
    if (tmp->get_command() == COM_BINLOG_DUMP ||
        tmp->get_command() == COM_BINLOG_DUMP_GTID)
    {
      ++dump_thread_count;
      continue;
    }
   
    /* First mark as KILL to connect a self The chance of breaking */
    tmp->killed= THD::KILL_CONNECTION;
   
    ......
   
  }
  mysql_mutex_unlock(&LOCK_thread_count);

  Events::deinit();

  sql_print_information("Shutting down slave threads");
  /* disconnect here Slave related threads*/
  end_slave();
 
  /* Second pass through the thread list*/
  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 the connection, do not wait for the end of the statement, but roll back the uncommitted Thread */
      close_connection(tmp);
    }
  }
                                        
call THD::disconnect() in close_connection() to disconnect and start rolling back the transaction after the
connection is disconnected

bool do_command(THD *thd)
{
......
packet_length= my_net_read( net); // return directly after 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 calls clean_up()

void clean_up(bool print_message)
{
/* here are some operations to release memory and locks */
......

/*
here call 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
is done here
*/
  plugin_shutdown();
 
  /* here are some operations to release memory and locks */
  ......
 
  /*
  delete the pid file, mysqld_safe will not restart mysqld after deletion,
  otherwise it will think mysqld crashed and try to restart
  */
  delete_pid_file(MYF(0));
 
  /* Here are some operations to release memory and locks */
  ......
                                                                                                                                                                                      
innodb shutdown analysis

The main operating innodb shutdown in logs_empty_and_mark_files_at_shutdown () in

wait for background thread end
srv_error_monitor_thread
srv_lock_timeout_thread
srv_monitor_thread
buf_dump_thread
dict_stats_thread
waiting for all things end trx_sys_any_active_transactions
wait for a background thread ends
worker Threads: srv_worker_thread Master the
Thread: srv_master_thread
purge the Thread: srv_purge_coordinator_thread
waiting buf_flush_lru_manager_thread end
waiting buf_flush_page_cleaner_thread end
wait Pending checkpoint_writes, Pending log flush writes end
Wait for buffer pool pending io to end
if (innodb_fast_shutdown == 2)
flush log buffer and return
log_make_checkpoint_at
flush buffer pool
write checkpoint
will put lsn on disk fil_write_flushed_lsn_to_data_files()
close all files
logs_empty_and_mark_files_at_shutdown() After finishing,

innobase_shutdown_for_mysql For reprints, please send an email to [email protected]; if you find any content suspected of plagiarism in this community, please send an email to: [email protected] to report and provide relevant evidence. Once verified, This community will immediately delete the allegedly infringing content.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326073714&siteId=291194637