本文先介绍新连接建立的主要调用栈,再分析thread cache 和每个连接的资源限制
mysql 支持三种连接方式
- socket
- named pipe
- shared memory
named pipe 和 shared memory 只能在本地连接数据库,适用场景较少,本文主要介绍 socket 连接方式
从主线程开始
用户连接线程栈如下
my_thread_init
for (;;)
THD *thd= init_new_thd(channel_info);
/* 第一次循环执行prepare,后面跳过 */
thd_prepare_connection(thd)
login_connection
check_connection
/* 权限认证 */
acl_authenticate
prepare_new_connection_state
/* 第二次循环开始,执行do_command */
while (thd_connection_alive(thd))
do_command(thd)
end_connection(thd);
close_connection(thd, 0, false, false);
thd->release_resources();
/* 进入 thread cache,等待新连接复用 */
channel_info= Per_thread_connection_handler::block_until_new_connection();
break;
参数 thread_cache_size 控制了 thread_cache 的大小, 设为0时关闭 thread_cache,不缓存空闲thread
mysql> show status like 'Threads%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 1 |
| Threads_connected | 1 |
| Threads_created | 2 |
| Threads_running | 1 |
+-------------------+-------+
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
保存用户连接限制的结构体,作为成员属性存在于各个和连接限制相关的类
typedef struct user_resources {
/* MAX_QUERIES_PER_HOUR */
uint questions;
/* MAX_UPDATES_PER_HOUR */
uint updates;
/* MAX_CONNECTIONS_PER_HOUR */
uint conn_per_hour;
/* MAX_USER_CONNECTIONS */
uint user_conn;
enum {QUERIES_PER_HOUR= 1, UPDATES_PER_HOUR= 2, CONNECTIONS_PER_HOUR= 4,
USER_CONNECTIONS= 8};
uint specified_limits;
3.1.2 ACL_USER
ACl_USER 对象保存在数组 acl_users 中,每次mysqld启动时,从mysql.user表中读取数据,初始化 acl_users,初始化过程在函数 acl_load 中
调用栈如下
main
mysqld_main
acl_reload
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 调用栈如下
handle_connection
thd_prepare_connection(thd)
login_connection
check_connection
acl_authenticate
get_or_create_user_conn
3.1.4 资源限制在源码中的位置
MAX_USER_CONNECTIONS MAX_CONNECTIONS_PER_HOUR | check_for_max_user_connections |
---|---|
MAX_QUERIES_PER_HOUR MAX_UPDATES_PER_HOUR | check_mqh |
调用栈如下
handle_connection
thd_prepare_connection(thd)
login_connection
check_connection
acl_authenticate
check_for_max_user_connections
do_command
dispatch_command
mysql_parse