《叶问》第21期

关 于 叶 问

《叶问》是知数堂新设计的互动栏目,不定期给大家提供技术知识小贴士,形式不限,或提问、或讨论均可,并在当天发布答案,让大家轻轻松松利用碎片时间就可以学到最实用的知识点。

2020年01月02日,周四

【P6级经典面试题】update SQL在MySQL的内部生命历程是怎样的?

一、Server层阶段:

1、连接器:负责跟客户端建立连接、获取权限、维持和管理连接

2、分析器:验证通过以后,分析器会对该语句分析, 判断是否语法有错误等。

3、优化器:选择索引,生成执行计划。

4、执行器:根据优化器生成的执行计划,调用存储引擎API执行SQL。

二、InnoDB引擎层阶段:

1、事务执行阶段:

进入InnoDB后引擎层,首先会判断该SQL涉及到的页是否存在于缓存中,如果不存在则从磁盘读取该行记录所在的数据页并加载到BP缓冲池。

假设不存在,然后通过 B+Tree 读取到磁盘的索引页加载到BP缓冲池中 ,如何加载到BP缓冲池中:

首先 通过 space id 和 page no 哈希计算之后把 索引页加载到指定的 buffer pool instance 中

判断 free list 是否有空闲页可用(Innodb_buffer_pool_pages_free、 Innodb_buffer_pool_wait_free),没有则淘汰脏页或者lru list的Old页

把数据页 copy到 free list中,然后加载到 lru list的 old区的 midpoint(头部);

通过二分法查找该页对应的记录,试图给这个SQL涉及到的行记录加上排他锁,过程如下:

如果事务当前记录的行锁被其他事务占用的话,就需要进入锁等待;

进入锁等待之后,同时判断会不会由于自己的加入导致了死锁;

检测到没有锁等待和不会造成死锁之后,行记录加上排他锁。

写逻辑的undo:

将修改前的记录写入undo中

修改当前行的值,填写事务编号,使用回滚指针指向undo log中的修改前的行

从而构建回滚段,用于回滚数据和实现MVCC的多版本

写redo log buffer:

先判断redo log buffer是否够用,redo log buffer不够用就等待,体现在状态值 Innodb_log_waits 上;

在 BP缓冲池 的 Lru list中old区的midpont中对该数据页的行记录的字段值做更新操作,并把修改之后的字段值写入到redo log buffer中

并给LSN加上当前redo log写入的长度(写入长度为 length 的redo log,LSN就会加上 length)

(因为redo group commit的原因,这次事务所产生的redo log buffer可能会跟随其它事务一同flush并且sync到磁盘上)

字段值在BP缓冲池更新成功以后,对应的数据页就是脏页了

写binlog cache:

同时修改的信息,会按照event的格式,记录到binlog_cache中。

写change buffer:

之后把这条sql, 需要在二级索引上做的修改,写入到change buffer page,等到下次有其他sql需要读取该二级索引时,再去与二级索引做merge

(随机I/O变为顺序I/O,但是由于现在的磁盘都是SSD,所以对于寻址来说,随机I/O和顺序I/O差距不大)

事务commit or rollback:

此时update语句已经完成,需要commit或者rollback。这里讨论双1即sync_binlog=1 和 innodb_flush_log_at_trx_commit=1;

2、假设事务COMMIT

(1)事务的COMMIT 分为prepare阶段与commit阶段

事务的COMMIT操作,在存储引擎层与server层之间采用的是内部XA;

两阶段提交协议, 保证两个事务的一致性,这里主要保证redo log和binlog的原子性;

(2)redo log prepare:

写入 redo log处于prepare状态 并且写入事务的xid;

将 redo log buffer 刷新到 redo log磁盘文件中,用于崩溃恢复;  #刷盘的方式由 innodb_flush_log_at_trx_commit 决定

(3)binlog write&fsync: 执行器把 binlog cache 里的完整事务和 redo log prepare中的XID 写入到 binlog 中

dump线程会从binlog_cache里把event主动发送给slave的I/O线程,同时执行 fsync刷盘(大事务的话这步非常耗时),并清空 binlog cache。

#事务中写 binlog 的部分日志:

190511 11:06:54 server id 123306  end_log_pos 439 CRC32 0x1c809de0     Xid = 614

COMMIT/*!*/;

