【JAVA核心知识】29:MySQL架构基础篇 ---《高性能MySQL》读书笔记

MySQL逻辑架构

在这里插入图片描述

图片来源:《高性能MySQL》

第一层做一下安全认证,授权处理之类的,大多数C/S工具都会有这样的结构。
第二层是大多数MySQL核心功能的所在,包括查询解析,分析,优化,以及所有的内置函数(例如日期,时间,加密函数),所有的跨存储引擎的功能也都在这一层实现(例如存储过程,触发器,视图)
第三层是存储引擎层,mysql有不同的存储引擎,服务器通过API和存储引擎通信,这样就屏蔽了不同存储引擎之间的差异。存储引擎API包含几十个底层函数,用于执行诸如‘开始一个事务’或者‘根据主键提取一行记录’等操作,存储引擎不会解析SQL,不同存储引擎之间也不会相互通信,而只是简单的响应上层服务器的请求。
因此一个SQL的执行过程如下:

  1. 连接器,进行身份认证,权限认证。权限认证对于一个连接来说是终身的,一旦认证完毕,即使管理后续修改了这个用户的权限也不会影响,除非断开重连。
  2. 查询缓存:执行之前,MySQL会先进行缓存查询,缓存以KV方式存储在内存中,Key是SQL,Value是结果集,如果命中就直接返回,没有命中就执行后续操作,最后把结果缓存起来。在MySQL8.0,缓存功能被舍弃,这是因为官方认为实际需要用的场景较少,对于常更新数据,缓存有效数据很短,而对于冷数据,缓存又显得没有必要。而对于经常读取的数据,大部分情况开发人员会自己在应用层缓存。
  3. 分析器:对SQL进行分析,分析SQL要干嘛
  4. 优化器:对SQL进行优化,选择最优的执行方案
  5. 执行器:校验权限,然后调用存储引擎的API去进行处理。

MySQL的缓冲池

MySQL数据是要落盘的,而磁盘的读写操作相较于内存慢的多,因此为了避免每次都操作磁盘,MySQL设计了基于内存的缓冲池来提高执行效率。缓冲池在存储引擎层,属于存储引擎的实现。
一条数据执行时,如果是读操作,要查找的数据所在的数据页在内存中时,则将结果返回,否则会先将其所在的数据页加载到内存中,然后再返回结果。如果是写操作,要修改的数据所在的数据页在内存中时,则修改后返回对应的结果(后续再刷入磁盘中),否则会先将其所在的数据页加载到内存中,再进行修改。
基于内存的操作会让执行效率大大提升,因此为了尽可能的让数据所在的数据页都在内存中,MySQL具有预读的特性,也就是说当读取一个数据页时,会将相邻的数个数据页也提前读入内存,因为大多数数据的获取都是相邻的。而不把所有的数据页都放入缓冲池一是因为内存的易失性,二是缓冲池大小有限制。
因为缓冲池大小的限制,缓冲池会采用LRU算法来淘汰不常用的数据页以避免溢出。
如果使用传统的LRU算法:维护一个链表,如果一个数据页被访问,就放到链表头部,如果这个数据页是新数据页,也同步抛弃链表尾部的是数据保证不溢出。这种算法对于MySQL的实现机制来说有两个隐患:预读失效和缓冲池污染。
预读失效:因为MySQL的预读机制,可能会一次加载多个数据页,而这些数据页并不一定是全都有用的,如果此时将这些数据页都放到链表头部,淘汰已有的尾部,那就可能出现无用的数据页被放入缓冲池,而常用的数据被移除。这就属于预读失效。
缓冲池污染:如果执行一条需要全表扫描或者扫描了大量数据的SQL,那么缓冲池会加载大量的数据页,如果采用传统LRU算法,会使得常用数据页都被替换出去,导致MySQL性能下降。这就是缓冲池污染。
为了应对预读失效和缓冲池污染,MySQL采用了冷热数据分离的方式,即数据页被读取后,先放到冷数据区域的头部,如果1S后缓冲页再次被访问,才将其移入热数据区域头部。1S的延迟是为了尽量保证这个数据页不是一个临时读取。另外,对于热数据区域来说,可能内部的数据页会被频繁读取,因此为了避免每次读取都维护链表,同时又能保证LRU算法的诉求,MySQL对热数据区的维护做了优化,只有后3/4的数据页被访问时才移动到头部,前1/4的数据页被访问了也不会移动。

