MySQL 源码解读 -- 连接管理

连接处理

创建连接

主要代码在sql/mysqld.cc中(create_new_thread/create_thread_to_handle_connection),精简后的代码如下:

1
2
3
4
5
6
7
8
9
static void create_new_thread(THD *thd) {
NET *net=&thd->net;
if (connection_count >= max_connections + 1 || abort_loop) { // 看看当前连接数是不是超过了系统配置允许的最大值,如果是就断开连接。
close_connection(thd, ER_CON_COUNT_ERROR, 1);
delete thd;
}
++connection_count;
thread_scheduler.add_connection(thd); // 将新连接加入到thread_scheduler的连接队列中。
}

创建连接处理线程

而thread_scheduler转而调用create_thread_to_handle_connection,精简后的代码如下:

1
2
3
4
5
6
7
8
9
void create_thread_to_handle_connection(THD *thd) {
if (cached_thread_count > wake_thread) { //看当前工作线程缓存(thread_cache)中有否空余的线程
thread_cache.append(thd);
pthread_cond_signal(&COND_thread_cache); // 有的话则唤醒一个线程来用
} else {
threads.append(thd);
pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))); //没有可用空闲线程则创建一个新的线程
}
}

连接处理

连接使用handle_one_connection函数,精简代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
pthread_handler_t handle_one_connection(void *arg)
{
thread_scheduler.init_new_connection_thread(); // 初始化线程预处理操作

setup_connection_thread_globals(thd); //载入一些Session级变量

for (;;) {
lex_start(thd); //初始化LEX词法解析器
login_connection(thd); // 进行连接身份验证
prepare_new_connection_state(thd); // 初始化线程Status,即show status看到的
do_command(thd); // 处理命令
end_connection(thd); //没事做了关闭连接,丢入线程池

}
}


void do_handle_one_connection(THD *thd_arg)
{
THD *thd= thd_arg;

thd->thr_create_utime= my_micro_time(); // 更新thd 时间

//// 初始化线程预处理操作
if (MYSQL_CALLBACK_ELSE(thd->scheduler, init_new_connection_thread, (), 0))
{
close_connection(thd, ER_OUT_OF_RESOURCES);
statistic_increment(aborted_connects,&LOCK_status);
MYSQL_CALLBACK(thd->scheduler, end_thread, (thd, 0));
return;
}

/*
If a thread was created to handle this connection:
increment slow_launch_threads counter if it took more than
slow_launch_time seconds to create the thread.
*/
if (thd->prior_thr_create_utime)
{
ulong launch_time= (ulong) (thd->thr_create_utime -
thd->prior_thr_create_utime);
if (launch_time >= slow_launch_time*1000000L)
statistic_increment(slow_launch_threads, &LOCK_status);
thd->prior_thr_create_utime= 0;
}

/*
handle_one_connection() is normally the only way a thread would
start and would always be on the very high end of the stack ,
therefore, the thread stack always starts at the address of the
first local variable of handle_one_connection, which is thd. We
need to know the start of the stack so that we could check for
stack overruns.
*/
thd->thread_stack= (char*) &thd;
if (setup_connection_thread_globals(thd))
return;


for (;;)
{
bool rc;

thd->ppi_thread= PPI_THREAD_CALL(create_thread)();
thd->ppi_transaction=
PPI_THREAD_CALL(get_transaction_context)(thd->ppi_thread);

NET *net= &thd->net;
mysql_socket_set_thread_owner(net->vio->mysql_socket);

//// 准备connection
rc= thd_prepare_connection(thd);
if (rc)
goto end_thread;

while (thd_is_connection_alive(thd))
{
mysql_audit_release(thd);
if (do_command(thd))
break;
}
end_connection(thd);

end_thread:
close_connection(thd);
if (MYSQL_CALLBACK_ELSE(thd->scheduler, end_thread, (thd, 1), 0))
return; // Probably no-threads

/*
If end_thread() returns, we are either running with
thread-handler=no-threads or this thread has been schedule to
handle the next connection.
*/
thd= current_thd;
thd->thread_stack= (char*) &thd;
}
}

thd_prepare_connection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool thd_prepare_connection(THD *thd)
{
bool rc;
lex_start(thd); //初始化LEX词法解析器
rc= login_connection(thd); //
if (rc)
return rc;

MYSQL_CONNECTION_START(thd->thread_id, &thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip);

//關鍵這個函數
prepare_new_connection_state(thd);
return FALSE;
}

login_connection
-- check_connection -- 设置vio, 检查main_security_ctx, vio_keepalive, 申请thd->packet 内存, acl_authenticate
-- autit log 开始
-- thd->protocol->end_statement(); lex_end

// 初始化session的status
prepare_new_connection_state
--