1 从mysqld的main()函数开始

本文详细介绍了MySQL服务器启动过程的关键步骤,包括初始化系统变量、加载配置文件、创建监听套接字及处理客户端连接等核心环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、sql/main.cc

extern int mysqld_main(int argc, char **argv);

int main(int argc, char **argv)
{
  return mysqld_main(argc, argv);
}
这就是mysqld的main()函数,它只是调用了mysql_main()。


二、mysqld_main

mysqld_main()位于sql/mysqld.cc中。sql/mysqld.cc中包含的头文件基本位于include/和sql/这两个文件夹下。下面看mysqld_main()的执行流程。


1、my_init()——init my_sys library & pthreads

其中static MYSQL_FILE instrumented_stdin.m_file= stdin;调用my_thread_global_init(),其中设置pthread_key_create(&THR_KEY_mysys, NULL)),mysql_mutex_init()初始化mutex(THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_charset, THR_LOCK_threads以及之后的THR_LOCK_lock, THR_LOCK_isam,HR_LOCK_heap等)(见备注1),调用my_thread_init()初始化THR_KEY_mysys(见备注2),mysql_cond_init()初始化THR_COND_threads。


备注1:mysql_mutex_init(key_THR_LOCK_malloc, &THR_LOCK_malloc, MY_MUTEX_INIT_FAST)

key_THR_LOCK_malloc为PSI_mutex_key(unsigned int),THR_LOCK_malloc为struct mysql_mutex(主要成员是pthread_mutex_t m_mutex),该函数主要是初始化m_mutex。

备注2:my_thread_init()

执行pthread_setspecific(THR_KEY_mysys, tmp)(tmp为struct st_my_thread_var *);初始化这个tmp。


2、load_defaults()->my_load_defaults()

调用init_alloc_root()初始化MEM_ROOT *alloc;dirs=init_default_directories(&alloc)初始化目录数组(可能存放my.cnf的目录),数据保存在alloc管理的内存block中。my_init_dynamic_array()初始化用于存放argv参数的动态数组DYNAMIC_ARRAY args;my_search_option_files()先根据目录数组找到my.cnf(打开为MYSQL_FILE* fp),并提取参数,但尚未处理


3、init_sql_statement_names

初始化LEX_CSTRING sql_statement_names[ ]数组,内部存放着诸如"create_db" "insert"等对应于SQL语句的字符串。


4、sys_var_init

调用my_hash_init()初始化HASH* system_variable_hash;调用mysql_add_sys_var_chain(all_sys_vars.first)(注意all_sys_vars此时并不为空,它已经被初始化了,什么时候初始化的,见备注3),它将这些变量的指针存在system_variable_hash哈希表中。


备注3:all_sys_vars.first的类型是sys_var *,这是一个class,具有构造函数。在sql/sys_vars.cc中具有多个static变量(当我们在客户端使用SHOW VARIABLES LIKE 'perf%';时就可以看到这些变量),这些变量在程序一开始就得到了初始化,并链入了all_sys_vars.first中。


5、my_getopt_register_get_addr()设置getopt_get_addr = null(getopt_get_addr 类型为my_getopt_value,这是一个函数指针)。


6、sys_var_add_options(&all_early_options, sys_var::PARSE_EARLY)

all_early_options为DYNAMIC_ARRAY,该函数将all_sys_vars链表中每个sys_var的option字段的地址存入该动态数组all_early_options中。


7、buffered_logs.init()初始化Buffered_logs buffered_logs的成员MEM_ROOT m_root(注意Buffered_logs是一个类)。(这是第二次出现MEM_ROOT了,这里它应当是用于管理log缓存的)。备注Buffered_logs::print()就是用于将缓存的日志写到日志文件中。


8、my_getopt_error_reporter= buffered_option_error_reporter前者是个函数指针,后者这个函数作用就是将出错信息缓存到上一步中的buffered_logs中。


9、handle_options(&remaining_argc, &remaining_argv,(my_option*)(all_early_options.buffer), NULL)
这里remaining_argv是命令行和my.cnf中所有参数的集合,all_early_options是每个sys_var的option字段都存在此处,很明显该函数是处理各参数;同时可看出上一步的设置的作用:将解析参数时的出错信息也记录到日志中。不过这里并没有处理完(实际上我设置的参数和option字段都没关系,所以这里并没有设置option字段中的值)。在此之后,delete all_early_options。


10、init_error_log_mutex()初始化LOCK_error_log(类型为mysql_mutex_t)


11、mysql_audit_initialize()——Initialize Audit global variables:初始化LOCK_audit_mask锁(mysql_mutex_t)


12、logger.init_base()

