写在前面
阅读本教程你需要提前知道的
1.本文参考的源码版本 mysql-5.7.38, 可能和8.0有区别, 请以实际版本为主.
2.不含初始化数据库(bootstrap/initialize), 不含win32 (_WIN32), 不含ia64, 尽量不含performance_schema
3.基础编程知识(c/c ,py,go,shell,js等均可) (*表示取指针的值, &表示取值的地址(指针))
4.不含流程图(懒得画)
5.不会详细讲具体的函数(太多了)
6.能力有限, 错误之处多多包涵.
启动流程
主要是sql/mysqld.cc中的mysqld_main , 其它文件尽量不要涉及.
mysqld_main(主函数)
int mysqld_main(int argc, char **argv)
入口函数, 为啥不是main呢(1.#define mysqld_main main 2.gcc -nostartfiles -e mysqld_main). (我想起个段子 #define mian main)
实际上是在 sql/main.cc 中定义的
代码语言:c复制extern int mysqld_main(int argc, char **argv);
int main(int argc, char **argv)
{
return mysqld_main(argc, argv);
}
//以下函数未特殊说明均在mysqld_main函数里面调用的
初始化参数
就是执行mysqld --datadir=xx --basedir=xx 的这个参数
代码语言:txt复制 orig_argc= argc;
orig_argv= argv;
my_getopt_use_args_separator= TRUE;
my_defaults_read_login_file= FALSE;
my_getopt_use_args_separator= FALSE;
defaults_argc= argc;
defaults_argv= argv;
remaining_argc= argc;
remaining_argv= argv;
system_charset_info (系统字符集)
system_charset_info= &my_charset_utf8_general_ci;
local_message_hook 定义日志打印
//error_log_print(enum loglevel level, const char *format, va_list args)
local_message_hook= error_log_print;
初始化performance instrument (可选)
init_pfs_instrument_array();
所以参数 performance_schema
是只读的, 只能开机设置
ho_error参数检查(兼容以前的参数)
ho_error= handle_early_options();
忽略未知参数,添加系统变量,添加命令行选项, 检查bootstrap和initialize/opt_initialize_insecure.
应该是为了兼容性的
init_sql_statement_names
这一块不熟... (LEX后面再看)
代码语言:txt复制 for (i= 0; i < ((uint) SQLCOM_END 1); i )
sql_statement_names[i]= empty;
sys_var_init
系统变量初始化 (必须先初始化system_charset_info (第三步))
adjust_related_options
适配依赖关系选项(顺序不能改变,因为有依赖关系)
代码语言:txt复制 adjust_open_files_limit(requested_open_files);
adjust_max_connections(*requested_open_files);
adjust_table_cache_size(*requested_open_files);
adjust_table_def_size(); //依赖table_cache_size
query_logger.init
初始化查询日志 slow log 和 general log 都需要这个玩意
判断ho_error
刚才检查的参数, 现在才判断...
代码语言:txt复制flush_error_log_messages();
exit (MYSQLD_ABORT_EXIT);
初始化命令行参数
init_common_variables
后初始的命令行参数, 所以命令行参数的优先级要高.
my_init_signals
初始化信号量的, 比如 SIGUSR1 is used to interrupt the socket listener
my_thread_attr_setstacksize
设置线程的堆栈大小的. pthread_attr_getstacksize
keyring_migration
和参数 --keyring-migration-xxxx 相关的, 没用过, 就不做介绍了.
opt_keyring_migration_source
opt_keyring_migration_destination
migrate_connect_options
my_setwd(mysql_real_data_home,MYF(MY_WME))
设置数据库数据目录
bin_log 选项检查
如果启用binlog就必须要server-id
代码语言:txt复制(opt_bin_log && !(server_id_supplied) )
init_server_components
使用函数初始化一些内部的东西. 具体如下, 挺多的
代码语言:txt复制mdl_init() //Initialize the metadata locking subsystem
partitioning_init()
table_def_init() | hostname_cache_init(host_cache_size)
my_timer_initialize()
init_server_query_cache() //这里初始的查询缓存,8.0没有
randominit()
setup_fpu()
init_slave_list //(ifdef HAVE_REPLICATION)
init_server_auto_options
就是server-uuid 唯一表示服务器的, 没得就会自动创建, 路径在@@datadir 下面的 auto.cnf
gtid_state->init()
初始化gtid(必须先初始化server-uuid)
read_gtid_executed_from_table
从mysql/gtid_executed.ibd表读取gtid集
初始化GLOBAL.GTID_EXECUTED和GLOBAL.GTID_PURGED
计算方式就不讲了, 太多了..... 懒得看.
init_ssl
初始化ssl 也挺复杂的....
network_init
初始化网络, 也是一堆... 成功就返回false (同init_ssl)
内存管理相关的函数
代码语言:txt复制 my_str_malloc= &my_str_malloc_mysqld; //my_malloc
my_str_free= &my_str_free_mysqld; //my_free
my_str_realloc= &my_str_realloc_mysqld; //my_realloc
error_handler_hook
跟客户端错误信息有关的
代码语言:txt复制error_handler_hook= my_message_sql
create_pid_file
创建pid文件, 并讲pid(getpid())写入文件(mysql_file_write), 然后关闭文件(mysql_file_close)
reload_optimizer_cost_constants
加载优化器成本配置表, 就是 mysql.server_cost 和 mysql.engine_cost
相关代码: sql/opt_costconstantcache.cc // c 写的
mysql_rm_tmp_tables
删除临时表
acl_init
初始化权限(user/db) sql/auth/sql_auth_cache.cc
my_tz_init
初始化时区
grant_init
初始化权限(table/column)
servers_init
servers_init(0) 初始化server, 0 表示要初始化 //bool servers_init(bool dont_read_servers_table)
具体内容如下:
代码语言:txt复制mysql_rwlock_init //init the mutex
my_hash_init //initialise our servers cache
init_sql_alloc //Initialize the mem root for data
init_status_vars
初始化服务器状态 sql/sql_show.cc //c 写的
check_binlog_cache_size
就是检查BINLOG_CACHE_SIZE是不是比MAX_BINLOG_CACHE_SIZE大, 是的话, 就取MAX_BINLOG_CACHE_SIZE的值 (两者取其小)
check_binlog_stmt_cache_size
就是检查 BINLOG_STMT_CACHE_SIZE是不是比MAX_BINLOG_STMT_CACHE_SIZE大.
binlog_unsafe_map_init
定义binlog异常, 在sql/sql_lex.cc里的
set_slave_skip_errors
就是配置文件里面的 slave-skip-errors=xxxx;
init_slave
初始化从库信息, 前提是server_id != 0 //其实前面还设置了opt_skip_slave_start, 但是没必要. 太罗嗦了.
initialize_information_schema_acl
初始化information_schema. //sql/auth/sql_auth_cache.cc
execute_ddl_log_recovery
执行内部DDL日志恢复.
read_init_file (可选)
就是启动的时候指定的 --init-file=name 这个是不需要权限认证的, 所以可以用来更改密码.
start_handle_manager
启动handle manager线程.(具体的后面再慢慢看)
create_compress_gtid_table_thread
创建压缩gtid的线程
sql_print_information
主要是error_log_print
start_processing_signals
设置进程信号量
set_super_read_only_post_init();
初始化super_read_only的值
server_operational_state
设置服务器状态 server_operational_state=SERVER_OPERATING //默认是SERVER_BOOTING 关闭的时候是SERVER_SHUTTING_DOWN, 下面就会说的
socket_listener_active
设置mysql监听状态 socket_listener_active= true;
opt_daemonize
就是启动时候的参数 --daemonize 俗称 放后台
代码语言:txt复制mysqld::runtime::signal_parent(pipe_write_fd,1); //1 means initialization complete and the serveris ready to accept client connections
mysqld_socket_acceptor->connection_event_loop
就是循环处理客户端连接. 数据库会一直'堵'在这里
代码语言:txt复制 Connection_handler_manager *mgr= Connection_handler_manager::get_instance();
while (!abort_loop)
{
Channel_info *channel_info= m_listener->listen_for_connection_event();
if (channel_info != NULL)
mgr->process_new_connection(channel_info);
}
server_operational_state= SERVER_SHUTTING_DOWN
从这开始就是关闭流程了, 不在本文范围内
总结
看完差不多就忘完了. 基本上都是各种初始化. 而且是c和c 混合写的. 感兴趣的可以使用strace验证下是不是这样的.
1.开启binlog必须要server-id!=0
2.可以通过--init-file修改root密码
3.可以通过skip-slave-start参数禁止slave自动启动
4.ddl log恢复是数据库启动的时候自动做的
5.权限分为系统级(user/db)权限和表级权限(table/column)
参考文献
https://dev.mysql.com/doc/dev/mysql-server/latest/
https://dev.mysql.com/doc/refman/5.7/en/preface.html
https://dev.mysql.com/doc/internals/en/