存储引擎

文件系统中,MySQL将每一个数据库(也可以称为schema)保存为数据目录下的一个子目录,创建表时会在子目录下创建一个与表名相同的.frm文件保存表的定义。数据库中元数据大小写敏感性与平台相关,如window平台大小不敏感,那么元数据也不敏感。
可以用SHOW TABLE STATUS命令查看表信息,会包含占据空间大小,类型,时间等各种各样的数据。可以用ALTER TABLE tableName ENGINE = InnoDB修改存储引擎。
MySQL5.1及之前的默认引擎是MyISAM,MySQL5.1之后默认的引擎是InnoDB。在引擎选择上,除非你要使用InnoDb没有的特性,且没有其它方式替代,否则都应该优先选择InnoDB引擎。因此就只看InnoDB引擎了。

InnoDB

MySQL5.5之前使用的是InnoDB,MySQL5.5使用的是InnoDB plugin.这是因为Oracle收购了Sun公司,将InnoDB变成自己的了,因此MySQL5.5之前的版本可能使用的是旧的InnoDB,可以换成InnoDB plugin,性能好很多哦。除这一段之外本文所有的InnoDB其实都指的是InnoDB plugin。
InnoDB支持事务,InnoDB采用MVCC支持高并发,而是实现了4个标准的隔离级别,默认的隔离级别是REPEATABLE_READ(可重复读),并且通过GAP锁也就是间隙锁(next-key locking)策略避免了幻读。即 InnoDB 存储引擎自己实现了行锁,通过 next-key 锁(记录锁和间隙锁的组合)来锁住记录本身和记录之间的“间隙”,防止其他事务在这个记录之间插入新的记录,从而避免了幻读。GAP锁在READ COMMITTED也存在,但是仅仅用做外键约束和重复键检查,而不避免幻读,这是因为幻读并不一定是坏的,有些场景确实需要这种读取最新数据的操作,而READ COMMITTED定义来就是支持这种场景的。
InnoDB表基于聚簇索引建立,聚簇索引对查询有很高的性能,不过他的二级索引必须包含主键列,因此如果一个表的索引很多的话可以适当的减小主键内容,这样能减少索引的大小。InnoDB的数据文件和索引文件是平台独立的,也就是说可以把Intel平台的文件复制到PowerPC平台继续使用。
InnoDB有很多优化,比如磁盘读取数据时的可预测性预读,能够自动创建hash索引以加速读操作的自适应哈希索引(adaptive hash index),以及能够快速插入操作的插入缓冲区(insert buffer)等。后面细说。
InnoDB可以通过一些机制和工具实现真正的热备份,无需限制读写。MySQL其它存储引擎都不支持。

锁级别

mysql默认的锁策略是表级锁,也是开销最小的策略。同时mysql一些存储引擎如InnoDB和XtraDB也实现了行级锁,最大程度的支持并发,但是相应的锁开销也最大。注意是存储引擎层实现的,上层服务器层完全不了解,它只是调API而已。如果采用了InnoDB引擎,那就是行级锁了。

死锁

事务带来更高的安全性的同时也带来了更高的开销。对于事务死锁,比如事务A要锁定行1,行2,事务B要锁定行2,行1,如果事务A先锁定1,事务B先锁定2,此时就会出现死锁,对于这种死锁数据库系统实现了各种死锁检测和死锁超时机制。InnoDB目前的处理方式是将持有最少行级排他锁的事务进行回滚。同时为了避免因为批量更新的顺序差异导致的死锁问题,我们在更新时,如果是根据ID进行更新,那么可以将ID都按照相同顺序来排列,这样就可以避免死锁的发生。而在涉及二级索引更新的语句中,可能发生二级索引和一级索引加锁顺序(如一个SQL先锁二级再锁一级,一个SQL先锁一级再锁二级),此时就需要依据具体的业务场景通过减小锁粒度或者分布执行来解决死锁问题。

