之前已经有过两篇有关连接的月报 网络通信模块浅析 mysql认证阶段漫游

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

mysql 支持三种连接方式

  • socket
  • named pipe
  • shared memory

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

1.代码栈分析

从主线程开始


1. main

3. mysqld_admin

5. network_init

7. /*
8. Connection_acceptor 是一个模板类
9. template <typename Listener> class Connection_acceptor
10. 三种连接方式分别传入各自的 Listener
11. Mysqld_socket_listener
12. Named_pipe_listener
13. Shared_mem_listener
14. 最常用的是 Mysqld_socket_listener
15. */
16. Connection_acceptor<Mysqld_socket_listener>::init_connection_acceptor

18. Connection_acceptor

20. Mysqld_socket_listener::listen_for_connection_event

22. /* 监听socket文件,没有新连接时,线程在这里等待 */
23. poll

25. /* 返回新连接的信息 */
26. return channel_info

28. Connection_handler_manager::process_new_connection

30. /* 检查max_connections */
31. check_and_incr_conn_count

33. /*
34. Connection_handler 有两个子类,
35. Per_thread_connection_handler:一个连接一个线程
36. One_thread_connection_handler:一个线程处理所有连接
37. 我们一般使用 Per_thread_connection_handler
38. */
39. m_connection_handler->add_connection(Per_thread_connection_handler::add_connection)

41. /* 查看 thread_cache 中是否有空闲thread,如有,使用cached thread */
42. if (!check_idle_thread_and_enqueue_connection(channel_info))
43. DBUG_RETURN(false);

45. /* 建立新线程,从 handle_connection 开始执行 */
46. mysql_thread_create(handle_connection)

49. Global_THD_manager::get_instance()->inc_thread_created();

用户连接线程栈如下


1. handle_connection

3. my_thread_init

5. for (;;)

7. THD *thd= init_new_thd(channel_info);

10. /* 第一次循环执行prepare,后面跳过 */
11. thd_prepare_connection(thd)

13. login_connection

15. check_connection

17. /* 权限认证 */
18. acl_authenticate

20. prepare_new_connection_state

22. /* 第二次循环开始,执行do_command */
23. while (thd_connection_alive(thd))

25. do_command(thd)

27. end_connection(thd);

29. close_connection(thd, 0, false, false);

31. thd->release_resources();

33. /* 进入 thread cache,等待新连接复用 */
34. channel_info= Per_thread_connection_handler::block_until_new_connection();
35. if (channel_info == NULL)
36. break;

2.thread_cache

参数 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_running:running状态的 thread 数量

Threads_created = Threads_cached + Threads_connected

Threads_running <= Threads_connected

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

2.1 thread cache 源码分析

channel_info 连接信息
waiting_channel_info_list channel_info的等待链表
COND_thread_cache block线程被唤醒的信号量,唤醒后从waiting_channel_info_list取出头部channel_info建立新连接
blocked_pthread_count 被block的线程数
max_blocked_pthreads 被block的最大线程数,也就是thread_cache_size
wake_pthread waiting_channel_info_list的链表长度
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 信号量

3.每个连接的限制

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

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

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

1. GRANT
2. priv_type [(column_list)]
3. [, priv_type [(column_list)]] ...
4. ON [object_type] priv_level
5. TO user [auth_option] [, user [auth_option]] ...
6. [REQUIRE {NONE | tls_option [[AND] tls_option] ...}]
7. [WITH {GRANT OPTION | resource_option} ...]

9. resource_option: {
10. | MAX_QUERIES_PER_HOUR count
11. | MAX_UPDATES_PER_HOUR count
12. | MAX_CONNECTIONS_PER_HOUR count
13. | MAX_USER_CONNECTIONS count
14. }

16. ALTER USER 'jeffrey'@'localhost' WITH MAX_QUERIES_PER_HOUR 90;

3.1 源码分析

3.1.1 USER_RESOURCES

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


1. typedef struct user_resources {

3. /* MAX_QUERIES_PER_HOUR */
4. uint questions;

6. /* MAX_UPDATES_PER_HOUR */
7. uint updates;

9. /* MAX_CONNECTIONS_PER_HOUR */
10. uint conn_per_hour;

12. /* MAX_USER_CONNECTIONS */
13. uint user_conn;

15. enum {QUERIES_PER_HOUR= 1, UPDATES_PER_HOUR= 2, CONNECTIONS_PER_HOUR= 4,
16. USER_CONNECTIONS= 8};
17. uint specified_limits;
18. } USER_RESOURCES;

3.1.2 ACL_USER

保存用户认证相关信息的类 USER_RESOURCES 是它的成员属性

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

调用栈如下


1. main

3. mysqld_main

5. acl_init

7. acl_reload

9. 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 对象绑定


1. typedef struct user_conn {

3. char *user;

5. char *host;

7. ulonglong reset_utime;

9. size_t len;

11. /* 当前用户连接数 */
12. uint connections;

14. /* 每小时连接数,请求数,更新数使用情况(实时更新) */
15. uint conn_per_hour, updates, questions;

17. /* 本用户资源限制 */
18. USER_RESOURCES user_resources;
19. } USER_CONN;

get_or_create_user_conn 调用栈如下


1. handle_connection

3. thd_prepare_connection(thd)

5. login_connection

7. check_connection

9. acl_authenticate

11. 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

调用栈如下


1. handle_connection

3. thd_prepare_connection(thd)

5. login_connection

7. check_connection

9. acl_authenticate

11. check_for_max_user_connections

13. do_command

15. dispatch_command

17. mysql_parse

19. check_mqh

原文:http://mysql.taobao.org/monthly/2018/02/07/