系统与MySQL 之间的连接

image.png

系统与MySQL之间需要建立网络连接(即从数据库连接池拿到数据库连接),才能进行CRUD的操作 — 也就 MySQL - connect - Java 驱动的作用

在MySQL内部如何执行SQL

  1. 网络连接必须得分配一个线程去进行处理,由一个线程来监听网络请求以及读取请求数据
  2. MySQL 内部提供了一个组件,就是SQL接口,是一套执行SQL语句的接口,MySQL工作线程收到SQL语句之后,就会调用SQL接口执行SQL语句。
  3. SQL接口如何执行SQL语句?
    • 看懂SQL : 查询解析器;负责解析SQL语句,即按照既定的SQL语法,理解SQL语句要干什么事情。

image.png

  • 选择执行路径:查询优化器;看懂SQL之后,生成复杂的查询路径树,选择最优的执行路径,路径很多,得根据最小代价选一条。 ```sql select in,name,age from user where id = 1

//路径1 直接定位到user表中的id = 1的一行数据,然后查出来哪行数据的id,name,age 三个字段值就可以了 //路径2 users表中的每一行数据的id,name,age三个字段值都查询出来,然后从这一批数据中过滤出id = 1 的数据

两种路径都可以达到目的,但是路径1会更好些。

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21550800/1648089406602-289dfa40-a9fb-4b32-aab6-089ccaaec8fa.png#clientId=uc7c3ea26-5a00-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=291&id=ue64b19d9&margin=%5Bobject%20Object%5D&name=image.png&originHeight=581&originWidth=1078&originalType=binary&ratio=1&rotation=0&showTitle=false&size=195663&status=done&style=none&taskId=uba6088c8-a484-470c-99e1-0d576ccd92b&title=&width=539)
  2. - 执行SQL:**存储引擎接口**;把查询优化器选择的最优执行路径(按照什么样的顺序和步骤去执行SQL语句)交给底层的**存储引擎**去真正的执行
  3. 存储引擎会按照一定的步骤去查询内存缓存的数据,更新磁盘数据。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21550800/1648090251962-1944251c-8b77-4bb4-a3a5-8fc0e93c7649.png#clientId=uc7c3ea26-5a00-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=574&id=u583c4864&margin=%5Bobject%20Object%5D&name=image.png&originHeight=574&originWidth=804&originalType=binary&ratio=1&rotation=0&showTitle=false&size=143855&status=done&style=none&taskId=u88e6f575-fa5e-4ad4-9a51-d80af617280&title=&width=804)<br />存储引擎是支持各种各样的,可以选择使用哪种存储引擎来负责具体的SQL语句执行的
  4. - **执行器**:会根据我们的优化器生成的一套执行计划,然后**不停的调用存储引擎的各种接口**去完成SQL语句的执行计划。
  5. ```sql
  6. 执行器 可能会先调用存储引擎的一个接口,去获取users 表中的第一行数据,然后判断这个数据的id字段值
  7. 是否等于我们期待的一个值,不是的话就继续调用存储引擎的接口,去获取users表的下一行数据。
  8. 并不一定只调用一次存储引擎接口就能拿到想要的数据。

image.png

如何基于存储引擎完成一条更新语句

image.png
引擎执行更新语句的时候,对id = 10 这一行数据

  1. 先判断是否在缓冲池里面?
  2. 不在的话,会直接从磁盘里面加载到缓冲池里面来
  3. 并且对这行记录加独占锁,因为我们在更新id= 10 这行数据的时候,不允许别人同事更新,必须对这行记录加独占锁。

image.png

undo 日志文件: 如何让你更新的数据可以回滚

将id= 10 的name = zhangsan 更新为 name = lisi;
那么此时需要先将更新前的值,即 id= 10 ,name = zhangsan ,写到日志文件中去。
执行一个更新语句,要是在一个事务里面的话,那么事务提交之前是可以对数据进行回滚,也就是将 name = lisi 回滚到 name = zhangsan 去;
考虑到未来有数据回滚的需要,就会把更新前的值写入undo 日志文件中
image.png

更新buffer pool 中的缓存数据

把要更新的那行记录从磁盘文件中加载到缓冲池中,同时对数据加锁,并且把更新前的旧值写入undo 日志文件中后,就可以正式开始更新这行记录了。
更新的时候,先更新缓冲池,就是算产生脏数据了,因为内存中的id= 10 的 name = lisi 了,但是在磁盘中 id= 10 的name = zhangsan
image.png

Redo Log Buffer : 万一系统宕机,如何避免数据丢失

image.png
如何保证在写入undo Log 和 更新内存数据之后,出现机器宕机时保证数据不丢失(因为此时数据的更改只在内存中还未更新到磁盘)?
在更新到内存数据库之后,写入到 redo log Buffer 中,即内存的一个缓冲区,放redo 日志,即记录了你对数据做了什么修改:id= 10 这行记录修改为name字段值为 lisi;
用来在MySQL突然宕机时,用来恢复你更新过的数据

如果还没提交事务,MySQL 宕机怎么办