事务

事务的ACID

事务的ACID 4要素:原子性,一致性,隔离性,持久性。

  • Atomicity(原子性):一个事务内的所有操作,要么全部完成,要么全部失败,不会结束在中间的某个环节。事务在执行过程中发生错误,会恢复到事务执行前的状态,就像这个事务从未执行过一样。
  • Consistency(一致性):事务的执行不能破坏数据库的完整性,数据库只能从一个一致性状态转变到另一个一致性状态。写入的数据要符合预设的规则。包括但不限于主键唯一性,列完整性(类型长度符合要求),外键约束,用户自定义的完整性。不能说事务对数据库的修改只有一部分写入物理数据库,这种就属于一致性被破坏。
  • Isolation(隔离性):隔离性是指事务之间相互干扰的程度,一个事务对另一个事务的干扰程度不能超过定义的隔离级别。
  • Durability(持久性):事务执行完毕后,对数据的修改是永久的,即使数据库发生故障也不会丢失。

事务的隔离级别

隔离级别isolation:事务之间的隔离级别。数据更新时因为并发的问题会导致各种各样的问题:

  1. 更新丢失.多个事务对同一条数据操作。在事务A未提交时,事务B进行操作,此时后提交的事务就会覆盖掉先提交的事务所做的更新,造成了更新丢失。如银行账户.用户A账户中有10元钱。同时受到用户B转账的10元和用户C转账的20元。此时如果我们使用sql.1{UPDATE USERACCOUNT SET ACCOUNT = 20 WHERE USERNAME = A}与sql.2{UPDATE USERACCOUNT SET ACCOUNT = 30 WHERE USERNAME = A}.此时无论A先提交还是B先提交都会导致先提交的那个事务所做的更改被覆盖。事实上,很多数据库都会默认在底层加锁来避免这种现象发生。

  2. 脏读。一个事务查询到另一个事务未提交的数据,如果再查询之后另一个事务的更新操作被回滚就会出现脏读现象。如银行账户,用户A想买了价值1000元的商品。此时用户A余额为900元。于是就用户B给用户A转账100元。在转账事务未提交的时间,用户A看到自己账户以及变为1000元就去付款。此时B给A的转账事务被回滚。那么A付款时就会出现余额不足的清况。脏读是非常危险的。因为其读取到一个未提交的数据。一旦这个未提交的数据被回滚。那么以此查询为基础的之后任何操作在链式反应下全部都会得到期望之外的结果。

  3. 不可重复读。一个事务对同一条数据的两次查询返回了不同的结果。同样以账户为例。在一个事务T里面,用户A第一次查询账户余额为1000元。查询之后用户B转账给用户A100元。此时在事务T里面再次查询用户A的余额得到为1100元。在同一个事务里面对同一条数据的两次查询获得了不同的结果就产生了不可重复读。

  4. 幻读。在同一个事务中,条件相同的两次查询得到不同的结果。比如说在事务T中。查询所有姓A的客户的数量,获得结果为999位,此时又有一位A姓客户注册了,那么在事务T中再次查询姓A的客户的数量时获得结果为1000.此时便出现了幻读现象。

不可重复读和幻读都必须是在同一个事务中内的。不同的是不可重复读是对同一条数据的查询。幻读是相同条件下的一批数据的查询。

