本文先介绍新连接建立的主要调用栈,再分析thread cache 和每个连接的资源限制

mysql 支持三种连接方式

  • socket
  • named pipe
  • shared memory

named pipe 和 shared memory 只能在本地连接数据库,适用场景较少,本文主要介绍 socket 连接方式

从主线程开始

用户连接线程栈如下

  1. my_thread_init
  2. for (;;)
  3. THD *thd= init_new_thd(channel_info);
  4. /* 第一次循环执行prepare,后面跳过 */
  5. thd_prepare_connection(thd)
  6. login_connection
  7. check_connection
  8. /* 权限认证 */
  9. acl_authenticate
  10. prepare_new_connection_state
  11. /* 第二次循环开始,执行do_command */
  12. while (thd_connection_alive(thd))
  13. do_command(thd)
  14. end_connection(thd);
  15. close_connection(thd, 0, false, false);
  16. thd->release_resources();
  17. /* 进入 thread cache,等待新连接复用 */
  18. channel_info= Per_thread_connection_handler::block_until_new_connection();
  19. break;

参数 thread_cache_size 控制了 thread_cache 的大小, 设为0时关闭 thread_cache,不缓存空闲thread

  1. mysql> show status like 'Threads%';
  2. +-------------------+-------+
  3. | Variable_name | Value |
  4. +-------------------+-------+
  5. | Threads_cached | 1 |
  6. | Threads_connected | 1 |
  7. | Threads_created | 2 |
  8. | Threads_running | 1 |
  9. +-------------------+-------+
  10. 4 rows in set (0.02 sec)

Threads_cached:缓存的 thread,新连接建立时,优先使用cache中的thread

Threads_connected:已连接的 thread

Threads_created:建立的 thread 数量

Threads_created = Threads_cached + Threads_connected

Threads_running <= Threads_connected

MySQL 建立新连接非常消耗资源,频繁使用短连接,又没有其他组件实现连接池时,可以适当提高 thread_cache_size,降低新建连接的开销

2.1 thread cache 源码分析

2.1.1 block_until_new_connection

handle_connection 线程结束之前,会执行 block_until_new_connection,尝试进入 thread cache 等待其他连接复用

如果 blocked_pthread_count < max_blocked_pthreads,blocked_pthread_count++,然后等待被 COND_thread_cache 唤醒,唤醒之后 blocked_pthread_count– , 返回 waiting_channel_info_list 中的一个 channel_info ,进行 handle_connections 的下一个循环

2.1.2 check_idle_thread_and_enqueue_connection

检查是否 blocked_pthread_count > wake_pthread (有足够的block状态线程用来唤醒) 如有 插入 channel_info 进入 waiting_channel_info_list,并发出 COND_thread_cache 信号量

除了参数 max_user_connections 限制每个用户的最大连接数,还可以对每个用户制定更细致的限制

以下四个限制保存在mysql.user表中

  • MAX_QUERIES_PER_HOUR 每小时最大请求数(语句数量)
  • MAX_UPDATES_PER_HOUR 每小时最大更新数(更新语句的数量)
  • MAX_CONNECTIONS_PER_HOUR 每小时最大连接数
  • MAX_USER_CONNECTIONS 这个用户的最大连接数

3.1 源码分析

3.1.1 USER_RESOURCES

保存用户连接限制的结构体,作为成员属性存在于各个和连接限制相关的类

  1. typedef struct user_resources {
  2. /* MAX_QUERIES_PER_HOUR */
  3. uint questions;
  4. /* MAX_UPDATES_PER_HOUR */
  5. uint updates;
  6. /* MAX_CONNECTIONS_PER_HOUR */
  7. uint conn_per_hour;
  8. /* MAX_USER_CONNECTIONS */
  9. uint user_conn;
  10. enum {QUERIES_PER_HOUR= 1, UPDATES_PER_HOUR= 2, CONNECTIONS_PER_HOUR= 4,
  11. USER_CONNECTIONS= 8};
  12. uint specified_limits;
3.1.2 ACL_USER

ACl_USER 对象保存在数组 acl_users 中,每次mysqld启动时,从mysql.user表中读取数据,初始化 acl_users,初始化过程在函数 acl_load 中

调用栈如下

  1. main
  2. mysqld_main
  3. acl_reload
  4. acl_load
3.1.3 USER_CONN

保存用户资源使用的结构体,建立连接时,调用 get_or_create_user_conn 为 THD 绑定 USER_CONN 对象

每个用户第一个连接创建时,建立一个新对象,存入 hash_user_connections

第二个连接开始,从 hash_user_connections 取出 USER_CONN 对象和 THD 绑定

同一个用户的连接,THD 都和同一个 USER_CONN 对象绑定

get_or_create_user_conn 调用栈如下

  1. handle_connection
  2. thd_prepare_connection(thd)
  3. login_connection
  4. check_connection
  5. acl_authenticate
  6. get_or_create_user_conn

3.1.4 资源限制在源码中的位置

MAX_USER_CONNECTIONS MAX_CONNECTIONS_PER_HOURcheck_for_max_user_connections
MAX_QUERIES_PER_HOUR MAX_UPDATES_PER_HOURcheck_mqh

调用栈如下

  1. handle_connection
  2. thd_prepare_connection(thd)
  3. login_connection
  4. check_connection
  5. acl_authenticate
  6. check_for_max_user_connections
  7. do_command
  8. dispatch_command
  9. mysql_parse