一条更新sql的执行之路

目录

Redolog与binlog   

一个 update sql 的执行流程?

思考解惑


Redolog与binlog   

数据库的查询操作具有天然幂等性,不会对数据库有任何的修改。但是mysql如何实现对数据库的更新操作呢?这里主要有两种重要的日志:Redo log和binlog

Redo log

1、Redo log是引擎层 InnoDB特有的日志;

2、循环写,固定空间会用完;

3、属于物理日志,记录的是做了什么变更;

ps:物理日志只有具体引擎自己能用,别人没有共享我的物理格式;

       逻辑日志可以给别的数据库用,公用的逻辑;

binlog

1、Binlog是MySql Server 层记录的日志,所有的存储引擎都可以使用;

2、可以追加写,不会覆盖以前的日志,用于归档;

3、属于逻辑日志,记录的是逻辑操作,是怎么修改的(sql或者是前后的行记录);

binlog的两种模式:

   statement记录的是sql语句;

   row格式记录的是行的内容,记两条,改变前和改变后的记录;一般采用row,但是数据量会变大;

我们通常选择binlog日志来作为replicatio[果采用redo log来实现会更快,但是只有innodb有,所以binlog必须存在]。

       数据库的性能绝大部分情况下都是由于IO影响了其吞吐量和性能。如果每一次的更新操作都需要写入磁盘,那么意味着有一次IO磁盘去查询,然后更新,这个成本比较大。为了提高数据库的吞吐量降低访问延时,还有重要的crash-safe能力,这里引入了重要的日志模块:Redo-log。也是经常说的WAL,Wirte-Ahead-Logging, 它的关键点是先写日志再写磁盘
    InnoDB有一个buffer pool简称(bp)。bp是数据库页面的缓存,对InnoDB的任何修改操作都会首先在bp的page上进行,对数据库的修改首先将记录在flush-list上,后续由专门的线程将这些页面写入磁盘(disk or ssd)。这样的好处是避免每次写操作都操作磁盘导致大量的随机IO阶段性的写入还可以将多次对页面的修改merge成一次IO操作,同时异步的写入也降低了访问的延迟。但是在dirty page还未刷入磁盘,server非正常关闭,那么这些操作和数据将丢失,甚至损坏数据库。为了避免上述问题的产生,设计师们将这些修改先写入一个专门的文件redo log,并在数据库启动时从此文件redo log 进行恢复操作,原地满血复活,这个文件就是Redo log,从而提升了数据库的吞吐,有效降低了访问延时及保证了crash-safe能力

Redo log优点    (1) 提高系统的吞吐量:组提交;
   (2) 降低访问延时:顺序写日志后磁盘;
   (3)崩溃恢复: crash-safe能力;
Redo log缺点   (1) 额外的写redo log操作的开销;
     
  (2) 数据库启动时恢复操作所需要的时间;

一个 update sql 的执行流程?

了解了上面的一些概念和优缺点后,我们先来看一下更新sql语句的执行过程

Update  USER_TABLE set age = age +1 where id = 6;
1、客户端通过tcp/ip和数据库的连接器建立连接,连接器获取用户账号信息并验证权限是否匹配;
   ⚠️此步可能出现的常见错误:“Access deied for user”
2、如果开启了缓存查询,先查看缓存是否存在数据,对表的权限进行校验,通过则直接返回给客户端;如果没有开启缓存,则走向第三步;
3、通过分析器的词法分析,得到是一个update操作,表名是USER_TABLE,字段age where;
   ⚠️此步可能出现的常见错误:“Unknown column ‘XXX’ in ‘where clause”
4、通过分析器的语义分析,看看是否有语法问题
   ⚠️此步可能出现的错误:“You hava an error in your SQL syntax. ”
5、通过优化器选择索引,id为主键,使用主键索引查询;
6、将生成的最优执行方案交给执行器,执行器调用底层的存储引擎的读接口通过搜索书取到id=6这行的数据,如果id=6的这行数据本来就在内存中,那么将会直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回;
7、执行器拿到引擎返回的age数据,进行运算+1,得到新的一行数据,然后执行器调用引擎的写接口写入这行新数据
8、引擎将这行数据更新到内存中,同时将这个更新操作录到Redo log 里面,此时redo log处于prepare状态,然后告诉执行器完成了,随时可以提交事务;
9、server层的执行器生成这个操作的binlog,并把binlog写入磁盘;
10、执行器调用引擎的条事务接口,引擎把刚刚写入的Redo log改为提交commit状态,更新完成。

  其中8-9-10三步中将Redo log拆成两步就是两阶段提交:主要为了保证binlog和Redo logd的数据的逻辑一致性

思考解惑

问题一:响应一次update sql需要写几次磁盘?
答:三次。redo log 2次(prepare + commit),binlog一次。

问题二:为什么需要两份日志呢?
答:Mysql里并没有InnoDB引擎,MySql自带的引擎是MyISAM,但是MyISAM 没有crash-safe能力,binlog只能用鱼归档,所以InnoDB使用了另外一套日志系统,也就是Redo log来实现creash-safe的能力。
一句话区别:crash-safe是崩溃恢复,就是原地满血复活;binlog时制造一个副本;