MySQL提供了4种事务隔离级别

  • READ_UNCOMMITTED:读未提交。此种隔离级别下未提交的的数据可以被查询到。会出现脏读,不可重复读,幻读。
  • READ_COMMITTED:读已提交。此种隔离级别下未提交的数据将不可被查询到。只有已提交的数据才可查询到。此种隔离级别不会出现脏读,但是会出现不可重复读和幻读。
  • REPEATABLE_READ:可重复读。此种隔离级别下不会出现脏读和不可重复读,但是会出现幻读。
  • SERIALIZABLE:串行化。最高的隔离级别。所有的事务串行执行,不允许并行。此种情况下脏读,不可重复读,幻读都不会出现。

隔离级别的提升势必会带来性能的下降。因为要花费更多的资源到事务隔离上。

事务中混合使用存储引擎

事务仅在支持事务的引擎上才有用,但是事务由存储引擎实现,所以在同一个事务中使用多种存储引擎是不可靠的。如果事务中涉及了事务型表(如InnoDB)和非事务型表(如MyISAM表),正常提交情况下是没有问题的,但是在异常情况下,事务型表可以回滚,非事务型却无法执行回滚操作,破坏事务的原子性。因此如果在非事务表上产生了回滚操作,MySQL会发出一个警告:某些非事务型的表上的变更不能被回滚。正常提交下则不会有任何提示或报错。
mysql目前支持事务的引擎有InnoDB和NDB Cluster。还有一些第三方的引擎如XtraDB和PBXT。
MySQL默认采用自动提交的事务策略,可以通过AUTOCOMMIT变量控制启动和禁用。这个对于非事务引擎是没用的,毕竟非事务引擎都没事务。如MyISAM表。
事务所加的锁属于隐式加锁,InnoDB可以通过SQL语句进行显示锁表。显示锁定的使用要慎重,尽量避免使用,因为严重影响性能,而且一般你都没有InnoDB自带的行级锁做的更好。

lock table ***;
select * from tableName where id = 1 lock in shard mode;
select * from tableName where id = 1 for update;

binlog,redolog与undolog

binlog

binlog用于记录数据库执行的修改性操作,以二进制的形式保存在磁盘中。binlog是MySQL的逻辑日志,由服务层进行记录,也就是说无论使用任何存储引擎的MySQL都会记录,binlog通过追加的方式写入,可以通过max_big_size参数设置每个binlog文件的大小,当文件大小达到给定值后,会生成新的文件来保存。binlog主要应用于数据的备份(包括主从复制)与数据恢复。
binlog在事务提交时记录在内存中,刷盘时机则通过sync_binlog参数决定,参数的取值范围为0-N:

  • 0:表示不强制要求,由系统自行判断何时写入磁盘
  • 1:表示每次commit都会将binlog写入磁盘
  • N:标识commit N个事务,进行一次写入操作

很明显,1是最安全的设置,也是MySQL 5.7.7之后的默认值。
binlog日志的记录形式有三种:STATEMENT,ROW,MIXED。通过binlog-format指定。

  • STATEMENT:此种方式会直接记录修改SQL到binlog中。这种方式的优点是无需记录每行的变化,减少了binlog的日志量,也从侧面降低了IO。缺点是在某些场景下会导致主从数据不一致。比如使用了sysdate()或者sleep(),由于机器时钟差异的问题,就会导致数据不一致,此外还有主从上下文不一致式,使用UUID()函数或者user()函数时,都可能导致数据不一致的问题。验证可以看这篇博文:MySQL主备复制数据不一致的情况
  • ROW:MySQL 8.0之后的默认形式。5.7及之前默认关闭,没有默认值。基于行的记录,也就是说不是记录SQL是什么,而是记录哪条数据被修改成了什么状态。当做就是一个对目标数据的set操作(只是可以这么理解,毕竟SQL并不只有update,还有表结构的修改)。这样的好处是不会出现数据不一致,缺点是会产生大量的日志,尤其是alter table时。
  • MIXED: 混合模式,MySQL自己决定使用STATEMENT还是ROW,一般是STATEMENT能胜任的使用STATEMENT,不能胜任的使用ROW。

