- GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
- GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。
前言
你知道MySQL启停都做了些什么吗? 启动的时候初始化配置文件,读取redo配合binlog进行事务recover;停止的时候好像没有啥操作可做;印象中除了这些,就再没有了,至少在今天之前,我是这么认为的,我是真的肤浅。
今天就来聊一聊MySQL关于启停的常规操作。
停止过程
先说一下比较简单的停止过程
-
可以由具有shutdown权限的用户在客户端执行
shutdown
命令关闭,或者是由mysqladmin shutdown
关闭; -
如果是由客户端发起的shutdown,则需要创建单独的线程进行关闭后续操作;如果server是接收到SIGTERM信号(linix操作系统的kill命令产生的信号),则由接收到信号的线程执行关闭操作。需要注意的是,如果此时服务器内存使用非常高,可能会关闭失败,并将失败信息记录到error日志中。
:::block-1
说明:
SIGTERM 这个是shell命令kill默认的信号,进程收到此信号后,可以继续做一些处理然后再退出,具体的命令为kill pid 或者kill -15 pid,即这两个命令发出后,进程会安全的退出。
SIGKILL 这个是shell命令kill -9 pid发送的信号,进程接收到此信号后,会立即停止进程,无法按正常的退出流程执行。
因此,在linux操作系统中,如果使用kill命令停止MySQL服务,建议使用kill (-15) pid,而不是kill -9 pid,虽然kill -9能够快速停止,但是可能会对数据、文件造成破坏,导致数据库无法启动。
:::
-
关闭新的的连接请求,拒绝所有尝试通过TCP/IP、socket、pip、shared memory的建立的连接。
-
终止当前活跃进程。其操作方式是标记所有线程的状态为killed,和我们通过mysql client发出kill processlist_id一样,如果thread为sleep状态,线程很快就关闭。如果是正在运行语句的线程,并且是在事务中(比如innodb的dml),则会进行回滚;如果是非事务中的语句(比如myisam表的dml)则会终止,导致批量插入可能部分成功,SQL执行结果与预期不符。有一个特殊情况,如果实例是slave节点,会等待sql_thread线程执行完当前的组事务、SQL命令,以减少复制问题。
-
关闭server层、关闭存储引擎层。在这一步,会刷新表结构到磁盘,关闭所有打开的表,刷新LSN到表空间文件(ibd文件)。另外很重要的一点,对于innodb存储引擎,会将innodb_buff_pool中的部分内容刷新到磁盘(如果innodb_fast_down设置为2,则不会刷新innodb_buff_pool到磁盘)。
-
MySQL实例关闭成功,返回操作结果的状态码(0/1/2)。
如果是由shell发起了kill -9,第3、4、5步都不会有,直接跳过
启动过程
本章节本来是打算详细描述下MySQL的启动过程,但是能力有限,暂时只观察到以下大概的启动步骤
整个启动过程,其他步骤比较容易理解,唯独恢复innodb_buffer_pool
这一步骤是以前不曾观察到的,今天就来重点聊一下它。
功能说明
为了避免重新启动MySQL服务后长时间的预热,特别是对于设置了比较大的innodb_buffer_pool_size的实例,可以在服务器关闭时保存buffer_pool内容,并在服务器启动时将buffer_pool恢复到关闭前的状态,避免数据库刚开始运行的一段时间内业务访问所有请求数据页都需要重新从磁盘上读取,减小数据库重启对系统带来的性能影响。
此功能在5.6版本引入,默认关闭;在5.7及之后的版本,就默认打开此功能。
参数说明
innodb_buffer_pool_dump_at_shutdown -- 控制在实例关闭时保存innodb_buffer_pool内容
innodb_buffer_pool_dump_pct -- 控制保存innodb_buffer_pool的完整度,默认25%,此参数是在5.7中增加。
innodb_buffer_pool_load_at_startup -- 控制在实例启动时加载上次关闭时保存的innodb_buffer_pool内容
使用介绍
一般来说,实例运行过程中会加载大量数据进入buffer_pool中,但是在保存innodb_buffer_pool内容时,并不会完整的保存所有数据,而是仅仅保存innodb_buffer_pool_dump_pct百分比的数据,按最近访问时间顺序保存,
数据可以在information_schema.INNODB_BUFFER_PAGE_LRU中查询到,保存的内容也仅仅是保存了SPACE_ID+PAGE_NUMBER,然后通过这两个属性组成的唯一值,到物理磁盘上获取完整数据,
保存的文件是在数据目录的ib_buffer_pool文件中,文件名是由innodb_buffer_pool_filename控制,可以看到相比innodb_buffer_pool_size的设置值,该文件非常小
[#3#root@greatdb81 /greatdb/dbdata/datanode4406/data 15:38:54]3 ll ib_buffer_pool
-rw-r-----. 1 greatdb greatdb 10555 5月 31 11:17 ib_buffer_pool
[#4#root@greatdb81 /greatdb/dbdata/datanode4406/data 15:39:00]4
由于在启动过程中,需要加载ib_buffer_pool文件的内容,还需要到对应数据文件中去读取完整用户记录,因此启动过程中会有比较大的IO消耗,但这个恢复是由单独的线程异步处理,并不会阻塞MySQL服务的正常启动。
下面对比了开、关参数对系统IO的影响时长,可以看到开启innodb_buffer_pool_load_at_startup=on,系统IO比较长的一段时间内处于ioutil为100%的情况
功能补充
MySQL还提供了一些功能,用于满足不同场景下对于innodb_buffer_pool导出、加载的使用
- 在MySQL运行过程中在线导出
SET GLOBAL innodb_buffer_pool_dump_now=ON;
- 在MySQL运行过程中手动加载
SET GLOBAL innodb_buffer_pool_load_now=ON;
- 查看导出进度
SHOW STATUS LIKE 'Innodb_buffer_pool_dump_status';
- 查看加载进度
SHOW STATUS LIKE 'Innodb_buffer_pool_load_status';
- 终止当前ib_buffer_pool加载
SET GLOBAL innodb_buffer_pool_load_abort=ON;
- 在数据库启动的error日志文件中,也有如下信息记录ib_buffer_pool加载动作
结束语
在MySQL启动过程中,因为有innodb_buffer_pool的load,可以让实例在端时间内恢复到重启前的状态,对于数据库系统的稳定有比较大的作用,但是由于加载需要消耗大量IO,可能会引起IO相关的问题,这是需要注意的。
停止MySQL,建议按正常命令执行shutdown,非常不推荐使用kill -9(我因为这个操作弄坏的数据库实例也不是一个两个了)。
整理过程中难免有错漏,还请各位同学多多包涵、多多指点。
最后附上参考资料
https://dev.mysql.com/doc/refman/8.0/en/server-shutdown.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-preload-buffer-pool.html
Enjoy GreatSQL :)