MySQL-related (two) - an update statement is how to implement it

Foreword

Previous article talked about "how a query is executed," should be a lot of people have noticed I added the prefix in the back of a MySQL-related (a), ha ha ha ha, there must be a two, serious and responsible how I saying only that the query execution flow statement said without modifying delete update statement execution process it? After much talking these processes would also like to say that the principle of certainty what index aspects of the matter, as Shashi Hou finished updating, Xixuan here only say, enjoy looking forward to ~

Here is a mind map, you want the full high-definition picture can go to my micro-channel public number under [6] reply Xixuan get MySQL mind map:
Here Insert Picture Description
Next we will start speaking with some knowledge of this mind map, but because article to limited space, some may only be a passing point, interested small partner to be my number with me a message of public discussion.

text

And then forward the text ramble

  • Why talking about the update instead of deleting and inserting it?

Because, update complexity is higher than inserting and deleting, if you have understood the updated set of processes, insert and delete processes also come in handy for you (does not seem appropriate, temporarily can not think of a better word to but the matter with disabilities), so there will only discuss the update, insert and delete processes similar, do not discuss.

  • Which database storage engine to use?

The current market popular or MySQL5.7, but most systems use the micro-architecture of distributed services, consider concurrent execution of transactions, selected here to explain the process to update statements innodb engine.

  • From what?

The update process is relatively complex, involving a database innodb transaction, so here will start to analyze disk + memory structure of the database structure of its internal mechanism, then by explaining redo log + undo log, the transaction log database side and binlog, service log level, there are two stages to furnish security database transaction ACID (temporarily expanded), to let everyone have a more comprehensive understanding of the entire process.

innodb disk and memory structures

According to my way of thinking, ah, let's look at this picture provided by the official website:

We can see from the figure:

  1. 左边是 innodb 的内存结构,其中包含自适应的 hash 索引(adaptive hash index),Buffer Pool,Change Buffer,最下面是 Log Buffer;
  2. 在内存数据刷到磁盘中间有操作系统的缓存;
  3. 右边是 innodb 的磁盘结构,包含系统表空间,独占表空间,共享表空间,临时表空间,redo log 和 undo log,用虚线表示的是逻辑存在而非物理存在;

接下来我会挑重点来介绍这些组件。

Buffer Pool

这个是 innodb 的缓冲池空间,保存的是数据页(data page)和索引页(index page),在修改数据的时候,数据不会直接写入到磁盘中,而会先写入 Buffer Pool(如果页数据在的话则修改 Buffer Pool),再由内存空间刷入到磁盘空间。

我们可以通过如下命令查看关于 innodb 的 Buffer Pool 的参数,有想详细了解的小伙伴请移步 MySQL 官网
show variables like '%innodb_buffer_pool%';
Here Insert Picture Description
show status like '%innodb_buffer_pool%';
Here Insert Picture Description
有几个点我们需要明确一下:

  1. 缓冲池(buffer pool)是一种常见的降低磁盘访问的机制;

使用 Buffer Pool,可以避免每次查询数据都跟磁盘进行 IO,磁盘读写,并不是按需读取,而是按页读取,一次至少读一页数据(1M / 64page=16K),如果未来要读取的数据就在页中,就能够省去后续的磁盘IO,提高效率,所以说Buffer Pool 是一种减少磁盘 IO 的机制。

  1. 缓冲池以页(page)为单位缓存数据;
  2. 缓冲池使用的内存淘汰算法是LRU,memcache,OS,InnoDB都使用了这种算法;

InnoDB对普通LRU(链表实现)进行了优化:将缓冲池分为老生代和新生代,入缓冲池的页,优先进入老生代,页被访问,才进入新生代,以解决预读失效的问题页被访问,且在老生代停留时间超过配置阈值的,才进入新生代,以解决批量数据访问,大量热数据淘汰的问题