主从复制时,主从的备份方式要记录设置同步,否则使用binlog备份时就会出现数据无法同步的方式。

redolog

redolog归属于InnoDB存储引擎,内部记录着所有执行的DML语句。属于物理日志(注意逻辑日志和物理日志的区分,逻辑日志只是记录了数据的逻辑,和磁盘地址没关系,而物理日志则有磁盘地址有关,记录着物理地址的值是什么)。我们都知道的事务的有持久化的特性,而要实现持久化,仅仅录入内存是不够的,势必要与磁盘进行交互,而InnoDB与磁盘的交互是以数据页为单位的,如果一个事务只修改了一个数据行内的某几个字段,此时就进行一次完整的数据页刷入的话,不仅影响性能,还导致资源浪费。而如果一个事务修改了了多个数据页,如果对没给数据页都进行磁盘操作,那么随机IO的情况下,这个事务的性能也会变得很差。redolog是InnoDB在保证性能的情况下实现持久性的关键,它记录事务对数据页进行了哪些修改,不仅减小了与磁盘的交互数据量大小,而且是顺序IO。
在计算机操作系统中,用户空间内的数据一般是无法直接写入磁盘的,需要先进入操作系统内核的缓冲区(OS Buffer),才能由操作系统调用fsync()写入磁盘。因此redolog的提供了三种刷盘方式,通过innodb_flush_log_at_trx_commit指定:

  • 0:延迟写,此种方式事务提交时不会直接将redolog buffer的日志写入OS Buffer,而是每秒写入一次,并调用fsync()写入磁盘(redolog文件)。此种无论是MySQL故障还是应用宕机都会导致1s以内的数据丢失。
  • 1:实时写,实时刷。默认的格式。此种方式事务的每次提交都会将redolog buffer的日志写入OS Buffer,再调用fsync()写入磁盘(redolog文件),不会导致数据丢失,但是性能相对差一些。
  • 2:实时写,延迟刷。此种方式事务提交后会直接将redolog buffer的日志写入OS Buffer,但是不会立即调用fsync(),而是1s执行一次。此种方式MySQL故障不会丢数据,因为数据已经进入操作系统,但是宕机依然会导致数据存在1s以内的丢失。

redolog不像binlog,无需长久保存,毕竟数据页持久化后他就没有存在的意义了,因此redolog采用的是大小固定,循环写入的方式,日志写到结尾是,会回到开头循环写,如下图:
在这里插入图片描述
redo文件记录有两个LSN(逻辑序列号)位置,分别是write pos和check point, check point表示当前数据页刷盘进度,write pos表示当前redolog 文件写入的位置。write pos随着redolog的写入追上check point时,就先暂停,推动check point向后移动空出新的位置。
如果应用故障或宕机,MySQL重启后就会先探查check point的位置,从check point开始进行恢复(当然实际不是这一句话这么简单)。
redolog和binlog各司其职,binlog进行数据备份, redolog的存在使得InnoDB具有crash-safe的能力。

undolog

undolog记录着数据的逻辑变化历程,属于逻辑日志,行数据的每次修改,都会copy原值的行到undolog日志中,然后生成新值的行。 通过他可以在事务执行失败时进行回滚,从而保证了事务的原子性。同时undolog也是MVCC实现的关键,这个在下面的MVCC章节来说。

日志的二段式提交

MySQL的binlog和InnoDB的redolog(undolog)采用二阶段提交方式,更新操作执行完毕后,InnoDB会记录redolog(undolog),此时redolog(undolog)进入prepare状态,然后InnoDB告知执行器执行完成,执行器记录binlog,记录完成后调用存储引擎接口,提交redolog(undolog)。以此来避免可能产生的数据不一致问题。如果出现宕机,会判定binlog是否完整,如果不完整就回滚redolog中的数据来保证数据同步
处理过程如下:

  1. 从内存中找出数据,进行更新
  2. 更新结果情况,写入undolog
  3. 将本次对物理数据页的修改记录到redolog(prepare)
  4. 将本次SQL逻辑操作记录到binlog中
  5. 由后台线程通过fsync进行刷盘(commit)。

