Mysql如何执行一条sql语句
mysql服务端主要分为两部分:server和存储引擎,server负责进行权限校验、sql语句解析、优化以及访问存储引擎;存储引擎主要负责数据的存取。当客户端向mysql server发送一条sql语句时主要经历以下步骤:
- 客户端段访问mysql sever端的连接器,通过输入用户名密码来建立连接。mysql连接建立过程很复杂,实际使用过程中最好避免频繁的建立、断开连接。
- mysql查询缓存中是否命中了传入的sql语句。如果命中则直接返回查询缓存中的结果。但是由于数据更新会导致缓存失效,所以对于频繁更新的场景,查询缓存最好关闭。
- 如果缓存中没有命中,sql语句将被传入分析器来进行词法、语法分析。如果sql语句非法会直接返回报错信息
- 之后优化器会对分析的结果的执行计划进行优化,例如使用哪个索引、执行顺序等
- 之后执行器使用优化结果调用存储引擎的接口来查找结果,并将结果返回。
更新语句的日志
当存储引擎使用innodb时,更新语句执行的时候涉及到binlog和redolog的更新。这种技术被称为WAL(write ahead log)预写日志。我们知道查找一条语句往往需要经历好多次随机的IO查找,如果每次更新数据都直接写回磁盘的话,涉及到的随机IO查找会让整个请求的响应速度变慢。而预写日志则可以顺序写,可以直接在一个文件中追加,顺序写的速度非常快。之后批量将日志文件中的内容写回磁盘中的数据区域。
binlog与redolog相似,都是更新sql执行时需要写入的日志。但是二者的不同点有以下三点:
- binlog记录的是逻辑日志,通俗点讲就是记录本次sql更新了什么内容。redolog记录的是物理日志,对应的是具体某个数据块中的数据变成了什么样。
- binlog是sever层的,所以可以被所有的存储引擎使用。而redolog是innodb存储引擎特有的,用来实现innodb的crash safe。
- binlog是追加型日志,当一个文件写满之后,可以使用新的日志文件记录。而redolog是环形日志,它的大小是固定的。当日志写满之后便会从头开始覆盖头部的区域。redolog有两个指针,write pos标识每次写入时的起始位置,checkpoint标识每次写入磁盘时的开始位置。所以write pos到checkpoint之间是可以写入的空白区域,而checkpoint到write pos之间的是不可用区域,待写入磁盘数据区域。当redolog的剩余空间不足时便会立即触发写入磁盘数据区域。
写日志的逻辑
- sever调用存储引擎查找需要修改的数据
- sever层执行器对数据进行修改(在内存中)
- 调用innodb写入redolog,但是不提交,此时状态是prepare
- server层写入binlog
- 调用innodo将redolog进行提交,此时状态是commit。
为什么要将redolog的写入分成prepare和commit两个阶段,是因为要保证二者写入的日志必须一致。在进行数据恢复时,mysql是通过重新执行binlog中的命令来恢复的,假如binlog的日志与redolog不一致,例如某个更新写入了binlog但是redolog未写入,就会导致本应回滚的数据在恢复的数据中出现,造成数据不一致。
双1模式
从上文可以看见,使用innodb存储引擎更新数据时,需要同时写入binlog和redolog两个日志文件。实际上在操作系统中,写入文件也并不是立刻就写入磁盘,而是先写入磁盘缓冲区,之后进行刷盘操作将数据写入磁盘。只有真正写入磁盘上之后,数据才真正被持久化了,未写入磁盘之前,数据都是有可能丢失的。
Mysql所谓的双1模式,也就是针对两个重点日志的刷盘时机进行控制。与之相关的是mysql的两个配置参数:innodb_flush_log_at_trx_commit
和sync_binlog
。当两个参数值都设置为1时,可以在事务提交前立刻进行刷盘,只有都刷盘成功才会提交事务,从而保障数据不会丢失。具体参数取值和含义可以参考文档。
但是也可以看到,刷盘本身是一个较为耗时的操作,每次事务提交前刷盘虽然能够提升可靠性,但是性能也会受到限制,实际生产过程中应根据使用情况进行取舍。