总的来说,select和update执行的逻辑大体一样,但是具体的实现还是有区别的。
当执行一条查询的SQl的时候大概发生了一下的步骤:
- 客户端发送查询语句给服务器。
- 服务器首先进行用户名和密码的验证以及权限的校验。
- 然后会检查缓存中是否存在该查询,若存在,返回缓存中存在的结果。若是不存在就进行下一步。
- 接着进行语法和词法的分析,对SQl的解析、语法检测和预处理,再由优化器生成对应的执行计划。
- Mysql的执行器根据优化器生成的执行计划执行,调用存储引擎的接口进行查询。
- 服务器将查询的结果返回客户端。
Mysql的执行的流程
这里以一个实例进行说明Mysql的的执行过程,新建一个User表,如下:
// 新建一个表DROP TABLE IF EXISTS User;CREATE TABLE `User` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `age` int DEFAULT 0, `address` varchar(255) DEFAULT NULL, `phone` varchar(255) DEFAULT NULL, `dept` int, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8;// 并初始化数据,如下INSERT INTO User(name,age,address,phone,dept)VALUES('张三',24,'北京','13265543552',2);INSERT INTO User(name,age,address,phone,dept)VALUES('张三三',20,'北京','13265543557',2);INSERT INTO User(name,age,address,phone,dept)VALUES('李四',23,'上海','13265543553',2);INSERT INTO User(name,age,address,phone,dept)VALUES('李四四',21,'上海','13265543556',2);INSERT INTO User(name,age,address,phone,dept)VALUES('王五',27,'广州','13265543558',3);INSERT INTO User(name,age,address,phone,dept)VALUES('王五五',26,'广州','13265543559',3);INSERT INTO User(name,age,address,phone,dept)VALUES('赵六',25,'深圳','13265543550',3);INSERT INTO User(name,age,address,phone,dept)VALUES('赵六六',28,'广州','13265543561',3);INSERT INTO User(name,age,address,phone,dept)VALUES('七七',29,'广州','13265543562',4);INSERT INTO User(name,age,address,phone,dept)VALUES('八八',23,'广州','13265543563',4);INSERT INTO User(name,age,address,phone,dept)VALUES('九九',24,'广州','13265543564',4);开始执行这条sql时,首先会校验你的用户名和密码是否正确,若是不正确会返回错误信息:"Access denied for user";
注意:后续的一些列操作都是依赖于这个权限的范围内的。
检索缓存
假如,缓存中key遭击中,便会直接将结果返回给客户端,假如没命中,便会履行后续的操作,完工之后亦会将结果缓存起来以便再次查询获取,当下一次进行查询的时候也是如此的循环操作。
在个人的观点中对于缓存这一块的看法是,没必要砍掉,可以设置成默认关闭缓存,需要的时候再设置开启,并且可以通过配置参数指定那别表使用缓存,那些表不使用缓存,这样或许使用缓存更有效。
分析器
词法分析主要执行提炼关键性字,比如select,提交检索的表,提交字段名,提交检索条件,确定该语句是select还是update或者是delete语句。
查询优化器会将解析树转化成执行计划。一条查询可以有多种执行方法,最后都是返回相同结果。优化器的作用就是找到这其中最好的执行计划。
生成执行计划的过程会消耗较多的时间,特别是存在许多可选的执行计划时。如果在一条SQL语句执行的过程中将该语句对应的最终执行计划进行缓存。
MySQL使用基于成本的查询优化器。它会尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最少的一个。
执行器
上面我们说完了select语句,select语句的执行过程会经过连接器、分析器、优化器、执行器、存储引擎,同样的update语句也会同样走一遍select语句的执行过程。
那么Mysql中又是怎么使用redo log和binlog?为什么要使用redo log和binlog呢?直接执行更新然后存库不就行了吗?还要放在redo log和binlog中,这不是多此一举吗?且听我慢慢道来,这里面大有文章。
redo log
这也是为什么引入非关系型数据库作为作为数据缓存原因,例如:Redis、MongoDB等,就是为了减少sql执行期间的数据库io操作。
基于上面的问题于是出现了redo log日志,redo log日志也叫做WAL技术(Write- Ahead Logging),他是一种先写日志,并更新内存,最后再更新磁盘的技术,并且更新磁盘往往是在Mysql比较闲的时候,这样就大大减轻了Mysql的压力。
如上图所示:若是四组的redo log文件,一组为1G的大小,那么四组就是4G的大小,其中write pos是记录当前的位置,有数据写入当前位置,那么write pos就会边写入边往后移。
redo log日志实现了即使在数据库出现异常宕机的时候,重启后之前的记录也不会丢失,这就是crash-safe能力。
binlog
那么这样看来redo log和binlog虽然记录的形式、内容不同,但是这两者日志都能通过自己记录的内容恢复数据,那么为什么还要这两个日志同时存在呢?只要其中一个不就行了嘛,两个同时存在不就多此一举了嘛。且听我慢慢道来,这里面也大有文章。
上面说了那么久两种日志的作用和特点,那么这两种日志究竟和update执行语句有什么关系呢?
前提:当前的引擎是使用InnoDB,update语句与select语句区别主要是这两日志的使用主要是在执行器和引擎之间进行交互时体现的区别。假如执行如下一条简单的更新语句是:
update user set age=age+1 where id =2;
与select语句相比,因为select没有更新数据,只是将引擎查询的数据返回给执行器就算是完后,而update涉及数据的更新并且重新调用引擎接口写会存储引擎中的交互过程。
两阶段提交
若是redo log写成功binlog写失败,或者redo log写失败binlog写成功,最后使用这两者日志进行数据恢复得到的结果数据都是不一致性的,所以为了保证两个日志逻辑上的一致,使用两阶段进行提交。
redo log与binlog的总结
而binlog以追加日志的形式写入,也就是当日志写到一定大小后,就会切换到下一个,并不会覆盖以前写的日志。
在使用redo和binlog这两种日志的时候,可以将参数innodb_flush_log_at_trx_commit和sync_binlog都设置为1,它表示每次事务提交的时候,都会将日志持久化到磁盘中。
好了,这里详细的介绍了select和update执行语句的区别,这一期就到这里