如果不采用二段式提交,那么:

  • 先写 binlog 再写 redolog。那么假设 binlog 写完后崩溃了,此时 redolog 还没写。那么重启恢复的时候就会出现问题:binlog 中已经有记录了,此时从库已经接受到这条数据并完成了同步;但是 redolog 中没有关于 R 的记录,所以崩溃恢复之后,主库插入 R 记录的这个事务是无效的,即数据库中没有该行记录,这就造成了主从数据不一致。
  • 先写 redolog 再写 binlog。那么假设 redolog 写完后崩溃了,此时 binlog 还没写。那么重启恢复的时候redolog 中已经有 R 的记录了,属于有效事务,但是因为binlog未同步,从库却没有。也造成了数据不一致。

采用二阶段提交:
一阶段提交之后崩溃了,即写入 redo log,处于 prepare 状态 写biglog前的时候崩溃了。 可以执行事务回滚,因为事务还没生效。
如果写binlog后,但是Commit失败了,那么重启时间就直接再次Commit这个redolog就能完成同步。

MVCC-多版本并发控制

MySQL的大部分事务型引擎实现的都是行级锁,基于并发性能考虑,一般都实现了多版本并发控制(MVCC),不仅MySQL,Oracle,PgSQL等系统也都实现了MVCC,只不过实现机制可能不同。MVCC是行级锁的变种,避免了大多数情况下读的锁操作,因此开销更低,虽然机制不同,但是MVCC一般都实现了非阻塞的读操作。注意MVCC针对是读操作,写操作依然需要加锁实现。
MVCC是通过数据快照实现的,即整个事务期间,事务能观察到的数据都是一致的,都是事务开始时数据的快照。这就有一种可能,两个开始时间不同的事务,在同一时间观察到数据是不一致的。因此MVCC只在REPEATABLE_READ和READ_COMMITTED两个级别下工作,READ_UNCOMMITTED和SERIALIZABLE都和MVCC不兼容,这是因为READ_UNCOMMITTED总是读最新的数据,而不是符合事务版本的行,而SERIALIZABLE是对所有读取的行都加锁。
对于MVCC,InnoDB引擎下会维护一个事务版本号,每开启一个新事务,事务版本号都会自动递增并赋予这个事务,InnoDB通过在每行记录后面添加两个隐藏行来实现的,这两个隐藏的数据行一个存储创建事务id(trx_id),一个存储回滚指针roll_pointer,这个回滚指针就是指向undolog的日志地址。
隐藏字段
● DB_TRX_ID。 6 字节,插入或更新该行的最后一个事务的事务标识符。删除按照更新处理。以特殊位将其标记为已删除。
● DB_ROLL_PTR。 7 字节,回滚指针指向undolog日志记录。
● DB_ROW_ID。行 ID,6 字节。 该行 ID 随着新行的插入而单调增加。如果设置了聚簇索引。那么聚簇索引会含有该值,否则该列不会出现在任何索引中。

undolog章节提到:行数据的每次修改,都会copy原值的行到undolog日志中,然后生成新值的行,再通过每一行的隐藏列roll_pointer,就可以得到一个数据行的变动历史。如图
在这里插入图片描述

图片来源面试中的老大难-mysql事务和锁,一次性讲清楚!

ReadView

有了这个变动链,怎么判断一个事务应不应该被访问呢?InnoDB引入了可读视图ReadView的概念,包含以下四个属性:

  • m_ids: 记录ReadView创建时当前系统中活跃的读写事务的事务id列表。
  • min_trx_id:表示ReadView创建时系统中活跃的读写事务中最小的事务id,就是m_ids中的最小值。
  • max_trx_id:注意不是m_ids的最大值,而是ReadView生成时应该分配的下一个事务id。
  • creator_trx_id:关联事务的事务id

