Mysql 更新流程

前言
      首先这里更新指的是 update,insert与delete。在mysql的InnoDB数据引擎中,更新并不是直接去写物理磁盘,他中间会有一个缓冲池(Buffer Pool)的概念,而这边会引起一系列的故事(关于查询的可以查看之前我写的博客 https://blog.csdn.net/TT_4419/article/details/105037194)。
 
关于缓存
    缓存在我们实际生产环境中,频繁被使用到,他的作用在于抗并发(限流,降级也能抗并发,对于限流,降级有兴趣的小伙伴,可以参考这篇博客 https://blog.csdn.net/TT_4419/article/details/103551297)。
    磁盘的读取速度取决于他的转速和对应数据存储的碎片化程度,如果数据存储的过于零散,磁盘会不停的去转,去匹配对应的数据(俗称刷盘)
    在设计大型的高并发场景下,会有这样一个问题,缓存与物理存储不一致的问题。实际上,对于读操作,先读缓存,若缓存不命中,再去读数据库,同时同步数据到缓存中,这是比较认可的方式。
但是对于写操作,我们需要保证的是两个操作的一致性问题,这样就会涉及到先写缓存还是先写数据库的问题。如果,先写数据库缓存更新失败,则会导致第二次读到的数据还是之前的脏数据;如果先写缓存,再写数据库,会导致数据的准确性问题(我们以实际数据库的数据作为最终数据)
    
对于这样的一个问题,有几种解决方案。
    第一种,数据发生变化时,删除缓存,这个时候缓存会miss,同时再去数据库去load数据,同步缓存。看起来这个方案解决了问题,而且实际操作的时候十分简单,但是我们使用缓存就是为了减少磁盘交互,降低数据库压力,不然也不会把这些热点数据全部缓存起来。删除缓存,同时查询数据库,看起来解决了问题,同时保证了数据的强一致性,但是一旦遇到缓存雪崩这种场景,大批量的缓存失效,数据库的瞬时压力会很大。所以笔者并不太建议这种方式
    第二种,先修改缓存,再去修改数据库,一旦数据库修改失败,或者说在数据库修改过程中,缓存又发生了变化,这个时候,我们可以定时的去触发同步机制,同步缓存到数据库中,只需要保证数据的最终一致性(BASE理论)
 
 
 
InnoDB内存结构与磁盘结构
 
 
逻辑结构
 
Buffer Pool
    在InnoDB中操作数据的最小逻辑单位叫做页(索引页,数据页),缓存叫做Buffer Pool。在修改数据的时候,先修改缓冲池里面的页。内存的数据页和磁盘数据不一致的时候,我们把它叫做脏页。InnoDB 里面有专门的后台线程把 Buffer Pool 的数据写入到磁盘,每隔一段时间就一次性地把多个修改写入磁盘,这个动作就叫做刷脏。
buffer pool主要分为3个部分,Buffer Pool、Change Buffer、Adaptive Hash Index,另外还有一个(redo)log buffer。
 
然后出现了一个问题,缓存满了怎么办?
这边需要引入一个LRU的技术,所谓的LRU 指的是 Least Recently used 也就是最近最少使用。算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
然后mysql的缓存淘汰策略并非传统的链表+容器,而是区分为yong区和old区。
 
 
Change Buffer Pool
    如果当前数据页不是唯一索引,不需要进行校验数据重复,,也就不需要从磁盘加载索引页判断数据是不是重复(唯一性检查)。
这种情况下可以先把修改记录在内存的缓冲池中,从而提升更新语句(Insert、Delete、Update)的执行速度
这一块区域就是 Change Buffer。5.5 之前叫 Insert Buffer 插入缓冲,现在也能支持 delete 和 update。
 
最后把 Change Buffer 记录到数据页的操作叫做 merge。在访问这个数据页的时候,或者通过后台线程、或者数据库 shut down、redo log 写满时触发。
如果数据库大部分索引都是非唯一索引,并且业务是写多读少,不会在写数据后立刻读取,就可以使用 Change Buffer(写缓冲)。
写多读少的业务,调大这个值:
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size';
代表着Change buffer占Buffer Pool的值 默认是25%
 
Adaptive Hash Index
存储的是hash索引
 
(redo)Log Buffer
在说这个Log buffer的时候 我们先引出一个问题,Redis的备份机制,redis的备份机制分为两种,一种RDB,一种AOF备份。在我看来AOF的备份的形式和InnoDB的备份形式很像,都是先写日志,再写磁盘。一旦发起服务器宕机,就靠这个操作日志文件进行恢复
 
那么很明显,Log Buffer 作用于服务器宕机下的恢复,而这样一种日志和磁盘相互配合的整个过程,就是Mysql中的WAL技术(Write-Ahead Logging),关键点在于先写日志,后写磁盘。
在百度百科中的定义中,WAL日志的数据库系统在事务提交时,磁盘写操作只有传统的回滚日志的一半左右,大大提高了数据库磁盘I/O操作的效率,从而提高了数据库的性能。
然后为什么呢,他没有说。其实很简单,顺序IO和随机IO
 
磁盘的最小组成单元是扇区,通常是 512 个字节。
操作系统和内存打交道,最小的单位是页 Page。
操作系统和磁盘打交道,读写磁盘,最小的单位是块 Block。
 
 
 
如果我们所需要的数据是随机分散在不同页的不同扇区中,那么找到相应的数据需要等到磁臂旋转到指定的页,然后盘片寻找到对应的扇区,才能找到我们所需要的一块数据,一次进行此过程直到找完所有数据,这个就是随机 IO,读取数据速度较慢。
 
假设我们已经找到了第一块数据,并且其他所需的数据就在这一块数据后边,那么就不需要重新寻址,可以依次拿到我们所需的数据,这个就叫顺序 IO。
刷盘是随机 I/O,而记录日志是顺序 I/O,顺序 I/O 效率更高。因此先把修改写入日志,可以延迟刷盘时机,进而提升系统吞吐。
 
当然 redo log 也不是每一次都直接写入磁盘,在 Buffer Pool 里面有一块内存区域(Log Buffer)专门用来保存即将要写入日志文件的数据,默认 16M,它一样可以节省磁盘 IO。
 
log buffer 写入磁盘的时机,由一个参数控制,默认是 1。
 
 
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
 
模型图如下,这里也可以清楚的看到,redo log日志 分为内存和磁盘模块
 
 
关于redo log日志的几个特点:
 
1.redo log 是 InnoDB 存储引擎实现的,并不是所有存储引擎都有。
2.不是记录数据页更新之后的状态,而是记录这个页做了什么改动,属于物理日志。
3、redo log 的大小是固定的,前面的内容会被覆盖
check point 是当前要覆盖的位置。如果 write pos 跟 check point 重叠,说明 redo log 已经写满,这时候需要同步 redo log 到磁盘中。
 
以上,我们基本讲完了第一张系统图的内存结构,他主要分为Buffer Pool,change buffer,Adaptive Hash Index, log buffer。
 
磁盘结构
 
磁盘结构里面主要是各种各样的表空间,叫做 Table space。(是不是很熟悉,其实oracle也有一个表空间)
 
表空间可以看做是 InnoDB 存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。InnoDB 的表空间分为 5 大类。
 
system tablespace 系统表空间
 
在默认情况下 InnoDB 存储引擎有一个共享表空间(对应文件/var/lib/mysql/ibdata1),也叫系统表空间。
InnoDB 系统表空间包含 InnoDB 数据字典和双写缓冲区,Change Buffer 和 Undo Logs),如果没有指定 file-per-table,也包含用户创建的表和索引数据。
 
1、undo 在后面介绍,因为有独立的表空间。
2、数据字典:由内部系统表组成,存储表和索引的元数据(定义信息)。
3、双写缓冲(InnoDB 的一大特性):InnoDB 的页和操作系统的页大小不一致,InnoDB 页大小一般为 16K,操作系统页大小为 4K,InnoDB 的页写入到磁盘时,一个页需要分 4 次写。
 
 
如果存储引擎正在写入页的数据到磁盘时发生了宕机,可能出现页只写了一部分的情况,比如只写了 4K,就宕机了,这种情况叫做部分写失效(partial page write),可能会导致数据丢失。
 
我们不是有 redo log 吗?但是有个问题,如果这个页本身已经损坏了,用它来做崩溃恢复是没有意义的。所以在对于应用 redo log 之前,需要一个页的副本。如果出现了写入失效,就用页的副本来还原这个页,然后再应用 redo log。这个页的副本就是 double write,InnoDB 的双写技术。通过它实现了数据页的可靠性。
 
跟 redo log 一样,double write 由两部分组成,一部分是内存的 double write,一个部分是磁盘上的 double write。因为 double write 是顺序写入的,不会带来很大的开销。
在默认情况下,所有的表共享一个系统表空间,这个文件会越来越大,而且它的空间不会收缩。
 
 
独占表空间 file-per-table tablespaces
我们可以让每张表独占一个表空间。这个开关通过 innodb_file_per_table 设置,默认开启。
SHOW VARIABLES LIKE 'innodb_file_per_table';
开启后,则每张表会开辟一个表空间,这个文件就是数据目录下的 ibd 文件(例如/var/lib/mysql/gupao/user_innodb.ibd),存放表的索引和数据。但是其他类的数据,如回滚(undo)信息,插入缓冲索引页、系统事务信息,二次写缓冲(Double write buffer)等还是存放在原来的共享表空间内。
 
通用表空间 general tablespaces
通用表空间也是一种共享的表空间,跟 ibdata1 类似。可以创建一个通用的表空间,用来存储不同数据库的表,数据路径和文件可以自定义。语法:
create tablespace ts2673 add datafile '/var/lib/mysql/ts2673.ibd' file_block_size= 16 K engine = innodb ;
在创建表的时候可以指定表空间,用 ALTER 修改表空间可以转移表空间。
create table t2673(id integer) tablespace ts2673;
不同表空间的数据是可以移动的。删除表空间需要先删除里面的所有表:
 
临时表空间 temporary tablespaces
存储临时表的数据,包括用户创建的临时表,和磁盘的内部临时表。对应数据目录下的 ibtmp1 文件。当数据服务器正常关闭时,该表空间被删除,下次重新产生。
 
Undo log
undo log(撤销日志或回滚日志)记录了事务发生之前的数据状态(不包括 select)。
如果修改数据时出现异常,可以用 undo log 来实现回滚操作(保持原子性)。在执行 undo 的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,属于逻辑格式的日志。
redo Log 和 undo Log 与事务密切相关,统称为事务日志。
undo Log 的数据默认在系统表空间 ibdata1 文件中,因为共享表空间不会自动收缩,也可以单独创建一个 undo 表空间
 
BingLog
 
binlog 以事件的形式记录了所有的 DDL 和 DML 语句(因为它记录的是操作而不是数据值,属于逻辑日志),可以用来做主从复制和数据恢复。
跟 redo log 不一样,它的文件内容是可以追加的,没有固定大小限制。
在开启了 binlog 功能的情况下,我们可以把 binlog 导出成 SQL 语句,把所有的操作重放一遍,来实现数据的恢复。
binlog 的另一个功能就是用来实现主从复制,它的原理就是从服务器读取主服务器的 binlog,然后执行一遍。
 
整体执行SQL过程
 
 
 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值