日志系统:更新语句是如何执行的

日志系统:更新语句是如何执行的

总的来说,更新语句同样要走查询语句的那套流程,不同之处在于,更新语句执行多了两个重要的日志模块:redo log(重做日志)、binlog(归档日志)

update T as t set t.c = t.c + 1 where t.id = 1

更新语句执行步骤:

  1. 先在表T中查找id = 1的一行数据
  2. 在内存中将c字段修改为c+1后
  3. 将c+1传递给执行器,由执行器将c+1写入磁盘,这个过程会再经历一次查询操作

redo log重做日志

请添加图片描述

重做日志的作用在于:节省了第三步操作的执行时间,近似认为,更新语句的效率由于redo log的存在提升了一倍!

需要注意的点是:

  1. redo log存在存在于磁盘上,断电不丢失数据,因此,redo log保证了就算数据库异常重启,仍然不会丢失数据。这个能力称为crash-safe。
  2. 由图可知,redo log是类似循环数组的数据结构。write pos表示当前日志数据尾部,check point则表示当前数据头部。当write pos与check point重合的时候,就是redo log不得不暂停一切更新,将redo log中的记录写到数据库磁盘上的时候了。
  3. 特别要注意的是,redo log属于Innode引擎独有的日志系统,而下面要讲的binlog属于Server层的日志系统,是所有引擎都可以使用的日志系统。

深入一点:

想想看,redo log由于可以长时间存在并替代磁盘上的数据,即无论是查询、修改、删除,都可以使用redo log。所以redo log必须是幂等的,最好是基于数据页Page的,也就是说:

一条redo log记录了某个Page最新的数据,当Innodb引擎查到某个Page时,会立即与redo log做比对,发现redo log上有这个Page,则将redo log上的数据Page返回给执行器。

下面是原评论:

redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。有了对这两个日志的概念性理解,我们再来看执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程。执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。执行器生成这个操作的 binlog,并把 binlog 写入磁盘。执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。这里我给出这个 update 语句的执行流程图,图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。update 语句执行流程你可能注意到了,最后三步看上去有点“绕”,将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是"两阶段提交"。两阶段提交为什么必须有“两阶段提交”呢?这是为了让两份日志之间的逻辑一致。要说明这个问题,我们得从文章开头的那个问题说起:怎样让数据库恢复到半个月内任意一秒的状态?前面我们说过了,binlog 会记录所有的逻辑操作,并且是采用“追加写”的形式。如果你的 DBA 承诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有 binlog,同时系统会定期做整库备份。这里的“定期”取决于系统的重要性,可以是一天一备,也可以是一周一备。当需要恢复到指定的某一秒时,

binlog归档日志

https://www.cnblogs.com/softidea/p/12624778.html

首先要考虑的是,为什么有了redo log日志还需要binlog

  1. redo log是循环写的,也就是说它大小有限,用完即止。
    binlog是追加写的,如果管理员不设置,那么binlog会占满你的磁盘。

  2. redo log是Innodb特有的日志。
    binlog是Server层实现,所有引擎共有。

  3. redo log具有crash-safe能力。
    binlog不具有。
    为什么说binlog不具有crash-safe能力呢?
    本质上还是https://www.wolai.com/kQXZwbwChtYpEd8ZvmVFjh#tsx19FoGQW2y7A8PPVxi21这里提到的原因
    https://blog.csdn.net/weixin_43698257/article/details/110676515
    举个栗子,binlog 记录了两条日志:
    给 ID=2 这一行的 c 字段加1
    给 ID=2 这一行的 c 字段加1
    在记录1刷盘后,记录2未刷盘时,数据库 crash。重启后,只通过 binlog 数据库无法判断这两条记录哪条已经写入磁盘,哪条没有写入磁盘,不管是两条都恢复至内存,还是都不恢复,对 ID=2 这行数据来说,都不对。
    但 redo log 不一样,只要刷入磁盘的数据,都会从 redo log 中抹掉,数据库重启后,直接把 redo log 中的数据都恢复至内存就可以了。这就是为什么 redo log 具有 crash-safe 的能力,而 binlog 不具备。

    根据 redo log 和 binlog 的两阶段提交,未持久化的数据分为几种情况:

    1. change buffer 写入,redo log 虽然做了 fsync 但未 commit,binlog 未 fsync 到磁盘,这部分数据丢失。
    2. change buffer 写入,redo log fsync 未 commit,binlog 已经 fsync 到磁盘,先从 binlog 恢复 redo log,再从 redo log 恢复 change buffer。
    3. change buffer 写入,redo log 和 binlog 都已经 fsync,直接从 redo log 里恢复。
      有关change buffer的内容,后续介绍TODO
  4. redo log 记录的是数据页Page上的真实数据。
    binlog记录的是数据逻辑,binlog的row、statement、mixed模式记录的都不是磁盘上真实的数据格式,而仅仅是人可读的数据变化逻辑。
    深入地说:
    如果redo log足够大,是可以直接通过redo log恢复数据的。
    但是仅仅只有binlog则不行,binlog+某一时间的数据全量备份 = 任意时间的数据

两阶段提交

请添加图片描述

为什么需要两阶段提交?

为了保证跨系统数据一致性,最常用的方案就是两阶段提交。应用场景举例:redis缓存与数据库的数据一致性。

在上述过程的任意阶段均可能发生异常重启,下面分析一下两阶段提交如何保证数据一致性:

  1. 在写redo log之前异常
    写入的数据无效,重启后恢复到该语句执行之前的数据状态
  2. redo log处于prepare 后 到 提交事务之前发生异常
    由于redo log状态不是commit,所以重启后redo log的这次修改被废弃
  3. 一般我们认为,写binlog和提交事务之间,不会发生异常,一旦发生,还是会产生数据不一致的问题