有了ReadView就可以判定一个数据行应不应该被访问了:

  1. 如果数据行的trx_id等于creator_trx_id,那么说明这个事务在访问一个自己修改的数据行,这个数据行应该被访问。
  2. 如果数据行的trx_id小于min_trx_id,那么这个数据行应该被访问。小于说明该版本已经被提交,因为min_trx_id代表活跃的最小版本。
  3. 如果数据行的trx_id大于等于max_trx_id,说明该版本在当前事务之后开启,不应该就访问。
  4. 如果数据行的trx_id在min_trx_id(包含)和max_trx_id(不包含)之间,那么就需要判断trx_id是不是在m_ids列表中,如果在说明该版本在当前事务启动时还没提交,处于运行状态,不应该被访问,如果不在说明该版本在当前事务启动已经提交,该版本可以被访问。

REPEATABLE_READ和READ_COMMITTED两个事务级别很大程度就是通过控制ReadView的创建时间实现的,READ_COMMITTED在每次SELECT时都会新建一个ReadView,而REPEATABLE_READ仅仅是在第一次SELECT时生成一个ReadView,后续就复用这个ReadView,这种方式不仅使得可重复读,还避免了一般意义上的幻读(为什么是一般意义上,见下一小节)。可以看到如果仅仅使用MVCC,RR反而比RC性能好一点,因为不需要频繁创建ReadView,然后实际场景中并不是只有简单读。

幻读,MVCC和间隙锁(GAP锁)

既然REPEATABLE_READ级别下好像通过复用ReadView就能解决幻读,为什么在上方存储引擎-InnoDB章节还说InnoDB通过GAP锁解决了幻读呢?这是因为MySQL的读分为两种,一种为快照读,一种为当前读。

  • 快照读:简单的SELECT语句就属于快照读,
  • 当前读:SELECT *** FOR UPDATE。或者是UPDATE、INSERT、DELETE操作对于的读操作。不要惊讶,UPDATE、INSERT、DELETE都是需要执行读操作的,毕竟UPDATE和DELETE要找到目标数据,INSERT也要判断是否冲突。

快照读可以通过MVCC解决幻读的问题,毕竟仅有读,但是当前读则不行,因为当前读涉及到了数据修改,对于修改,在一个事务锁定期间是不能运行其他事务操作的,此时仅靠MVCC就不行了,需要进行加锁。此时就需要间隙锁的加入了,间隙锁通过锁住两条记录之间的间隙,来达到避免幻读的效果。

主从同步

MySQL实现主从同步是通过binlog(备份日志)和 relay-log(中继日志)实现的,主节点将binlog信息发送给从节点,从节点将数据写到 relay-log(中继日志),然后根据 relay-log(中继日志)来进行同步。
MySQL的主从同步有四种:

  • 同步复制:主节点写好binlog日志就发送同步信息给从节点,commit后接到从节点的ACK才继续操作。从节点commit后才ACK。
  • 异步复制:默认方式,主节点写好binlog日志就放同步信息给从节点,然后就不管了。这种主节点宕机可能导致数据不同步,DBA人工查看binlog日志确定position来完成同步。MySQL5.6.5 提供了GTID来进行同步定位,理念就是为每个事务都生成一个ID并记录当前执行位置以进行定位。
  • 半同步复制:主节点写好binlog日志就发送同步信息给从节点,commit后接到所有从节点的ACK才继续操作。从节点写好binlog就ACK。
  • 增强半同步复制:主节点写好binlog日志就发送同步信息给从节点,接到任意一个从节点的ACK后才进行commit。从节点写好binlog就ACK。

参考资料:
《高性能MySQL》
一条SQL语句在MySQL中是如何执行的
MySQL 为什么需要两阶段提交?

PS:
【JAVA核心知识】系列导航 [持续更新中…]
关联导航:MySQL数据类型选择与设计
关联导航:创建高性能的索引
关联导航:查询性能优化
关联导航:EXPLAIN的使用
欢迎关注…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yue_hu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值