msyql的線程池的源碼分析

mysqld啓動流程

1、設置斷點break mysqld_main
2、啓動時的bt棧
(gdb) bt
     #0  mysqld_main (argc=4, argv=0x7fffffffdee8) at           /mysql/zxz/tianyicloud/mysql-5.7.21/sql/mysqld.cc:4408    
    #1  0x0000000000e96ff0 in main (argc=4, argv=0x7fffffffdee8) at   /mysql/zxz/tianyicloud/mysql-5.7.21/sql/main.cc:25
…………………………………………
…………………………………………
3、調整相關limits
  adjust_related_options(&requested_open_files);
  包括三個子函數

//調整open_files_limit

void adjust_open_files_limit(ulong *requested_open_files){
  /* MyISAM requires two file handles per table. */
  /*table_cache_size對應table_open_cache變量,表示所有線程同時打開表的最大數量(我們的配置4096)*/
  limit_1= 10 + max_connections + table_cache_size * 2;//10+3000+4096*2=11202
    /*
    We are trying to allocate no less than max_connections*5 file
    handles (i.e. we are trying to set the limit so that they will
    be available).
  */
  limit_2= max_connections * 5;
   /* Try to allocate no less than 5000 by default. */
  limit_3= open_files_limit ? open_files_limit : 5000;//默認爲5000
  request_open_files= max<ulong>(max<ulong>(limit_1, limit_2), limit_3);

  /* Notice: my_set_max_open_files() may return more than requested. */
  effective_open_files= my_set_max_open_files(request_open_files);
  if (effective_open_files < request_open_files)//131072<15000
  {
    if (open_files_limit == 0)
    {
      sql_print_warning("Changed limits: max_open_files: %lu (requested %lu)",
                        effective_open_files, request_open_files);
    }
    else
    {
      sql_print_warning("Could not increase number of max_open_files to "
                        "more than %lu (request: %lu)",
                        effective_open_files, request_open_files);
    }
  }
    open_files_limit= effective_open_files;
  if (requested_open_files)
     //將requested的值設置爲effective_open_files
    *requested_open_files= min<ulong>(effective_open_files,request_open_files);
}

調整最大連接數

void adjust_max_connections(ulong requested_open_files)
{
  ulong limit;

  limit= requested_open_files - 10 - TABLE_OPEN_CACHE_MIN * 2;

  if (limit < max_connections)
  {
    sql_print_warning("Changed limits: max_connections: %lu (requested %lu)",
                      limit, max_connections);

    // This can be done unprotected since it is only called on startup.
    max_connections= limit;
  }
}
//思考:在平時運維過程中,爲什麼我們設置了最大連接數爲3000,但我們show最大連接數時候仍看到214

調整table_cache_size

void adjust_table_cache_size(ulong requested_open_files)
{
  ulong limit;

  limit= max<ulong>((requested_open_files - 10 - max_connections) / 2,
                    TABLE_OPEN_CACHE_MIN);

  if (limit < table_cache_size)
  {
    sql_print_warning("Changed limits: table_open_cache: %lu (requested %lu)",
                      limit, table_cache_size);

    table_cache_size= limit;
  }

  table_cache_size_per_instance= table_cache_size / table_cache_instances;
}

思考題:[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-YfIjHEtf-1572922906868)(C:\Users\zxz\AppData\Roaming\Typora\typora-user-images\1572835651351.png)]

void adjust_table_def_size()
{
  ulong default_value;
  sys_var *var;

  default_value= min<ulong> (400 + table_cache_size / 2, 2000);
  var= intern_find_sys_var(STRING_WITH_LEN("table_definition_cache"));
  DBUG_ASSERT(var != NULL);
  var->update_default(default_value);

  if (! table_definition_cache_specified)
    table_def_size= default_value;
}

線程池的初始化

(gdb)  bt
#0  Connection_handler_manager::init () at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/conn_handler/connection_handler_manager.cc:175
#1  0x0000000000e9ee53 in mysqld_main (argc=326, argv=0x2e19728) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/mysqld.cc:4605
#2  0x0000000000e96ff0 in main (argc=4, argv=0x7fffffffdee8) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/main.cc:25

初始化流程

1、初始化連接管理類