一直在说数据不一致问题,那么数据不一致的问题如何产生的呢?如果不采用两阶段提交,而仅仅单独写入redo log 与 binlog,那么通过redo log 与 binlog恢复的数据会不一致,这就叫数据不一致。

下面这篇文章详细介绍了两阶段提交与三阶段提交的规范描述:

https://segmentfault.com/a/1190000012534071

根据这篇文章,结合上述redo log 日志与binlog日志两阶段提交谈谈我的认识:

  • 第一步:投票
    投票即各部分** 各自执行自己的事务**,在协调者发布执行命令后自身进入阻塞状态,参与者开始执行事务但不提交,在提交事务的前一步阻塞,并发送信息告诉协调者,我已执行完毕且无异常,等你发布提交命令。
  • 第二步:事务提交
    协调者在收到所有参与者执行完毕的回复后,再次向全部参与者发送提交事务的命令,参与者在收到此命令后,提交事务。

上述过程存在三个缺陷,尤其是第三点导致两阶段提交也不能一定保证跨系统数据一致性:

  1. **阻塞过程太久。**协调者在第一次发布指令后必须等待全部参与者执行完(提交前一步)事务后才继续运行。参与者也必须等待其他参与者都执行完任务后协调者发布提交命令后才能继续运行。
  2. 单点问题。协调者在系统中处于至关重要的位置,一旦协调者异常,整个系统都将无法运行。
  3. 数据不一致问题。比方协调者在收到全部参与者执行完毕可以提交的回复后,发送的提交事务指令,部分参与者没有收到,或者收到了正准备执行提交操作的时候突然断电了。表现在上面redo log与binlog提交上,就是倒数第二步“写binlog”与倒数第一步“提交事务处于commit状态”之间发生异常的情况。

为了解决上面的问题,提出超时回滚互询机制

  • 超时回滚:协调者在指定时间内,若没有收到全部参与者的响应,则向所有参与者发送rollback信息
  • 互询:参与者A在发送“我已执行完任务,随时可以提交”的信息后,指定时间后,若没有收到协调者的响应,则立即向其他参与者B询问该执行commit还是rollback,询问B时,若B的状态在Ready之前,则完全可以认为事务已经rollback;若B状态为Commit,则自己执行Commit;若B状态为Ready,则向其他参与者询问。除非全部参与者都处于Ready状态,否则系统不会长时间阻塞。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 SpringBoot 中,可以通过配置日志级别来获取系统执行过的 SQL 语句。具体实现步骤如下: 1. 打开 application.properties 或 application.yml 配置文件,添加以下配置: ``` logging.level.org.hibernate.SQL=debug logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace ``` 2. 重启项目,执行 SQL 语句。 3. 在控制台或日志文件中查看输出信息,就可以看到系统执行的 SQL 语句了。 注意:以上配置只是一个示例,具体的 SQL 日志输出方式可能会因为使用的数据库框架或日志框架而有所不同。 ### 回答2: 在Spring Boot中可以通过配置来获取系统执行过的SQL语句。具体步骤如下: 1. 首先,在Spring Boot的配置文件(例如application.properties或application.yml)中配置以下属性: ``` spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true ``` 2. 设置spring.jpa.show-sql为true,表示开启显示SQL语句。这样在系统执行JPA操作时,会在控制台输出相应的SQL语句。 3. 设置spring.jpa.properties.hibernate.format_sql为true,表示格式化显示SQL语句。这样输出的SQL语句会更容易阅读和理解。 4. 重新启动应用程序,当应用程序执行JPA操作时,控制台会打印出相应的SQL语句,包括执行的查询语句、插入语句更新语句等等。 需要注意的是,以上配置只适用于使用JPA作为持久化框架的情况。如果应用程序使用其他持久化框架(例如Hibernate)或者使用原生的JDBC进行数据库操作,需要根据具体框架的配置方式进行相应的调整。 ### 回答3: 在Spring Boot中获取系统执行过的SQL语句,可以通过配置数据库连接池的属性来实现。具体步骤如下: 1. 添加依赖:在pom.xml文件中添加如下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>net.ttddyy</groupId> <artifactId>datasource-proxy</artifactId> <version>1.7</version> </dependency> ``` 这里使用了datasource-proxy库来拦截SQL语句。 2. 配置数据源:在application.properties或application.yml中配置数据源,例如: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=123456 ``` 3. 配置代理数据源:创建一个配置类,例如: ```java @Configuration public class DataSourceConfig { @Bean @Primary public DataSource dataSource() { ProxyDataSource proxyDataSource = new ProxyDataSource(); proxyDataSource.setDataSource(createActualDataSource()); proxyDataSource.setListener(new MyQueryExecutionListener()); return proxyDataSource; } private DataSource createActualDataSource() { // 创建真实的DataSource并返回 // 例如:return new HikariDataSource(); } private class MyQueryExecutionListener extends AbstractQueryExecutionListener { @Override public void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) { // 在执行SQL语句之前的操作 } @Override public void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) { // 在执行SQL语句之后的操作 for(QueryInfo queryInfo : queryInfoList) { String sql = queryInfo.getQuery(); // 对执行过的SQL语句进行处理 } } } } ``` 通过以上配置,我们创建了一个代理数据源,其中设置了一个自定义的QueryExecutionListener,当程序执行SQL语句时,该监听器会拦截并将执行过的SQL语句传递给afterQuery方法进行处理。 最后,就可以在程序中通过注入DataSource,并在需要的地方使用它来执行SQL语句,同时也能获取到系统执行过的SQL语句

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值