Mysql-5.5.21
-
主要启动流程
main(…) : sql/main.cc
#mysqld_main(…):sql/mysqld.cc
##my_init()
##load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv)
加载配置文件my.cnf,mysql加载配置文件是有一个目录搜索顺序的,以最先找到的配置文件为准。
##sys_var_init()
##mysql_audit_initialize()
##logger.init_base()
##init_common_variables()
##init_signals()
##init_server_components():sql/mysqld.cc
完成Mysql组件的初始化,这个函数是mysql启动过程中最重要的函数。各个存储引擎的初始化就是在这里完成的,包括innodb。后面我们会详细分析这个函数。
##init_ssl()
##network_init()
这个函数主要是完成监听套接字的建立。主要就是调用socket,bind,listen等函数建立监听套接字。通常建立两个套接字,一个tcpip套接字,一个unix域套接字。
##start_signal_handler()
##servers_init(0)
##init_slave()
##create_shutdown_thread()
##start_handle_manager()
##handle_connections_sockets()
主线程进入while循环中接通过poll来接收客户请求,有请求到来时调用accept获得请求,然后新建或拿一个缓存的连接线程处理客户请求。
2.服务器组件初始化init_server_components()
init_server_components():sql/mysqld.cc
#query_cache_init()查询缓存初始化,查询缓存配置时可以选择关闭。
#xid_cache_init()
#delegates_init()
#mysql_bin_log.generate_name(opt_bin_logname, "-bin", 1, buf)
#mysql_bin_log.open_index_file(opt_binlog_index_name, ln, TRUE)
#process_key_caches(&ha_init_key_cache)
#ha_init_errors()
#plugin_init(…):sql/sql_plugin.cc
存储引擎初始化的地方,在plugin_init(…)中,循环依次调用plugin_initialize(…)初始化各个插件存储引擎.最终通过调用sql/handler.cc中的ha_initialize_handlerton(st_plugin_int *plugin)调用每个插件存储引擎注册的初始方法,(对于innodb来说就是innobase_init())这里才是各个插件存储引擎真正完成初始化的函数。
#mysql_bin_log.open(…)
#init_max_user_conn()
#init_update_queries()
3.innodb存储引擎初始化 innobase_init():storage/innobase/handler/ha_innodb.cc
在innobase_init()中,先完成innodb各种参数的初始化,然后调用innobase_start_or_create_for_mysql()完成主要的存储引擎初始化任务。接下来我们重点看看这个函数。
innobase_start_or_create_for_mysql():storage/innobase/srv/srv0start.c
#srv_boot()
#os_aio_init(io_limit,srv_n_read_io_threads,srv_n_write_io_threads,
SRV_MAX_N_PENDING_SYNC_IOS);
#fil_init(srv_file_per_table ? 50000 : 5000,
srv_max_n_open_files);
#buf_pool_init(srv_buf_pool_size, srv_buf_pool_instances)
#fsp_init();
#log_init();
//创建10个后台IO线程,4个读线程,4个写线程
#for (i = 0; i < srv_n_file_io_threads; i++) {
n[i] = i;
os_thread_create(io_handler_thread, n + i, thread_ids + i);
}
#open_or_create_data_files(…)
#open_or_create_log_file(…)
#fil_open_log_and_system_tablespace_files()
#trx_sys_file_format_init()
#recv_recovery_from_checkpoint_start(…)
…
#recv_recovery_from_checkpoint_finish()
#trx_sys_create_doublewrite_buf()
/* Create the thread which watches the timeouts for lock waits */
#os_thread_create(&srv_lock_timeout_thread, NULL,
thread_ids + 2 + SRV_MAX_N_IO_THREADS);
/* Create the thread which warns of long semaphore waits */
#os_thread_create(&srv_error_monitor_thread, NULL,
thread_ids + 3 + SRV_MAX_N_IO_THREADS);
/* Create the thread which prints InnoDB monitor info */
#os_thread_create(&srv_monitor_thread, NULL,
thread_ids + 4 + SRV_MAX_N_IO_THREADS);
/* Create the master thread which does purge and other utility
operations */
#os_thread_create(&srv_master_thread, NULL, thread_ids
+ (1 + SRV_MAX_N_IO_THREADS))
#os_thread_create(&srv_purge_thread, NULL, NULL)
4.网络初始化network_init()
network_init() : sql/mysqld.cc
#error= getaddrinfo(my_bind_addr_str, port_buf, &hints, &ai);//获得地址信息
# ip_sock= socket(a->ai_family, a->ai_socktype, a->ai_protocol);
# (void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg));
//设置socket选项
# ret= bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 )
# listen(ip_sock,(int) back_log)
//至此ip 监听socket建立起来,可以用来接收远程的tcp/ip连接请求。接下
//来将建立unix域套接字。用来接收本机的连接请求。
#unix_sock= socket(AF_UNIX, SOCK_STREAM, 0)
#(void) setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
sizeof(arg));
#bind(unix_sock, reinterpret_cast<struct sockaddr *>(&UNIXaddr),
sizeof(UNIXaddr)
#listen(unix_sock,(int) back_log)
//至此,unix域监听套接字建立成功。
5.处理连接请求handle_connections_sockets()
handle_connections_sockets():sql/mysqld.cc
# fds[socket_count].fd= ip_sock;
# fds[socket_count].events= POLLIN;
# socket_count++;
# fds[socket_count].fd= unix_sock;
# fds[socket_count].events= POLLIN;
# socket_count++;
//将之前network_init()中建立的两个监听描述符ip_sock和unix_sock加入描述符//集fds
#retval= poll(fds, socket_count, -1);//poll轮询描述符集
…
#for(…){
# new_sock= accept(sock, (struct sockaddr *)(&cAddr),&length);//接收到新连接
#}
# thd= new THD//创建THD类
# my_net_init(&thd->net,vio_tmp)//初始化NET类
# create_new_thread(thd);//交给连接线程处理连接在create_new_thread函数中如果有缓存的线程,就交由缓存的线程处理,否则新建一个线程处理。建立新连接线程的函数为create_thread_to_handle_connection:sql/mysqld.cc。建立新连接的语句如下:mysql_thread_create(key_thread_one_connection,
&thd->real_id, &connection_attrib,
handle_one_connection,
(void*) thd))
因此每个连接线程的入口方法为handle_one_connection:sql/sql_connect.cc,而此方法仅仅是又调用了do_handle_one_connection(thd):sql/sql_connect.cc。do_handle_one_connection函数在循环中调用do_command(thd):sql/sql_parse.cc
6连接线程请求处理流程
pthread_handler_t handle_one_connection(void *arg) :sql/sql_connect.cc
调用
do_handle_one_connection(thd) :sql/sql_connect.cc
在循环中调用
do_command(thd) :sql/sql_parse.cc
因此一个客户连接的每条命令都会调用一次do_command(thd). 这个函数中的
packet_length= my_net_read(net)读取网络请求数据。如果客户端没有请求数据到达那么连接线程就会阻塞在这个函数调用中。对读取的请求数据适当处理后调用
dispatch_command(command, thd, packet+1, (uint) (packet_length-1)) :
switch (command)
{
case COM_QUERY:
{
… …
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);//详见后面讲解
… …
}
}
mysql_parse():sql/sql_parse.cc
#bool err= parse_sql(thd, parser_state, NULL);//完成sql语句的词法分析和语法分析,生成语法树,thd->lex保存着语法树
#error= mysql_execute_command(thd);
mysql_execute_command(thd):sql/sql_parse.cc
{
switch (lex->sql_command)
{
case SQLCOM_SELECT:
{
… …
res= execute_sqlcom_select(thd, all_tables);
… …
}
case SQLCOM_UPDATE:
{
… …
up_result= mysql_update(thd, all_tables,
select_lex->item_list,
lex->value_list,
select_lex->where,
select_lex->order_list.elements,
select_lex->order_list.first,
unit->select_limit_cnt,
lex->duplicates, lex->ignore,
&found, &updated);
… …
}
}
}
execute_sqlcom_select(thd, all_tables)调用
handle_select(thd, lex, result, 0)调用
mysql_select(thd, &select_lex->ref_pointer_array,
select_lex->table_list.first,
select_lex->with_wild, select_lex->item_list,
select_lex->where,
select_lex->order_list.elements +
select_lex->group_list.elements,
select_lex->order_list.first,
select_lex->group_list.first,
select_lex->having,
lex->proc_list.first,
select_lex->options | thd->variables.option_bits |
setup_tables_done_option,
result, unit, select_lex)
# join->prepare(rref_pointer_array, tables, wild_num,
conds, og_num, order, group, having, proc_param,
select_lex, unit);
#err= join->optimize()
#join->exec();调用
error= do_select(curr_join, curr_fields_list, NULL, procedure);