預備知識:teledb的mysql的連接分爲3類,分別是一個線程處理所有連接、一個連接對應對應一個線程(每連接每線程)、線程池,這3種處理連接的方式被封裝成C++類,它們有一個共同的基類Connection_handler

bool Connection_handler_manager::init()
{
  /*
    This is a static member function.
    Per_thread_connection_handler's static members need to be initialized
    even if One_thread_connection_handler is used instead.
  */
  /*
  每連接每線程的初始化函數,
  該函數的具體實現如下
  */
  Per_thread_connection_handler::init();

  /*
  將connection_handler定義爲線程池
  ulong Connection_handler_manager::thread_handling=
  SCHEDULER_THREAD_POOL;
  */
  Connection_handler *connection_handler= NULL;
  switch (Connection_handler_manager::thread_handling)
  {
  ………………
  ………………
  case SCHEDULER_THREAD_POOL: 
    connection_handler= new (std::nothrow) Thread_pool_connection_handler();
  ………………
  ………………
  }

  //Unix socket connection handler. By teledb 2018-1-25.
  Connection_handler *extra_connection_handler=
    new (std::nothrow) Per_thread_connection_handler();

  if (connection_handler == NULL || extra_connection_handler == NULL)
  {
    // This is a static member function.
    Per_thread_connection_handler::destroy();
    return true;
  }

  /*
  初始化連接管理類,封裝每連接每線程類
  */
  m_instance= new (std::nothrow) Connection_handler_manager(connection_handler, extra_connection_handler);

  if (m_instance == NULL)
  {
    delete connection_handler;
    // This is a static member function.
    Per_thread_connection_handler::destroy();
    return true;
  }
  ………………
  ………………
  return false;
}
network_init分析

功能:set_ports進行相應的初始化端口,另一個很重要的功能就是創建監聽對象Mysqld_socket_listener,並對其進行相應的初始化操作,然後對listener進行相應的監聽動作,最終是調用setup_listener()的方法來初始化listener的主要信息;(判斷後續的連接是socket還是tcp連接的重要依據)

bt信息如下

(gdb)   bt
#0  Mysqld_socket_listener::setup_listener (this=0x2f7aee0) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/conn_handler/socket_connection.cc:807
#1  0x0000000000ea8329 in Connection_acceptor<Mysqld_socket_listener>::init_connection_acceptor (this=0x99382000)
    at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/conn_handler/connection_acceptor.h:55
#2  0x0000000000e99bae in network_init () at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/mysqld.cc:1651
#3  0x0000000000e9f90c in mysqld_main (argc=326, argv=0x2e19728) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/mysqld.cc:4927
#4  0x0000000000e96ff0 in main (argc=4, argv=0x7fffffffdee8) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/main.cc:25

setup_listenener的代碼流程處理如下

bool Mysqld_socket_listener::setup_listener()
{
  // Setup tcp socket listener
  if (m_tcp_port)//9482
  {
    TCP_socket tcp_socket(m_bind_addr_str, m_tcp_port,
                          m_backlog, m_port_timeout);

    MYSQL_SOCKET mysql_socket= tcp_socket.get_listener_socket();
    if (mysql_socket.fd == INVALID_SOCKET)
      return true;

    m_socket_map.insert(std::pair<MYSQL_SOCKET,bool>(mysql_socket, false));
  }
#if defined(HAVE_SYS_UN_H)
  // Setup unix socket listener
  if (m_unix_sockname != "")
  {
    Unix_socket unix_socket(&m_unix_sockname, m_backlog);

    MYSQL_SOCKET mysql_socket= unix_socket.get_listener_socket();
    if (mysql_socket.fd == INVALID_SOCKET)
      return true;

    m_socket_map.insert(std::pair<MYSQL_SOCKET,bool>(mysql_socket, true));
    m_unlink_sockname= true;
  }
#endif /* HAVE_SYS_UN_H */

  // Setup for connection events for poll or select
#ifdef HAVE_POLL
  int count= 0;
#endif
  for (socket_map_iterator_t sock_map_iter=  m_socket_map.begin();
       sock_map_iter != m_socket_map.end(); ++sock_map_iter)
  {
    MYSQL_SOCKET listen_socket= sock_map_iter->first;
    mysql_socket_set_thread_owner(listen_socket);
#ifdef HAVE_POLL
    m_poll_info.m_fds[count].fd= mysql_socket_getfd(listen_socket);
    m_poll_info.m_fds[count].events= POLLIN;
    m_poll_info.m_pfs_fds[count]= listen_socket;
    count++;
#else  // HAVE_POLL
    FD_SET(mysql_socket_getfd(listen_socket), &m_select_info.m_client_fds);
    if ((uint) mysql_socket_getfd(listen_socket) > m_select_info.m_max_used_connection)
      m_select_info.m_max_used_connection= mysql_socket_getfd(listen_socket);
#endif // HAVE_POLL
  }
  return false;
}
客戶端連接處理流程分析
創建channel_info
(gdb)   bt
#0  0x00002aaaac03abcd in poll () from /lib64/libc.so.6
#1  0x0000000001664389 in Mysqld_socket_listener::listen_for_connection_event (this=0x994e0070)
    at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/conn_handler/socket_connection.cc:860