binlog刷盘的方式由 sync_binlog 决定;binlog写入完成,事务就算是成功。

事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog file 中。当sync_binlog为1时,当binlog落盘以后才会通知dump thread进行主从复制

(4)redo log commit: commit阶段,由于之前该事务产生的redo log已经sync到磁盘了。所以这步只是在redo log里标记commit,说明事务提交成功。

(5)事务提交成功,释放行记录持有的排他锁;

(6)当binlog和redo log都已经落盘以后,如果触发了刷新脏页的操作:

先把该脏页复制到doublewrite buffer里,其次把doublewrite buffer里的刷新到共享表空间(ibdata),然后才是把脏页写入到磁盘中;

这时候内存页与磁盘的数据页一致。

3、假设事务ROLLBACK

如果事务因为异常或者被显式的回滚了,那么所有数据变更都要改回去。这里就要借助回滚日志中的数据来进行恢复了。

对于in-place(原地)更新,将数据回滚到最老版本;

对于delete+insert方式进行的,标记删除的记录清理删除标记,同时把插入的聚集索引和二级索引记录也会被直接删除。

三、至此,一条update SQL在MySQL中结束生命历程

扫码加入MySQL技术Q群

(群号:650149401)

点“在看”给我一朵小黄花

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
import javafx.application.Application; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.stage.Stage; public class MovieTicketSystem extends Application { // 创建并初始化折扣对象 Discount[] discounts = { new StudentDiscount(), new ChildrenDiscount(), new VIPDiscount() }; @Override public void start(Stage primaryStage) throws Exception { // 设置窗口标题 primaryStage.setTitle("电影票销售系统"); // 创建Grid布局 GridPane gridPane = new GridPane(); gridPane.setAlignment(Pos.CENTER); gridPane.setHgap(10); gridPane.setVgap(10); // 创建UI控件 Label movieLabel = new Label("选择电影:"); ComboBox<String> movieComboBox = new ComboBox<>(); movieComboBox.getItems().addAll("功夫熊猫", "叶问", "疯狂动物城"); movieComboBox.setValue("功夫熊猫"); Label discountLabel = new Label("选择优惠方式:"); ComboBox<String> discountComboBox = new ComboBox<>(); discountComboBox.getItems().addAll("学生优惠", "儿童优惠", "VIP优惠"); discountComboBox.setValue("学生优惠"); Label priceLabel = new Label("原价:50元"); Label finalPriceLabel = new Label("最终价格:"); TextField finalPriceField = new TextField(); finalPriceField.setEditable(false); Button calculateButton = new Button("计算价格"); calculateButton.setOnAction(e -> { // 获取用户选择的电影和优惠方式 String movie = movieComboBox.getSelectionModel().getSelectedItem(); String discount = discountComboBox.getSelectionModel().getSelectedItem(); // 根据用户选择设置票价和折扣对象 MovieTicket mt = new MovieTicket(); if (movie.equals("功夫熊猫")) { mt.setPrice(60); } else if (movie.equals("叶问")) { mt.setPrice(70); } else if (movie.equals("疯狂动物城")) { mt.setPrice(80); } else { finalPriceField.setText("请选择正确的电影!"); return; } if (discount.equals("学生优惠")) { mt.setDiscount(discounts[0]); } else if (discount.equals("儿童优惠")) { mt.setDiscount(discounts[1]); } else if (discount.equals("VIP优惠")) { mt.setDiscount(discounts[2]); } double price = mt.getPrice(); finalPriceField.setText(price + "元"); }); // 添加UI控件到Grid布局中 gridPane.add(movieLabel, 0, 0); gridPane.add(movieComboBox, 1, 0); gridPane.add(discountLabel, 0, 1); gridPane.add(discountComboBox, 1, 1); gridPane.add(priceLabel, 0, 2); gridPane.add(finalPriceLabel, 0, 3); gridPane.add(finalPriceField, 1, 3); gridPane.add(calculateButton, 0, 4, 2, 1); // 创建场景并将Grid布局添加到场景中 Scene scene = new Scene(gridPane, 400, 250); primaryStage.setScene(scene); // 显示窗口 primaryStage.show(); } public static void main(String[] args) { launch(args); }把这段代码的电影票原价改随选择的电影变化而变化
06-13

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值