哪怕执行一条SQL语句,其实也可以是一个独立的事务,只有当你提交事务之后 ,SQL语句才算执行结束。
当更新的操作写入到redo log 后,事务还未提交,此时MySQL 宕机,那么内存中的buffer pool 的修改过的数据都会丢失,同时写入Redo Log Buffer 中redo 日志也会丢失。
但其实,现在数据丢失是不要紧的。因为一条更新语句,没提交事务,就代表没执行成功,此时MySQL 宕机会导致内存的数据丢失,但是磁盘的数据不变。但是该事务是执行失败的,没能完成更新,会受到数据库异常,MyQL重启后,数据没有发生任何变化。

image.png

提交事务的时候,将redo 写入磁盘

提交一个事务时,根据一定的策略把redo 日志从redo log buffer 中刷到磁盘文件中去
image.png
策略通过 innodb_flush_log_at_trx_commit 来配置
innodb_flush_log_at_trx_commit= 0 时
提交事务时,不会把redo log buffer 里的数据刷入磁盘文件中,此时可能都提交事务了,结果MySQL宕机,没存数据全部丢失。相当于事务提交成功,但MySQL突然宕机导致内存中的数据和redo日志都丢失。
image.png
image.png
innodb_flush_log_at_trx_commit = 1时 建议设置为1,保证数据不丢失
提交事务时,就必须把 redo log 从内存刷入到磁盘文件里去,只要事务提交成功,redo log 就必然在磁盘里了,肯定有一条 redo 日志说了,此时对哪个数据做了个什么修改,比如 name 字段 修改为 lisi;

此时若是内存中的数据还没有刷到磁盘中,但 MySQL 突然宕机了,数据也不会丢失,因为刷入磁盘的redo 日志说了对 某条数据进行了修改,MySQl 重启后,根据redo 日志去恢复之前做过的修改。
image.png

innodb_flush_log_at_trx_commit==2时
提交事务时,把redo 日志写入到磁盘文件对应的 os cache 缓存里去,而不是直接进入到磁盘文件,可能1秒后才会把os cache的数据写入到磁盘文件去。
提交事务后,redo log 日志只存在 os cache 内存缓存里,没刷到磁盘去,此时要是机器宕机,那么 os cache 的 redo log 就会丢失,让你感觉提交了事务,但是数据却丢失了。image.png

bin log 是什么

redo log 是一种偏向物理性质的重做日志,记录了“对哪个数据页中的什么记录,做了什么修改”,且 redo log 本身也是一个 InnoDB存储引擎特有的一个东西。
bin log 叫归档日志,记录了偏向逻辑性的日志,记录了“对 users表中的id= 10 的一行做了更新操作,更新后的值是什么”,不是InnoDB 特有的日志文件,而是属于mysql server 的日志文件

image.png
1,2,3,4 是执行这个更新语句干的事情;
5,6是 提交事务开始,属于提交事务阶段;

bin log 刷盘策略

sync_binlog 参数 ,控制bin log 的刷盘策略

sync_binlog = 0
此时把bin log 写入磁盘时,不是直接进入磁盘文件,而是进入了 os cache 内存缓存,那么机器宕机,bin log 也会丢失的。
image.png

sync_binlog 参数 == 1
在强制提交事务时,会把bin log直接写入到磁盘文件中去,那么在提交事务后,即使机器宕机,bin log 是不会丢失的。

image.png

当把bin log 都写入磁盘后,就接着完成最终的事务提交,会把本次更新对应的binlog 文件名称,和这次更新的binlog 日志在文件里面的位置,都写入到redo log,同时在 redo log 日志文件中 写入一个commit 标记。 这时才算最终完成了事务的提交。
image.png

commit 的意义?

保持 bin log 和 redo log 日志文件一致的。
事务提交一共有5,6,7三个步骤,必须是三个步骤都执行完毕才算是提交了事务,但是当我们完成步骤5时,redo log 刚刷入磁盘,机器宕机。因为没有最终的commit标记在redo 中,故判定为事务失败,不会说redo log 日志中有这次的更新,但是bin log 日志中没有,从而出现数据不一致的问题。

要是完成步骤6,那么此时redo log ,bin log 都写入磁盘,但是此时宕机了,因为redo log 中没有commit 标记,事务也是失败。

必须redo log 中写入最终事务commit 标记,才算事务提交成功,而且redo log 有本次更新的记录,bin log 本次也有更新对应的日志,redo log 和 bin log 算完全一致。

后台IO线程,随机将内存更新后的脏数据刷回磁盘

当我们的rodo log 和 bin log 日志都刷到磁盘了,但是此时磁盘的数据还是就数据,name = zhangsan;那么此时MySQL 会有一个 后台IO线程 在之后的某个时间里,随机的将内存中 buffer pool 里面的数据 刷回到磁盘上的数据文件里去。

image.png

当IO线程把buffer pool 里面的脏数据刷回到磁盘后,那么此时内存和磁盘数据就保持一致了 name = lisi 。
即使在刷脏数据前,机器宕机,那么此时也没关系,重启之后,MySQL会根据redo 日志 将提交成功事务修改的值 刷到内存中去,之后待后台IO线程刷回到磁盘中去。