logger的类型是class Logger(位于sql/log.h),Class which manages slow, general and error log event handlers。初始化log handler (class Log_to_file_event_handler,其中元素有mysql_log和mysql_slow_log)。


13、init_common_variables()见第三部分。

14、init_signals()信号相关初始化。

15、init_server_components()

table_def_init()初始化table_def_cache哈希表;hostname_cache_init()初始化hostname_cache;query_cache_init();ifdef HAVE_REPLICATION,init_slave_list(); process_key_caches(&ha_init_key_cache)初始化key caches;plugin_init(&remaining_argc, remaining_argv, ...)初始化剩余的参数(这是第三次处理),这些参数,这些与存储引擎相关,见下一篇,该函数还创建了很多线程;ha_init()初始化存储引擎;Set the default storage engine,并设置到global_system_variables.table_plugin中;ha_recover();


16、network_init()
创建tcp套接口ip_sock并监听,创建unix套接口unix_sock(需要创建文件mysqld.sock,注意需要给mysql用户添加文件夹写入权限)。


17、start_signal_handler()

创建线程signal_thread,线程处理函数为signal_hand();主线程等待该线程完成一些工作。signal_hand见第四部分。


18、mysql_rm_tmp_tables() || acl_init(opt_noacl) || my_tz_init()与数据表相关

acl_init():Initialize structures responsible for user/db-level privilege checking and load privilege information for them from tables in the 'mysql' database;

my_tz_init():Initialize time zone support infrastructure.


19、grant_init():Initialize structures responsible for table/column-level privilege checking and load information for them from tables in the 'mysql' database

20、servers_init(): Initialize structures responsible for servers used in federated server scheme information for them from the server table in the 'mysql' database

21、init_status_vars():Make all_status_vars[ ] usable for SHOW STATUS(注意add_status_vars()已在init_common_variables()中调用。)只是做了排序。

22、binlog_unsafe_map_init()?

23、init_slave():Initialize slave structures;其中pthread_key_create(&RPL_MASTER_INFO, NULL);初始化Master_info *active_mi。

24、execute_ddl_log_recovery()

25、Events::init(opt_noacl || opt_bootstrap)

26、start_handle_manager()实际上本例什么也没做(因为flush_time=0)

27、mysql_cond_signal(&COND_server_started):如果有某线程等待主线程启动,此时主线程通知它已启动。

28、handle_connections_sockets()

Handle new connections and spawn new process to handle them. 其中循环调用poll一直等待客户端连接。当连接到来时,accept,vio_new,my_net_init(&thd->net, vio_tmp),create_new_thread()处理连接;然后主线程继续循环等待新的连接。注意:对于本地,使用的是unix套接字。

备注:vio_new()初始化一个struct st_vio,设置了write、read等处理函数,封装了buffer相关处理;my_net_init()用这个st_vio去初始化thd->net(类型为struct st_net,该结构可看做client connection descriptor)。这样对io的处理进行了虚拟化。


29、当上述循环退出时,主线程等待ready_to_exit为1时,调用clean_up()和mysqld_exit()退出。


三、init_common_variables

1、init_thread_environment()初始化多个线程相关的锁;初始化connection_attrib(类型为pthread_attr_t);pthread_key_create(&THR_THD, NULL)。

2、初始化mysql_bin_log(类型为MYSQL_BIN_LOG,class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG)

3、设置pidfile_name为“hostname+.pid”,本例为“ubuntu.pid”。

4、default_storage_engine= const_cast<char *>("InnoDB")

5、add_status_vars(status_vars)创建all_status_vars这个动态数组,将status_vars各个数组的各项的指针存入动态数组中,类似于前面的all_early_options。

6、get_options(&remaining_argc, &remaining_argv)这里继续处理命令行和my.cnf中设置的参数。操作的是struct my_option my_long_options[ ]。不过还有一些关于innoDB的参数没有被处理。

7、设置open_files_limit(本例为1024)

8、unireg_init()设置error_handler_hook = my_message_stderr;my_disable_async_io=1

9、设置my_default_lc_messages为"en_US";global_system_variables.lc_messages= my_default_lc_messages

10、init_errmessage()初始化DEFAULT_ERRMSGS(即my_default_lc_messages->errmsgs->errmsgs,类型为char**,指向各种错误提示字符串)

11、init_client_errs():Register client error messages for use with my_error()

12、mysql_client_plugin_init():Initializes the client plugin layer

13、lex_init()初始化SYMBOL symbols[ ]

14、item_create_init():Load the hash table for native functions;将Native_func_registry func_array[ ]中的各项(好像是各种函数,位于sql/item_create.cc中)加入native_functions_hash这个哈希表。

15、item_init()初始化hash_user_locks哈希表和LOCK_user_locks锁。

