一、问题
InnoDB存储引擎,执行了下列语句:
UPDATE user SET name = "小明" WHERE id=1002;
其中id是主键,这条SQL语句的执行过程是怎样的?
二、答案
首先客户端与MySQL连接器进行连接,然后分析器经过词法分析和语法分析,然后到达优化器,优化器生成执行计划,选择索引,这里选择主键索引id,然后就到达了执行器,执行器调用存储引擎,存储引擎返回结果。
具体而言,在执行器调用存储引擎时都发生了下面这些事儿:
1. 从磁盘加载到内存
首先存储引擎在缓冲池中查找该数据页,如果缓冲池中没有id=1002的记录,会在B+树上查找数据页。一般来说,一个表的B+树索引根节点在数据库启动时就放在缓冲池。对根节点根据id=10进行二分查找,然后把子节点磁盘块加载到缓冲池继续二分查找,然后把叶子节点磁盘块加载到缓冲池继续二分查找。对页进行二分查找是在页目录中查找合适的槽。一旦确定了槽,就去查找槽里面的一行一行记录,根据每个记录行格式上的next_record字段遍历单链表,直到找到目标记录。
2. 写入缓冲池
然后会写到缓冲池中,由于这个页面之前不在缓冲池,会在free链表上取一个空闲缓冲页的控制块填写信息,然后这个控制块从free链表移除,然后将控制块放到lru链表的冷数据区头部。最后将记录返回给server层的执行器。
3. 写入Undo日志
执行器更新数据前,Undo日志会写下更新前的数据,然后将写好的Undo日志写到update undo log链上,最终这个Undo页面链表会放到系统表空间的某个回滚段下的Undo Log Segment中。由于这是一个没有修改主键的UPDATE语句,只会产生一个Undo日志。把这个Undo日志使用头插法挂在这行记录的roll_pointer下,形成一条Undo版本链。
4. 执行器修改
执行器设置name="小明",再调用执行引擎的接口写入新的数据。
5. 更新缓冲池
执行引擎将这行新的数据更新到缓冲池中。在flush链表中记录这个缓冲页控制块,同时lru链表会把这个控制块从冷数据区移动到热数据区的头部。
6. 写入Redo日志和Bin日志
随后将更新的记录写到Redo日志中,Redo日志会写到Redo Log Buffer,此时Redo日志处于prepare状态,事务提交之前会将Redo Log Buffer的Redo日志刷盘到Redo Log File,随后告诉执行器事务“可以提交”,存储引擎会根据操作写Bin Log到磁盘。写入Bin Log之后,事务成功提交,存储引擎再把Redo日志的prepare状态改成commit状态。
7. 脏页刷盘
数据库的后台线程会定时从flush链表中刷新一部分脏页到磁盘文件中。