这里再明确几个概念:

  • 脏页:当缓冲池数据和磁盘不一致的时候;
  • 刷脏:把脏页的数据写入磁盘中的时候叫刷脏;
  • 磁盘和缓冲区都是以页为单位,而关于页的大小:Here Insert Picture Description

Change Buffer

  • 更新数据页时,数据页已经加载到缓冲池中的时候,可以直接更新;
  • 那如果更新数据页时,如果数据页存在于磁盘,则需要将数据页先读取到内存,再修改内存,不在内存区域的话至少得发生一次 IO,这样不就资源浪费了?

这个时候 change buffer 就出现了,如果要更新的数据不是唯一索引,并且没有数据重复的情况,则不需要确保数据是否会重复。我们将要修改的记录写入到 change buffer 中,再通过 change buffer 一次性同步到磁盘中 ,减少 IO,5.5 之后叫 change buffer,以前叫 insert buffer (只支持 insert),这样可以减少 IO 次数,提升修改效率。

我们也可以用下面的命令看看 change_buffer 的参数:
show variables like '%change_buffer%';
Here Insert Picture Description
注意:max_size 不是指 change buffer 的大小,而是指 change buffer 占整个 buffer pool 的比例,写多读少的情况下可以适当调大 change buffer的比例 。

Log Buffer + Redo Log

看到这里,相信很多人还是会有很多问号:

  • 既然是用内存空间来做缓冲区,那如果数据库服务器宕机了,那未同步完的数据不都完了?

其实很多小伙伴应该之前有接触过或者听说过innodb 是支持事务的,而它支持事务的方式是 redo log + undo log 作为事务日志,但是对 redo log 可能没有进行一个比较深入的了解或者说并不知道它实际上是以一个什么样的角色存在。
好了不卖关子了,了解过 redis 的小伙伴应该都知道 redis 也是基于内存的,它做异常恢复的方式是通过持久化到磁盘的方式,那我们MySQL 的 innodb 引擎也是通过类似的方式,在刚才官网的结构图中我们看到磁盘空间中有 redo log,而 redo log 就是通过buffer 区的另外一块区域 log buffer 进行同步的,这个过程叫crash save。

  • 那已经有Buffer pool 作为缓存了,为啥还要多此一举再多一个 log buffer呢?

我想问一下大家 kafka 快的原因有哪些知道不?其中有一点就是多这个 log buffer 的原因。没错,这位同学答对了!就是顺序读取和随机读取的区别,随机读取是需要刷盘的操作,时长是不稳定的,而顺序读取相对而言就要快很多了,在崩溃恢复的时候起到了很大的作用。buffer pool 还是作为刷入磁盘的主角

  • 那 log buffer 什么时候才会触发刷数据到 redo log 的操作呢?

我们来看下 log buffer 的参数,大小默认是 16M:
show variables like '%log_buffer%';
Here Insert Picture Description
redo log 既然是存在于磁盘空间,那它是可以找到对应的文件的,在 mysql 目录中对应的文件名如下,默认是两个大小恒为 48M 的 ib_logfile 文件:
Here Insert Picture Description
既然大小是恒定的,那它肯定是会有删除数据的操作的,
Here Insert Picture Description
write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos 和 checkpoint 之间的是还空着的部分,可以用来记录新的操作。如果 write pos 跟 checkpoint 重合了,表示已经没有空间,这时候不能再执行新的更新,得停下来先删除一些记录,把 checkpoint 往前移一下。
那到底什么时候刷盘呢?
我的回答是跟事务相关,先看这条命令:
show variables like '%log_at_trx_commit%';
默认值是 1,
Here Insert Picture Description
默认是提交事务就写入 log buffer,值为 0 时事务提交后不会刷盘,值为 2 时由操作系统提交。
Here Insert Picture Description

  • 标题上没见到 undo log,为什么不说说 undo log?

redo log 和 undo log 是一对,它们组合到一起就是 innodb 的事务日志,但是 undo log 是关于事务提交回滚的日志,这次主要讨论流程,这里就一笔带过不做深入讨论。

