MySQL一条更新语句是怎么执行的呢?

来分析下一个简单的update语句,update T set i = i + 1 where id = 100

一、锁

      Innodb引擎引擎支持表锁和行级锁的,id是主键,这时候只会锁住id=100这行数据,其他线程更新的话就要等到当前线程将锁释放掉。锁不是在事务开始的时候就获取,而是真正执行这条更新语句的时候才会获取锁,执行完也不会立即释放锁资源,而是事务提交的时候才会释放。如果如果两个事务同时开启的,那可重复读的隔离级别下,第二个执行更新的事务读到的是100还是101呢,是101,如果是100的话不就导致更新丢失了,更新读数据都是当前读,可以参考我的另一篇文章,事务的隔离级别是怎么实现的。你可能会有疑问,为什么不更新完就立即释放锁资源呢,这样就不会导致死锁了,其中的一个原因就是,事务可能执行失败要回滚的,你想一个事务100->101失败了,另一个101->102成功了,这样回滚起来基本是不太现实的。

二、执行过程

1. 首先执行器会根据主键索引找到id为100的数据,当然如果你的MySQL使用了缓存它会先从缓存中找,没有命中就会用主键找,如果内存页中没有这条数据,它会先把整个数据页都读到内存中,如果在内存页的话,直接在changebuffer中记录一条更新。

2.接着,执行器会调用相应的接口更改这条数据,将101的结果更新到内存中,然后将这个动作记录到redo日志里面,标记为prepar状态,表示随时可以提交。

3.然后执行器生成相应的binlog日志,写入binlog

4. redo日志为commit状态,提交

这就是Mysql的二阶段提交协议

写入redo的日志的速度是很快的,这个过程你不需要操作磁盘,可以将随机写磁盘转化为顺序写redo日志,减少了写的IO

如果3 4步骤之间数据库突然崩溃了,这时候数据库重启先看redo日志,有一条prepare的数据,它会根据相应的id尝试着去看binlog里面有没有相应的 数据,如果有的话redo日志会提交这条数据,没有的话redo日志会进行回滚,这和从binlog备份数据库得到的结果是一样的。

二、具体执行过程

1.首先客户端通过tcp/ip发送一条sql语句到server层的SQL interface
2.SQL interface接到该请求后,先对该条语句进行解析,验证权限是否匹配
3.验证通过以后,分析器会对该语句分析,是否语法有错误等
4.接下来是优化器器生成相应的执行计划,选择最优的执行计划
5.之后会是执行器根据执行计划执行这条语句。在这一步会去open table,如果该table上有MDL,则等待。
如果没有,则加在该表上加短暂的MDL(S)
(如果opend_table太大,表明open_table_cache太小。需要不停的去打开frm文件)
6.进入到引擎层,首先会去innodb_buffer_pool里的data dictionary(元数据信息)得到表信息
7.通过元数据信息,去lock info里查出是否会有相关的锁信息,并把这条update语句需要的
锁信息写入到lock info里(锁这里还有待补充)
8.然后涉及到的老数据通过快照的方式存储到innodb_buffer_pool里的undo page里,并且记录undo log修改的redo
(如果data page里有就直接载入到undo page里,如果没有,则需要去磁盘里取出相应page的数据,载入到undo page里)
9.在innodb_buffer_pool的data page做update操作。并把操作的物理数据页修改记录到redo log buffer里
由于update这个事务会涉及到多个页面的修改,所以redo log buffer里会记录多条页面的修改信息。
因为group commit的原因,这次事务所产生的redo log buffer可能会跟随其它事务一同flush并且sync到磁盘上
10.同时修改的信息,会按照event的格式,记录到binlog_cache中。(这里注意binlog_cache_size是transaction级别的,不是session级别的参数,
一旦commit之后,dump线程会从binlog_cache里把event主动发送给slave的I/O线程)
11.之后把这条sql,需要在二级索引上做的修改,写入到change buffer page,等到下次有其他sql需要读取该二级索引时,再去与二级索引做merge
(随机I/O变为顺序I/O,但是由于现在的磁盘都是SSD,所以对于寻址来说,随机I/O和顺序I/O差距不大)
12.此时update语句已经完成,需要commit或者rollback。这里讨论commit的情况,并且双1
13.commit操作,由于存储引擎层与server层之间采用的是内部XA(保证两个事务的一致性,这里主要保证redo log和binlog的原子性),
所以提交分为prepare阶段与commit阶段
14.prepare阶段,将事务的xid写入,将binlog_cache里的进行flush以及sync操作(大事务的话这步非常耗时)
15.commit阶段,由于之前该事务产生的redo log已经sync到磁盘了。所以这步只是在redo log里标记commit
16.当binlog和redo log都已经落盘以后,如果触发了刷新脏页的操作,先把该脏页复制到doublewrite buffer里,把doublewrite buffer里的刷新到共享表空间,然后才是通过page cleaner线程把脏页写入到磁盘中

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值