#2  0x0000000000ea8352 in Connection_acceptor<Mysqld_socket_listener>::connection_event_loop (this=0x99381c40)
    at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/conn_handler/connection_acceptor.h:66
#3  0x0000000000e9fdd9 in mysqld_main (argc=326, argv=0x2e19728) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/mysqld.cc:5135
#4  0x0000000000e96ff0 in main (argc=4, argv=0x7fffffffdee8) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/main.cc:25

1、通過如下方式判斷到底是socket請求還是tcp請求,然後獲取相應的listen_sock

#ifdef HAVE_POLL
  for (uint i= 0; i < m_socket_map.size(); ++i)
  {
    if (m_poll_info.m_fds[i].revents & POLLIN)
    {
      listen_sock= m_poll_info.m_pfs_fds[i];
      is_unix_socket= m_socket_map[listen_sock];
      break;
    }
  }

2、獲取mysql_socket

 connect_sock= mysql_socket_accept(key_socket_client_connection, listen_sock,
                                      (struct sockaddr *)(&cAddr), &length);

3、根據sock類型建立相應的channel通道

  Channel_info* channel_info= NULL;
  if (is_unix_socket)
    channel_info= new (std::nothrow) Channel_info_local_socket(connect_sock);
  else
    channel_info= new (std::nothrow) Channel_info_tcpip_socket(connect_sock);
創建新的連接
(gdb)  bt
#0  Connection_handler_manager::process_new_connection (this=0x2f2c010, channel_info=0x99641540)
    at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/conn_handler/connection_handler_manager.cc:301
#1  0x0000000000ea8370 in Connection_acceptor<Mysqld_socket_listener>::connection_event_loop (this=0x2f81ee0)
    at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/conn_handler/connection_acceptor.h:68
#2  0x0000000000e9fdd9 in mysqld_main (argc=326, argv=0x2e19728) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/mysqld.cc:5135
#3  0x0000000000e96ff0 in main (argc=4, argv=0x7fffffffdee8) at /mysql/zxz/tianyicloud/mysql-5.7.21/sql/main.cc:25

1、process_new_connection代碼如下:

void  Connection_handler_manager::process_new_connection(Channel_info* channel_info)
{
/*
判斷是哪種類型的連接,是socket還是tcp
*/
  bool is_unix_socket_connection = channel_info->vio_type() == VIO_TYPE_SOCKET;
 /*
通過check_and_incr_conn_count函數來判斷socket連接數或者tcp連接數是否超過預設值,socket目前代碼裏固定爲20,tcp連接數根據配置文件以及相關的open_files_limits來決定
 */
    
  if (abort_loop || !check_and_incr_conn_count(is_unix_socket_connection))
  {
    channel_info->send_error_and_close_channel(ER_CON_COUNT_ERROR, 0, true);
    delete channel_info;
    return;
  }
 /*
 根據是socket連接類型還是tcp連接類型來決定Connection_handler,繼而使用後面的add_connection
 */
  Connection_handler* handler= is_unix_socket_connection ? m_extra_connection_handler : m_connection_handler;
  if (handler->add_connection(channel_info))
  {
    inc_aborted_connects();
    delete channel_info;
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章