binlog

用过 binlog 的伙伴应该知道binlog 有这几个功能:

  1. 记录DDL 和 DML 的逻辑日志
  2. 主从复制 (slave 拿到 master 的 binlog 再执行)
  3. 数据恢复

MySQL 在 server 层面引入了 binlog, 它可以记录所有引擎中的修改操作,因而可以对所有的引擎使用复制功能,然而这种情况会导致redo log与binlog的一致性问题。在 MySQL 中是通过内部 XA 机制解决这种一致性的问题。

我们先来看看 binlog 怎么配置,在 my.cnf 中配置 binlog:
vi /etc/my.cnf

log-bin=mysql-bin #添加这一行就ok
binlog-format=ROW #选择row模式
server_id=1 #配置mysql replaction需要定义,不能和canal的slaveId重复

业内目前推荐使用的是 row 模式,准确性高,虽然说文件大,但是现在有 SSD 和万兆光纤网络,这些磁盘 IO 和网络 IO 都是可以接受的。

在 innodb 里其实又可以分为两部分,一部分在缓存中,一部分在磁盘上。这里业内有一个词叫做刷盘,就是指将缓存中的日志刷到磁盘上。跟刷盘有关的参数有两个:
sync_binlog 和binlog_cache_size。
这两个参数作用如下:

binlog_cache_size: 二进制日志缓存部分的大小,默认值32k
sync_binlog=[N]: 表示每多少个事务写入缓冲,刷一次盘,默认值为0

要注意两点:

  1. binlog_cache_size设过大,会造成内存浪费。binlog_cache_size设置过小,会频繁将缓冲日志写入临时文件。
  2. sync_binlog=0:表示刷新binlog时间点由操作系统自身来决定,操作系统自身会每隔一段时间就会刷新缓存数据到磁盘,这个性能最好。sync_binlog=1,代表每次事务提交时就会刷新binlog到磁盘,对性能有一定的影响。sync_binlog=N,代表每N个事务提交会进行一次binlog刷新。
    另外,这里存在一个一致性问题,sync_binlog=N,数据库在操作系统宕机的时候,可能数据并没有同步到磁盘,于是再次重启数据库,会带来数据丢失问题。

MySQL 的 binlog 是多文件存储,定位一个 LogEvent 需要通过 binlog filename + binlog position,进行定位。

二阶段提交

二阶段提交时指当一个事务跨越多个节点时,为了保证事务的 ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。

因此,二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

第一阶段:

  1. InnoDB prepare, write/sync redo log;
  2. binlog不作任何操作;

第二阶段:包含两步:

  1. write/sync Binlog;
  2. InnoDB commit (commit in memory);

总结

写得是有点累啊,不过有大家的支持我觉得就是值得的,也是我更新的动力,不对的地方希望大家帮忙指正,觉得写得还可以的话麻烦大家帮我点个赞哈哈哈。

最后我们还是以一张图来将流程描述清楚:
Here Insert Picture Description

By the way

有问题?可以给我留言或私聊
有收获?那就顺手点个赞呗~

Of course, I can also go to the public under the number "6 Xixuan"
reply "learning", you can receive a
[Java architect engineer advanced video tutorials] ~
Reply "interview" can be obtained:
[I worked hard to organize the Java interview questions]
reply "MySQL brain map", you can get
*** [MySQL knowledge combing HD brain map] ***
Here Insert Picture Description
Because of my blanket, a trained programmer, php, Android and hardware have done, but finally chose to focus on doing Java, so what's the problem may be the number of public discussion questions (emotional talk technology can ha ha ha), will see it as soon as possible, hoping to learn together with you progress, on the server architecture, Java core analytical knowledge, career, interview summary and other articles occasionally adhere to push output, welcome attention ~ ~ ~

Published 29 original articles · won praise 9 · views 9933

Guess you like

Origin blog.csdn.net/weixin_42669785/article/details/104114754