16、get_charset_by_csname()初始化default_charset_info(本例初始化为utf8);初始化character_set_filesystem(本例为binary);my_locale_by_name()设置my_default_lc_time_names(本例为en_US)。这些量都被保存于global_system_variables中。

17、本例设置opt_logname为"/var/lib/mysql2/ubuntu.log",opt_slow_logname为"/var/lib/mysql2/ubuntu-slow.log"

18、bitmap_init()设置位图。

19、my_dboptions_cache_init()初始化dboptions这个哈希表。

20、初始化table_alias_charset(本例为&my_charset_bin,为"binary")。


四、signal_hand()

1、my_thread_init():以后分析。

2、init_thr_alarm():Setup alarm handler

3、 create_pid_file():创建pid.fill。(之后就可以唤醒主线程了)

4、之后是一直等待信号的到来,并相应处理。因此次线程就是一个信号处理线程。


本篇只是对mysqld的启动做了一个大概的认识,很多函数并未深入下去,下一篇将从客户端连接后,mysqld的处理来继续学习。看了mysqld的启动,觉得mysql确实是一个很庞大的东西,要学习的东西很多,只能在此先给自己加个油。

<think>我们正在解决MySQL忘记密码后使用`--skip-grant-tables`启动时卡住的问题。根据引用内容,特别是引用[1]和[5],MySQL8在配置文件中添加`skip-grant-tables`可能导致服务无法启动。而引用[2]和[5]提供了在命令行中启动时添加额外参数的方法。 用户描述:使用`--skip-grant-tables`启动MySQL实例时进程卡住,无法继续操作。 可能的原因及解决方案: 1. **参数组合问题**:MySQL8需要同时使用`--skip-grant-tables`和`--shared-memory`(如引用[5]所示),有时还需要指定`--datadir`(数据目录)。 2. **初始化问题**:引用[2]提到,在运行`mysqld --console --skip-grant-tables`之前,需要先执行`mysqld --initialize --console`进行初始化(注意:初始化会生成临时密码,但这里我们仅为了解决启动问题)。 3. **服务未完全停止**:在启动无验证模式之前,必须确保MySQL服务已完全停止。 具体步骤: ### 解决方案:分步操作指南 #### 步骤1:停止MySQL服务(管理员权限) ```cmd net stop mysql ``` 如果服务名不是`mysql`,请使用实际服务名(如`mysql80`)。可通过`sc query`查看服务名。 #### 步骤2:检查并结束残留进程 使用任务管理器结束所有`mysqld.exe`进程。 #### 步骤3:尝试安全模式启动(带控制台输出) ```cmd cd "C:\Program Files\MySQL\MySQL Server 8.0\bin" # 替换为你的MySQL bin目录路径 mysqld --console --skip-grant-tables --shared-memory ``` 如果上述命令卡住或失败,尝试指定数据目录(引用[5]): ```cmd mysqld --console --skip-grant-tables --shared-memory --datadir="C:\ProgramData\MySQL\MySQL Server 8.0\Data" ``` 注意:`--datadir`的路径需要根据实际安装配置调整(通常位于`C:\ProgramData\MySQL\MySQL Server 8.0\Data`)。 #### 步骤4:新开窗口重置密码(无需密码登录) 保持步骤3的窗口运行,新开管理员命令行: ```cmd mysql -u root ``` 在MySQL命令行中依次执行: ```sql FLUSH PRIVILEGES; -- 刷新权限 ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码'; -- MySQL 8.0+修改密码 ``` 如果出现错误,尝试使用传统方法(引用[3]): ```sql UPDATE mysql.user SET authentication_string='' WHERE User='root'; -- 先清空密码 FLUSH PRIVILEGES; EXIT; ``` 然后重启MySQL服务(正常模式),再使用空密码登录并设置新密码。 #### 步骤5:重启MySQL服务 1. 在`--skip-grant-tables`运行窗口按`Ctrl+C`停止 2. 重新启动服务: ```cmd net start mysql ``` ### 常见问题处理表 | 问题现象 | 解决方案 | |---------|---------| | 启动时提示`Can't create/write to file` | 检查`--datadir`路径是否正确,确保有写入权限 | | `ALTER USER`报错`ERROR 1290` | 先执行`FLUSH PRIVILEGES;`再修改密码 | | 修改密码后登录提示`Access denied` | 检查密码字段,MySQL 8.0+使用`caching_sha2_password`插件,可尝试:<br>`ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码';` | | 服务无法正常启动 | 检查错误日志(通常在data目录下的`.err`文件) | ### 关键注意事项 - **路径一致性**:`--datadir`必须与MySQL配置文件(`my.ini`)中设置的数据目录一致[^5]。 - **密码强度**:MySQL 8.0默认要求强密码(大小写字母+数字+特殊字符,长度至少8位)。 - **操作顺序**:重置密码后必须重启服务才能生效。 ### 相关问题 1. **如何确定MySQL的数据目录路径?** [通过配置文件`my.ini`或服务启动参数获取] 2. **MySQL 8.0修改密码时出现`ERROR 1396`如何解决?** [用户主机名不匹配问题处理方案] 3. **跳过授权表启动后仍无法修改密码怎么办?** [权限系统表损坏的修复方法] 4. **Windows系统下如何查看MySQL错误日志?** [默认路径与日志分析技巧] 5. **使用`--skip-grant-tables`启动后如何避免安全风险?** [配合`--skip-networking`禁止远程访问] > 提示:操作前备份数据目录,避免误操作导致数据损坏。90%的启动卡住问题可通过指定`--datadir`解决[^2][^5]。</think>### MySQL忘记密码后使用 `--skip-grant-tables` 启动卡住的解决方案 当使用 `--skip-grant-tables` 启动MySQL卡住时,通常是由于权限问题、数据目录不匹配或服务冲突导致的。以下是逐步解决方案: #### 🔧 步骤1:完全停止MySQL服务 ```powershell # 管理员权限运行 net stop mysql # 强制结束所有相关进程 taskkill /F /IM mysqld.exe ``` #### 📂 步骤2:确认数据目录路径(关键) ```powershell # 查看MySQL默认数据目录(通常位置) dir "C:\ProgramData\MySQL\MySQL Server 8.0\Data" ``` 如果目录不存在,检查: 1. MySQL配置文件 `my.ini` 中的 `datadir` 设置 2. 服务注册表中的路径 `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MySQL` #### ⚙ 步骤3:使用完整参数启动(解决卡住问题) ```powershell # 组合使用所有必要参数(根据引用[5]) mysqld --console --skip-grant-tables --shared-memory --skip-networking --datadir="你的实际数据目录路径" ``` **参数说明:** - `--skip-networking`:禁止远程连接(安全必须,引用[4]) - `--shared-memory`:解决启动失败(引用[2]) - `--datadir`:指定正确数据目录(关键,引用[5]) #### 🔑 步骤4:新终端执行密码重置 ```powershell # 新开管理员终端 mysql -u root # MySQL命令序列 FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码'; # 如果报错使用(引用[3]): UPDATE mysql.user SET authentication_string='' WHERE User='root'; FLUSH PRIVILEGES; EXIT; ``` #### 🔄 步骤5:重启服务并验证 ```powershell # 结束无验证模式进程 taskkill /F /IM mysqld.exe # 正常启动服务 net start mysql # 验证登录 mysql -u root -p ``` ### 常见问题排查表 | 问题现象 | 解决方案 | |---------|---------| | 启动后无响应 | 添加 `--console` 参数查看实时日志(引用[2]) | | 端口占用冲突 | `netstat -ano | findstr :3306` 结束占用进程 | | 数据目录权限不足 | 右键目录 → 属性 → 安全 → 添加`NETWORK SERVICE`完全控制 | | 配置文件冲突 | 启动时指定配置文件 `--defaults-file="C:\my.ini"` | | 表空间损坏 | 启动前执行 `mysqld --initialize-insecure`(会重置数据) | ### 💡 关键注意事项 1. **路径一致性**:`--datadir` 必须与实际数据目录完全匹配(大小写敏感) 2. **参数顺序**:`--skip-grant-tables` 必须放在命令开头 3. **安全防护**:操作期间启用防火墙阻止3306端口访问 4. **版本差异**: - MySQL 5.7 使用 `password()` 函数 - MySQL 8.0 需用 `ALTER USER`(引用[3]) 5. **初始化问题**:若数据目录为空,需先执行 `mysqld --initialize`(引用[2]) > 实际案例:某用户因未指定 `--datadir` 导致启动卡住,添加 `--datadir="C:\ProgramData\MySQL\MySQL Server 8.0\Data"` 后成功重置密码(引用[5])。 ### 📌 相关问题 1. **如何确认MySQL的准确数据目录路径?** [配置文件解析与服务注册表查询方法] 2. **使用 `--skip-grant-tables` 启动后仍无法修改密码怎么办?** [权限系统表损坏的修复方案] 3. **MySQL 8.0 重置密码出现 `ERROR 1396` 如何解决?** [用户主机名不匹配问题的处理] 4. **跳过授权表启动后如何避免安全风险?** [配合 `--skip-networking` 的最佳实践] 5. **Windows 系统下如何永久保存MySQL配置参数?** [my.ini 配置文件的详细语法指南]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值