问题三:如何让数据库恢复到一个月内的任意一秒的状态呢?
答:首先我们的备份系统需要保存近一个月的所有的binlog;另外,要求系统会定期做整库备份,根据系统的重要性,可以一天或者是一周备份。定期的整库备份时间越短,“最快恢复的时间”就越短,主要根据具体的业务容忍度来做。
恢复步骤:
1、找到需要恢复时间点之前的最近一次的整库备份,将其恢复到临时数据库;
2、从整库备份时间点开始,将备份的binlog依次回放,重放到需要的时间点那个时刻;
3、至于误删之后的,不能只靠binlog,需要和业务方一起来完成数据的恢复,因为由于误删,可以插入了一些错误的操作;


问题四:为什么需要两阶段提交?
答:
1、redo log 处于prepare状态;
2、server写binglog;
3、redolog commit;
第2步 崩溃:不满足binlog和redo log一致性,重启恢复:没有commit,回滚;备份恢复:没有binlog ;结果:一致;
第3步 崩溃:    满足binlog和redo log一致性,重启恢复:自动commit,提交;备份恢复:有binlog;     结果:一致
事务是否提交的条件是:看结果是否符合我们要达到的“用binlog恢复的库和原库逻辑相同”这个要求;
如果不使用两阶段提交,无论是先写Redo log 后写 binlog,还是先写Binlog 后写 Redo log,都会出现主从数据库数据的不一致性。可利用反证法证明;


问题五:两个参数的意义?
答:innodb_flush_log_at_trx_commit:表示每次事务的redo log 都直接持久化到磁盘,值建议设置为1,可以保证MySql异常重启后的数据不会丢失;
sync_binlog: 表示每次事务的binlog都持久化到磁盘,这个参数最好也设置为1,可以保证mysql异常重启后binlog不丢失;
保证事务成功,参数设置为1后,日志必须落盘,这样在crash后不会出现数据的丢失;

问题六:有了Redo log,binlog能不能去掉?
答:不能去,至少目前不能去。
原因:
1、redo log只有innodb有,别的引擎没有;
2、redo log是循环写的,不持久保存,binlog的归档功能redo log不具备。所以在主从备份的时候还是需要server层所有引擎都可以用的binlog。
3、binglog没有crash-safe功能;
4、binlog是可以手动关闭的,所以只依靠binlog是不靠谱的;
ps:个人观点:当redo log可以追加写 并被所有的存储引擎可用的时候就可以丢弃binlog,并且redo log的恢复效率和同步效率会显著提高,因为它记录的是物理的变化。

问题七:redo log也是写io,如何做到优化呢?
答:主要优化有两点:顺序写 + 组提交
    首先数据库的数据更新都是基于内存页的更新,更新的时候不会直接更新磁盘,如果内存有数据就直接更新内存,如果没有就从磁盘读取数据到内存,在内存更新,并写入redo log。目的就是为了减少访问延迟,提高更新效率,等空闲的时候再将redo log所做的改变更新到磁盘中。Rodo log是顺序写,而update是直接更新磁盘,寻找到数据再进行更新;即使有索引也是随机写,所以速度会很慢;磁盘访问顺序写的时间优势,不用找“磁盘位置”。
    访问磁盘的时间:每次访问磁盘的一个块时,磁臂就需移动到正确的磁道上(这段时间为寻址时间),然后盘片就需旋转到正确的扇区上(这叫旋转时延),这套动作需要时间,所以说顺序写比随机写性能高,要知道db的最大瓶颈在io;

问题八:数据库Redo log只有commit的时候才会真正的提交吗?
答:正常情况是只有在commit时才提交到数据库落盘,但是当崩溃恢复的过程中,当存在“binlog完整 + redo log prpare ”的条件,数据也会自动被提交到数据库;redo log 和binlog 之间通过事务ID进行对应。

问题九:数据写在redo log上而没有写入数据库,那读到的数据不是不一致吗?
答:写到了内存,读取的时候是在内存读取。并且读和写操作会引起内存的淘汰。

问题10:mysql启动,对于innodb的启动是如何实现的,undo log的作用?
答:mysql重启,需要读完redo log的日志,从checkpoint开始到writepos结束。如果mysql的一个实例崩溃了,一个事务写入了redo log但是未写入binlog,也就是未提交commit,那么该mysql在重启的时候,会先恢复redo log,之后构造undo log回滚宕机前没有提交的事务。

binlog文件

查看命令:

show binlog events mysql-bin.000001;
             *************************** 20. row ***************************
                Log_name: mysql-bin.000001  ----------------------------------------------> 查询的binlog日志文件名
                     Pos: 11197 ----------------------------------------------------------> pos起始点:
              Event_type: Query ----------------------------------------------------------> 事件类型:Query
               Server_id: 1 --------------------------------------------------------------> 标识是由哪台服务器执行的
             End_log_pos: 11308 ----------------------------------------------------------> pos结束点:11308(即:下行的pos起始点)
                    Info: use `zyyshop`; INSERT INTO `team2` VALUES (0,345,'asdf8er5') ---> 执行的sql语句
             *************************** 21. row ***************************
                Log_name: mysql-bin.000001
                     Pos: 11308 ----------------------------------------------------------> pos起始点:11308(即:上行的pos结束点)
              Event_type: Query
               Server_id: 1
             End_log_pos: 11417
                    Info: use `zyyshop`; /*!40000 ALTER TABLE `team2` ENABLE KEYS */
             *************************** 22. row ***************************
                Log_name: mysql-bin.000001
                     Pos: 11417
              Event_type: Query
               Server_id: 1
             End_log_pos: 11510
                    Info: use `zyyshop`; DROP TABLE IF EXISTS `type`

学习笔记,内容简单,用于复习,原内容2月有更新。
##参考资料,《MySql实战详解》

发布了250 篇原创文章 · 获赞 191 · 访问量 57万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章