数据库--第2篇(深入MySQL篇一)

数据库

  • 一,深入MySQL
        • 1,数据库执行流程
        • 2,mysql日志系统
        • 3,事务隔离

一,深入MySQL

1,数据库执行流程

mysql基本逻辑架构图

数据库--第2篇(深入MySQL篇一)_第1张图片
连接器
连接器负责跟客户端建立连接、获取权限、维持和管理连接。
查询缓存
MySQL拿到一个查询请求后,先找缓存,执行过的语句可能会以key-value对的形式缓存在内存中。key是查询的语句,value是查询的结果。下次查询缓存中找到key,那么这个value就会直接返回。查不到缓存中,就会继续后面的执行阶段,结果会被存入缓存。但是查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。对于更新压力大的数据库来说,查询缓存的命中率会非常低。在mysql8.0版本之后删除了查询缓存。
分析器
分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条SQL语句,MySQL会识别字符串代表什么,做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断SQL语句是否满足MySQL语法。
优化器
优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联的时候,决定各个表的连接顺序;
执行器
MySQL通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。开始执行的时候,要先判断一下你对这个表T有没有执行查询的权限,没有权限就抛出异常。执行器会调用InnoDB引擎接口取这个表的第一行,如果不是则跳过,是则将这行存在结果集中;调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

2,mysql日志系统

redo log(重做日志,InnoDB特有)

  1. crash-safe(崩溃安全能力):redo log保证即使数据库发生异常重启,之前提交的记录都不会丢失;
  2. WAL技术:WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘;
  3. 当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log 里面,并更新内存,这个时候更新就算完成了。InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做;
  4. InnoDB的redo log是固定大小的,每次写redo log满的时候会将前面的更新到磁盘,擦除redo log在重写开始记录;
  5. 日志擦除:write pos是当前记录的位置,边写边后移,写到第3号文件末尾后就回到0号文件开头。checkpoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write pos和checkpoint之间的还空着的部分,可以用来记录新的操作。如果write pos追上checkpoint,redo log满了,不能再执行新的更新,需先擦掉一些记录,把checkpoint推进一下。
    数据库--第2篇(深入MySQL篇一)_第2张图片

binlog(归档日志,server层日志)
binlog有两种模式,statement 模式是记录sql语句, row模式记录行的内容,更新前和更新后都有;

不同点 binlog redo log
试用引擎 mysql的server层实现的日志记录功能,所有引擎都可以使用 InnoDB引擎所特有
日志属性 逻辑日志,记录了sql语句的原始逻辑 物理日志,记录在什么数据上做了什么修改
记录方式 日志追加方式,binlog会自动追加写日志,当写到一定大小时会自动切换新文件继续记录 循环方式,redo log 大小固定,空间用完需要写磁盘擦除才能继续记录

update语句执行流程

 update T set c=c+1 where ID=2;

浅色框表示是在InnoDB内部执行的,深色框表示是在执行器中执行的
数据库--第2篇(深入MySQL篇一)_第3张图片
上图中,将redo log的写入拆成了两个步骤:prepare和commit,这就是"两阶段提交",保证了事务的一致性;

前面的update语句来做例子。假设当前ID=2的行,字段c的值是0,再假设执行update语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了crash,会出现什么情况呢?

  1. 先写redo log后写binlog。假设在redo log写完,binlog还没有写完的时候,MySQL进程异常重启。由于我们前面说过的,redo log写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行c的值是1。但是由于binlog没写完就crash了,这时候binlog里面就没有记录这个语句。因此,之后备份日志的时候,存起来的binlog里面就没有这条语句。然后你会发现,如果需要用这个binlog来恢复临时库的话,由于这个语句的binlog丢失,这个临时库就会少了这一次更新,恢复出来的这一行c的值就是0,与原库的值不同。
  2. 先写binlog后写redo log。如果在binlog写完之后crash,由于redo log还没写,崩溃恢复以后这个事务无效,所以这一行c的值是0。但是binlog里面已经记录了“把c从0改成1”这个日志。所以,在之后用binlog来恢复的时候就多了一个事务出来,恢复出来的这一行c的值就是1,与原库的值不同。

两阶段提交prepare&commit(如上图)

  1. 当在写binlog崩溃时,重启后恢复:后发现redolog没有commit,通过binlog恢复也没有之日,保持了一致;
  2. 当在commit时崩溃,重启后恢复:虽没有commit,但满足prepare和binlog完整,所以重启后会自动commit,保持了一致

3,事务隔离

事务隔离的实现

  1. 在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。
  2. 假设一个值从1被按顺序改成了2、3、4,在回滚日志里面就会有类似下面的记录。
    数据库--第2篇(深入MySQL篇一)_第4张图片
  3. 当前值是4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的read-view。如图中看到的,在视图A、B、C里面,这一个记录的值分别是1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)
  4. 对于read-view A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。
  5. 现在有另外一个事务正在将4改成5,这个事务跟read-view A、B、C对应的事务是不会冲突的。

长事务

  1. 长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间;
  2. 建议set autocommit=1,自动结束事务,尽量避免长事务;
  3. 长事务还占用锁资源,也可能拖垮整个库;
  4. 在information_schema库的innodb_trx这个表中查询长事务;
-- 查找持续时间超过60s的事务
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

注: 文章内容来源于极客时间《MySQL45讲》

你可能感兴趣的:(数据库,执行流程,日志系统,事务隔离)