面经-mysql

数据库调优的措施

1.选择合适的存储引擎,比如对于mysql来说,如果进行事务处理的话可以选择lnnoDB,非事务处理可以选择MylSAM

2.优化表的结构

3.优化逻辑查询,比如子查询会生成一个临时表,可以用连接查询代替子查询

4.优化索引

5.使用 Redis 或 Memcached 作为缓存

6.读写分离等等

7.优化数据库的结构

 如何优化数据库的结构

 1.拆分表:冷热数据分离

把1个包含很多字段的表拆分成2个或者多个相对较小的表。这样做的原因是,这些表中某些字段的操作频率很高(热数据),经常要进行查询或者更新操作,而另外一些字段的使用频率却很低(冷数据),冷热数据分离,可以减小表的宽度。如果放在一个表里面,每次查询都要读取大记录,会消耗较多的资源。

2.增加中间表

对于需要经常联合查询的表,可以建立中间表以提高查询效率。通过建立中间表,把需要经常联合查询的数据插入中间表中,然后将原来的联合查询改为对中间表的查询,以此来提高查询效率。

 大表优化

当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:

1.读/写分离

模式一:一主一从模式,主库负责写,从库负责读

模式二:双主双从模式

2.垂直拆分

 当数据量级达到 千万级 以上时,有时候我们需要把一个数据库切成多份,放到不同的数据库服务器上,减少对单一数据库服务器的访问压力。

  • 如果数据库中的数据表过多,可以采用垂直分库的方式,将关联的数据表部署在同一个数据库上。
  • 如果数据表中的列过多,可以采用垂直分表的方式,将一张数据表分拆成多张数据表,把经常一起使用的列放到同一张表里。

垂直拆分的优点: 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。

垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起 JOIN 操作。此外,垂直拆分会让事务变得更加复杂。

3.水平拆分

这里我们主要考虑业务数据的水平分表策略。将大的效x据表按照某个属性维度分拆成不同的小表,每张小表保持相同的表结构。比如你可以按照年份来划分,把不同年份的数据放到不同的数据表中。2017年、2018年和2019年的数据就可以分别放到三张数据表中。
 

MySQL的存储引擎

常见的有两种存储引擎(5.5之前默认的存储引擎)Myisam和(5.5之后默认的存储引擎)Innodb

Myisam

  • 不支持事务,崩溃后无法安全恢复
  • 不支持行级锁,只支持表级锁
  • 不支持外键
  • 适合多查询、插入的场景,访问速度快
  • 针对数据统计有额外的常数存储。故count(*)的查询效率很高,O(1)

Innodb

 优点

  • 具有外键功能
  • 支持事务
  • 适合多更新、删除操作的场景
  • 支持行锁,锁的粒度小,操作时只锁某一行,不对其他行有影响,适合高并发操作

缺点;

  • 对比MyISAM的存储引擎,InnoDB写的处理效率差一些,并且会占用更多的磁盘空间以保存数据和索引。
  • .MylSAM只缓存索引,不缓存真实数据;InnoDB不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响。(因为对于InnoDB引擎来说,数据与索引是一起存储的,都存在.ibd文件中,索引即数据;而MyISAM的索引和数据是分开存储的,.frm存储表结构,.myd存储表数据,.myi存储表索引)

Q:什么是事务?

A:首先我们要知道事务需要解决什么问题,当我们插入多条数据到数据库时,可能会出现:

  • 数据库中途突然因为某些原因挂掉了。
  • 客户端突然因为网络原因连接不上数据库了。

这些问题都可能会导致数据的不一致性。为了保证数据的一致性,系统必须能够处理这些问题。事务就是我们抽象出来简化这些问题的首选机制。

所以,事务就是是逻辑上的一组操作,要么都执行,要么都不执行

Q:关系型数据库的四大特性(ACID)

A:

  1. 原子性Atomicity) : 事务是最小的执行单位,不允许分割。
  2. 一致性Consistency): 执行事务前后,数据从一个合法性的状态到另一个合法性的状态;
  3. 隔离性Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性Durabilily): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。(持久性一定是数据写入磁盘中)

Q:并发事务带来了哪些问题?

A:

脏读(Dirty read): 对于两个事务Session A、Session B,Session A读取了已经被Session B更新但还没有被提交的字段。之后若Session B回滚,SessionA读取的内容就是临时且无效的;

脏写(Dirty write):对于两个事务Session A、Session B,Session A修改了已经被Session B更新但还没有被提交的字段,那就意味着发生了脏写;

不可重复读(Unrepeatable read): 对于两个事务Session A、Session B,Session A读取了一个字段,然后Session B更新/删除了该字段。之后Session A再次读取同一个字段,值就不同/没了。那就意味着发生了不可重复读。

幻读(Phantom read): 幻读与不可重复读类似。对于两个事务Session A、Session B, Session A从一个表中读取了一个字段,然后Session B在该表中插入了一些新的行。之后,如果Session A再次读取同一个表,就会多出几行。那就意味着发生了幻读。

Q:数据库是如何解决的?

A: 为了解决这些问题,sql定义了四种隔离级别:

  • READ-UNCOMMITTED(读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读) : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读但幻读仍有可能发生mysql的默认隔离级别
  • SERIALIZABLE(可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读

脏写是最严重的问题,所以四种隔离级别都解决了脏写的问题 

事务的隔离性由锁机制实现。

而事务的原子性、一致性和持久性有事务的redo log和 undo log来保证

        redo log保证事务的持久性

        undo log保证事务的原子性、一致性

首先为什么会有redo log?

我们先必须明白InnoDB存储引擎是以为单位来管理存储空间的。在真正访问页面之前,需要把在磁盘上的页缓存到内存中的Buffer Pool(缓冲池)之后才可以访问。所有的变更都必须先更新缓冲池中的数据然后缓冲池中的脏页会以一定的频率被刷入磁盘( checkPoint机制),通过缓冲池来优化CPu和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。

类似于JMM

 这样会出现的问题就是事务提交后,刚写完缓冲池还未刷入磁盘,数据库宕机,那么这段数据就丢失了,不能保证持久性

所以我们需要解决这个问题,怎么做呢?

一个简单的做法∶就是做到实时更新,缓冲池一旦更新,就刷入磁盘

问题:①修改量与刷新磁盘工作量严重不成比例。加入我们仅仅修改某个页面中的一个字节的数据,但是InnoDB是以页为单位进行磁盘IO的,显然是小题大做;

          ②加入我们需要将工资为5000的员工工资改为6000,但是有可能这些员工信息是存储在不同的不连续的页中,那么刷新到磁盘中时,需要很多随机IO,效率低

怎么做更好呢?那就是redo log

就是将缓冲池中修改的数据,先保存到文件中(也就是redo log,是真实存在的文件)。假如有更新的数据没来得及刷入到磁盘。数据库重启之后,就直接读取redo log,就能保证commit的数据的操作能体现在磁盘中,保证持久性

Redo log的优点

  • .redo日志降低了刷盘频率(相比如之前那个简单方法,刷入磁盘的频率不必再是一更新就刷)
  •    redo日志占用的空间非常小(存储表空间ID、页号、偏移量以及需要更新的值,所需的存储空间是很小的,刷盘快。)

Redo log的特点

  • redo日志是顺序写入磁盘的
  • 事务执行过程中,redo log不断记录,不是commit之后才记录

Redo log的执行过程

 针对步骤3的刷盘策略 170-Redo日志的刷盘策略与过程剖析_哔哩哔哩_bilibili

171-写入Redo Log Buffer和Redo Log File的写入策略_哔哩哔哩_bilibili

刷盘策略

针对上图3

InnoDB给出 innodb_flush_log_at_trx_commit 参数,该参数控制 commit提交事务
时,如何将 redo log buffer 中的日志刷新到 redo log file 中。它支持三种策略:

  • 设置为0 :表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次重做日志的同步)

  • 设置为1 :表示每次事务提交时都将进行同步,刷盘操作( 默认值 )

  • 设置为2 :表示每次事务提交时都只把 redo log buffer 内容写入 page cache(文件系统缓存 ),不进行同步。由os自己决定什么时候同步到磁盘文件。

Undo log

 什么是Undo log?

每当我们要对一条记录做改动时(这里的改动可以指TINSERT、DELETE、UPDATE ),都需要把回滚时所需的东西记下来。MysQL把这些为了回滚而记录的这些内容称之为撤销日志或者回滚日志(即undo log )。注意,由于查询操作( SELECT )并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的undo日志。

redo log是事务持久性的保证,undo log是事务原子性的保证。

作用?

  • 回滚数据,注意undo是逻辑日志,是指将修改的逻辑取消,必须insert一条数据时,需要开辟一个新页,但是回滚的时候,只是将该数据删除,并不会把新增的页也删除。再比如说手机电池不行了,需要更换电池,更换之后,是好了,但是不是之前的电池了

Undo log的生命周期

 8 和 9 的顺序取决于你如何设置

Mysql如何处理事务的

持久性:redoLog保证,一定会先写入redoLog日志才算真正提交,之后一定机制刷数据到磁盘,如果出现意外redoLog将会起到作用;
原子性、一致性:undoLog日志实现,每个事物修改一条数据前会留下一个旧版本的数据副本在undoLog里,如果事物回滚就将undoLog里对应的数据覆盖回去;
隔离性:MySQL默认是RR级别,是mvcc去实现的,同时也保证了最大的并发性,每行有个tx_id,undoLog_指针,undoLog日志,
在每个事务进行事务读的时候会加锁,比如select * from t where id = 1 for update;会把id1锁住,
对于写写并发事务,第二个事务如果也要修改这个id1的数据就会阻塞,有行锁间隙锁表锁实现,
对于读写、写读事务mvcc机制则可以保证并发执行,比如另一个事务锁住的是id2他并不修改id1的数据但是它看到的id1的数据一定是稳定的(即其他事务已经提交了的),
对于事务1对id1在未提交前的任何操作事务2是看不见的,实现了可重复读(快照读),
事务2在第一次读取数据的时候会生成一个ReadView对象(如果是读已提交则每次都生成一个ReadView(当前读)),
里面有记录了当时的事务id列表,根据这个列表事务2可以知道哪些数据可见哪些不可见,如果事务1提交了,事务2是可以看到的因为事务2并不关心id1的数据变化,
同理对于事务1来说,事务2对于id2的数据修改只有事务2能看到

Q:Mysql使用的是什么索引?为什么?

A:从MysQL的角度讲,数据库数据都是存放在磁盘中的,读取数据库数据就是要去磁盘中读取(为什么不全部存放在内存中?查找都是索引操作,一般来说索引非常大,尤其是关系型数据库,当数据量比较大的时候,索引的大小有可能几个G甚至更多,为了减少索引在内存的占用,数据库索引是存储在外部磁盘上的。当我们利用索引查询的时候,不可能把整个索引全部加载到内存,只能逐一加载,那么MysQL衡量查询效率的标准就是磁盘l0次数),不得不考虑一个现实问题就是磁盘IO。如果我们能让索引的数据结构尽量减少硬盘的I/o操作,所消耗的时间也就越小。可以说,磁盘的 IO操作次数对索引的使用效率至关重要。

加速查找速度的数据结构有两种:树和hash表

首先Hash表的CRUD时间复杂度是O(1)

树,例如平衡二叉树,查询/插入/修改/删除的平均时间复杂度都是0(log2N);
那为什么不用hash呢?

原因1: Hash索引仅能满足(=) (<>)和IN查询。如果进行范围查询,哈希型的索引,时间复杂度会退化为o(n);而树型的“有序"特性,依然能够保持o(log2N)的高效率。
原因2:Hash 索引还有一个缺陷,数据的存储是没有顺序的,在ORDER BY的情况下,使用Hash索引还需要对数据重新排序
原因3:对于联合索引的情况,Hash值是将联合索引键合并起来计算的,无法对单独的一个键或者几个索引键进行查询。
原因4∶对于等值查询来说,通常Hash索引的效率更高,不过也存在一种情况,就是索引列的重复值如果很多,效率就会降低。这是因为遇到Hash冲突时,需要遍历桶中的行指针来进行比较,找到查询的关键字,非常耗时。比如年龄,性别

如果是MyIsam或者是InnoDB存储引擎,那么对应的底层的数据结构为B+树,如果是Memory存储引擎,那么对应的底层的数据结构为Hash表。

树,二叉排序树可能会退化成链表,就不存在二分查找,查询时间复杂度O(n);所以为了控制深度来看看平衡二叉树,能保证查询时间复杂度一定是O(log2N),但是为了保证平衡,在插入数据的时候必须要旋转,插入性能有所损失。但是一个结点最多只有两个子节点,数据库数据庞大会导致树的深度很大,磁盘 IO 的次数多,性能低;

那么我们变成三叉树,四叉树呢

那如果用红黑树呢?

  • 平衡二叉树的左右子树的高度差绝对值不超过1,但是红黑树在某些时刻可能会超过1,只要符合红黑树的五个条件即可。
  • 二叉树只要不平衡就会进行旋转,而红黑树不符合规则时,有些情况只用改变颜色不用旋转,就能达到平衡,修改性能较高

但是还是存在一个大问题,就是深度过大,磁盘IO效率低

如果把有序二叉树变成有序多叉树,就能降低树的高度,这个就是演变出来的B树的核心思想。

B+树和B-树有什么区别,

①区别就是B+树只有叶子结点存放数据,B-树非叶子结点也存放数据。这样就导致,遍历的时候B-树需要中序遍历,但是B+树直接顺序遍历

 ②最主要的就是B+树平均IO次数会比B-树少,B-树的非叶子结点存放数据,那么目录页的结点树就少,会导致B-树高瘦;B+树非叶子结点不存放数据,一页可以存更多结点,B+树就会矮胖,IO效率高

所以采用B+树的最根本的原因是由于二叉树的树太高,树太高则直接影响到磁盘IO的次数,影响数据查询的效率,采用B+树的数据结构,可以在某个数据节点里面尽可能多的存储数据,使树的高度尽量的变低,提高效率。

【数据结构】红黑树与平衡二叉树的区别以及原理详解(附图解)_雪花不落的博客-CSDN博客_红黑树与平衡二叉树

B-树和B+树区别

①B-树非叶子节点存放数据,B+树只有叶子结点存放数据,非叶子结点只存放key

②B-树的叶子结点在物理上没有关系,B+树的叶子结点是用指针连在一起的,形成链表结构

Q:什么是慢查询,如何判断是否是慢查询

A:指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10秒以上(不含10秒)的语句,认为是慢查询

如何判断是否是慢查询:

 慢查询日志分析工具:Mysqldumpslow

①首先要进入慢查询日志的目录 ,慢查询日志文件是以 -slow.log结尾的

image-20220812160221292

 ②然后输入指令

Q:聚簇索引和非聚簇索引

A:在innoDB中每个索引都对应一颗B+树,而聚簇索引和非聚簇索引最大的区别在于叶子节点存储的数据不同,聚簇索引叶子节点存储的是行数据,因此通过聚簇索引可以直接找到真正的行数据,而非聚簇索引叶子节点存储的一个是非主键信息,一个是主键信息,利用非聚簇索引查询,还需要回表查询

Q:联合索引

A:我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引,比方说有一个表有三个字段(c1(主键),c2,c3),以 c2 和 c3 列的大小为排序规则建立的 B+ 树称为 联合索引 。那么这颗B+树的特点是

每条目录项记录都由c2、c3、页号这三个部分组成,各条记录先按照c2列的值进行排序,如果记录的c2列相同,则按照c3列的值进行排序。
B+树叶子节点处的用户记录由c2、c3主键c1列组成。

Q:索引失效的情况

①非最左匹配(最左匹配原则:最左优先,在检索数据时从联合索引的最左边开始匹配):假如我们以A+B+C的顺序建立联合索引,如果以A+C/A+B/A+B+C的顺序查询符合最左匹配,如果以B+C/C+B/C+A则不符合最左匹配

②如果索引列涉及计算、函数、类型转换(自动或手动)导致索引失效

③范围条件右边的列索引失效,如果以A+B+C顺序建立联合索引,SQL语句WHERE a=10 AND b>10 AND c<9 ,那么索引之后涉及到前两个字段,即b>10后面的索引全部失效

如何知道的,使用EXPLAIN,查看key_len

 ④如果索引列涉及不等于(!= 或者 <>),索引失效

⑤is null可以使用索引,is not null无法使用索引

⑥like 以通配符 % 开头索引失效

⑦ OR 前后存在非索引的列,索引失效,也就是说,OR 前后的两个条件中的列都是索引时,查询中才使用索引。.

Q:什么是MVCC?

A:

MVCC (Multiversion Concurrency Control),多版本并发控制。MVCC 是通过数据行的多个版本(undolog实现)管理(readview实现)来实现数据库的`并发控制`。

标准的sql的可重复读解决了脏读、脏写、不可重复读,mysql则通过MVCC在RR的默认隔离级别下解决了幻读的问题

MVCC的实现依赖于隐藏字段、Undo Log、Read View

ReadView解决了查询这个行记录时,需要读取哪个版本的问题。

这个ReadView中主要包含 4 个比较重要的内容,分别如下:

  1. creator_trx_id,创建这个 Read View 的事务 ID。

    说明:只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为 0 。

  2. trx_ids,表示在生成ReadView时当前系统中活跃的读写事务的事务id列表

  3. up_limit_id,活跃的事务中最小的事务 ID。

  4. low_limit_id,表示生成ReadView时系统中应该分配给下一个事务的id值。

MVCC的实现过程(举例的方式),分为两种情况 读已提交/可重复读 ,两种隔离级别的MVCC的区别是:

读已提交:每次读取数据前都生成一个ReadView

可重复读:只会在第一次执行查询语句时生成一个ReadView,之后的查询就不会重复生成了

(对于读未提交/串行化来说,读操作可以读到最新的数据,没必要使用MVCC)

假如现在student表中只有一条由事务id为8的事务插入的一条记录

+----+--------+--------+
| id | name   | class  |
+----+--------+--------+
|  1 | 张三   | 一班   |
+----+--------+--------+

现在有两个事务id分别为 1020 的事务在执行:

-----------------------------------------------------------------------

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id= 1 ;
UPDATE student SET name="王五" WHERE id= 1 ;

# Transaction 20
BEGIN;
UPDATE student SET name="钱七" WHERE id= 1 ;
UPDATE student SET name="宋八" WHERE id= 1 ;
...

-------------------------------------------------------------------------

那么现在有一个事务执行查询,就会生成一个readview,ReadView的 trx_ids列表的内容就是[10,20]up_limit_id10, low_limit_id21, creator_trx_id为0

(为什么是0,因为只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为 0

 

 从最新的undolog版本链开始读取,第一个宋八 trx_id是 20,属于 trx_ids列表,向下读取,知道读到trx_id不属于trx_ids列表为止,最终读到的就是张三

如果事务10提交,

对于读已提交的隔离级别来说,再次执行读操作,会生成新的readview,ReadView的trx_ids列表的内容就是[20],up_limitid为.20,low_limit_id为21, creator_trx_id0,读取版本链的信息,读到王五

而对于可重复读来说,不会生成新的readview,查询到的信息还是张三

接下来说一下MVCC如何解决幻读问题:

对于可重复读来说,多次查询并不会生成新readview,所以其他事务的插入操作,并不会对readview有任何影响

Q:索引如何优化

1 优化左外连接

对于驱动表来说,因为是左外连接,LEFT JOIN左边的表的数据无论是否满足条件都会保留,因此全表扫描也是不赖的。LEFT JOIN 条件用于确定如何从被驱动表搜索行,所以 被驱动表是我们的关键点,一定需要建立索引。右外连接也是如此

左外链接左表是驱动表右表是被驱动表,右外链接和此相反,内链接则是按照数据量的大小,数据量少的是驱动表,多的是被驱动表

2 优化内连接

对于内连接来说,在两个表的连接条件都存在索引的情况下,会选择小表作为驱动表。“小表驱动大表”

如果有一个表不存在索引一个表存在索引,就将存在索引的表作为被驱动表

3. 子查询优化

执行子查询时,MySQL 为内层查询语句的查询结果建立一个临时表,对查询性能有一定的影响

在 MySQL 中,可以使用连接(JOIN)查询来替代子查询。 连接查询 不需要建立临时表,其 速度比子查询要快,如果查询中使用索引的话,性能就会更好。

4. 排序优化

在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中 避免全表扫描,在 ORDER BY 子句 避免使用 FileSort 排序

尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列;如果不同就使用联合索引。

5. GROUP BY优化

Order by、group by、distinct 这些语句较为耗费 CPU,尽量减少使用

6. 优化分页查询

一般分页查询时,通过创建覆盖索引能够比较好地提高性能。一个常见有非常头疼的问题就是 limit 2000000,10,此时需要 MySQL 排序前 2000010 记录,仅仅返回 2000000-2000010 的记录,其他记录丢弃,查询排序的代价非常大。

EXPLAIN SELECT * FROM student LIMIT 2000000,10;

优化思路一

在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。

EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a
WHERE t.id = a.id;

优化思路二

该方案适用于主键自增的表,可以把 Limit 查询转换成某个位置的查询 。

EXPLAIN SELECT * FROM student WHERE id > 2000000 LIMIT 10;

Q:索引的设计原则

A:①业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引

②经常作为 WHERE 查询/更新条件的字段

③经常 GROUP BY 和 ORDER BY 的列

DISTINCT 字段需要创建索引
⑤使用字符串前缀创建索引(重要)
假设我们的字符串很长,那存储一个字符串就需要占用很大的存储空间。在我们需要为这个字符串列建立 前缀索引 
注:前缀索引中索引长度的设计原则是使用count(distinct left(列名, 索引长度))/count(*) 如果公式值达到90%,那么索引长度就可以使用
使用最频繁的列放到联合索引的左侧

1.MySQL 事务

#何谓事务?

回答什么是事务,就要知道事务需要解决什么问题。

当我们需要插入多条相关联的数据到数据库时,突然会遇到下面这些问题:

  • 数据库中途突然因为某些原因挂掉了。
  • 客户端突然因为网络原因连接不上数据库了。
  • 并发访问数据库时,多个线程同时写入数据库,覆盖了彼此的更改。
  • ......

上面的任何一个问题都可能会导致数据的不一致性。为了保证数据的一致性,系统必须能够处理这些问题。事务就是我们抽象出来简化这些问题的首选机制。事务的概念起源于数据库,目前,已经成为一个比较广泛的概念。

何为事务? 一言蔽之,事务是逻辑上的一组操作,要么都执行,要么都不执行。

事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账 1000 元,这个转账会涉及到两个关键操作,这两个操作必须都成功或者都失败。

  1. 将小明的余额减少 1000 元
  2. 将小红的余额增加 1000 元。

事务会把这两个操作就可以看成逻辑上的一个整体,这个整体包含的操作要么都成功,要么都要失败。这样就不会出现小明余额减少而小红的余额却并没有增加的情况。

具体操作

# 开启一个事务
START TRANSACTION;
# 多条 SQL 语句
SQL1,SQL2...
## 提交事务
COMMIT;

关系型数据库的四大特性(ACID)

  1. 原子性Atomicity) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性Consistency): 执行事务前后,数据从一个合法性的状态到另一个合法性的状态,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
  3. 隔离性Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性Durabilily): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。(持久性一定是数据写入磁盘中)

原子性是基础,隔离性是手段,一致性是约束条件,持久性是目的

并发事务带来了哪些问题?

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。

  • 脏读(Dirty read): 对于两个事务Session A、Session B,Session A读取了已经被Session B更新但还没有被提交的字段。之后若Session B回滚,SessionA读取的内容就是临时且无效的。示意图如下:

  • 脏写(Dirty write):对于两个事务Session A、Session B,如果事务SessionA修改了另一个未提交事务Session B修改过的数据,那就意味着发生了脏写

  • 不可重复读(Unrepeatable read): 对于两个事务Session A、Session B,Session A读取了一个字段,然后Session B更新/删除了该字段。之后Session A再次读取同一个字段,值就不同/没了。那就意味着发生了不可重复读。

  • 幻读(Phantom read): 幻读与不可重复读类似。对于两个事务Session A、Session B, Session A从一个表中读取了一个字段,然后Session B在该表中插入了一些新的行。之后,如果Session A再次读取同一个表,就会多出几行。那就意味着发生了幻读。

严重性排序:

 为了解决这些问题,sql定义了四种隔离级别:

  • READ-UNCOMMITTED(读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读) : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。mysql的默认隔离级别
  • SERIALIZABLE(可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
隔离级别脏读不可重复读幻读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE××

×

注:脏写是最严重的问题,所以四种隔离级别都解决了脏写的问题 

总结:

Q:什么是事务?

A:首先我们要知道事务需要解决什么问题,当我们插入多条数据到数据库时,可能会出现:

  • 数据库中途突然因为某些原因挂掉了。
  • 客户端突然因为网络原因连接不上数据库了。

这些问题都可能会导致数据的不一致性。为了保证数据的一致性,系统必须能够处理这些问题。事务就是我们抽象出来简化这些问题的首选机制。

所以,事务就是是逻辑上的一组操作,要么都执行,要么都不执行

Q:关系型数据库的四大特性(ACID)

A:

  1. 原子性Atomicity) : 事务是最小的执行单位,不允许分割。
  2. 一致性Consistency): 执行事务前后,数据从一个合法性的状态到另一个合法性的状态;
  3. 隔离性Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性Durabilily): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。(持久性一定是数据写入磁盘中)

Q:并发事务带来了哪些问题?

A:

脏读(Dirty read): 对于两个事务Session A、Session B,Session A读取了已经被Session B更新但还没有被提交的字段。之后若Session B回滚,SessionA读取的内容就是临时且无效的;

脏写(Dirty write):对于两个事务Session A、Session B,Session A修改了已经被Session B更新但还没有被提交的字段,那就意味着发生了脏写;

不可重复读(Unrepeatable read): 对于两个事务Session A、Session B,Session A读取了一个字段,然后Session B更新/删除了该字段。之后Session A再次读取同一个字段,值就不同/没了。那就意味着发生了不可重复读。

幻读(Phantom read): 幻读与不可重复读类似。对于两个事务Session A、Session B, Session A从一个表中读取了一个字段,然后Session B在该表中插入了一些新的行。之后,如果Session A再次读取同一个表,就会多出几行。那就意味着发生了幻读。

Q:数据库是如何解决的?

A: 为了解决这些问题,sql定义了四种隔离级别:

  • READ-UNCOMMITTED(读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读) : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读但幻读仍有可能发生mysql的默认隔离级别
  • SERIALIZABLE(可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读

脏写是最严重的问题,所以四种隔离级别都解决了脏写的问题 

MySQL事务日志

事务有4中特性:原子性、一致性、隔离性、持久性

事务的隔离性由锁机制实现。

而事务的原子性、一致性和持久性有事务的redo log和 undo log来保证

        redo log保证事务的持久性

        undo log保证事务的原子性、一致性

事务的隔离性由锁机制实现。

而事务的原子性、一致性和持久性有事务的redo log和 undo log来保证

        redo log保证事务的持久性

        undo log保证事务的原子性、一致性

首先为什么会有redo log?

我们先必须明白InnoDB存储引擎是以为单位来管理存储空间的。在真正访问页面之前,需要把在磁盘上的页缓存到内存中的Buffer Pool(缓冲池)之后才可以访问。所有的变更都必须先更新缓冲池中的数据然后缓冲池中的脏页会以一定的频率被刷入磁盘( checkPoint机制),通过缓冲池来优化CPu和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。

类似于JMM

 这样会出现的问题就是事务提交后,刚写完缓冲池还未刷入磁盘,数据库宕机,那么这段数据就丢失了,不能保证持久性

所以我们需要解决这个问题,怎么做呢?

一个简单的做法∶就是做到实时更新,缓冲池一旦更新,就刷入磁盘

问题:①修改量与刷新磁盘工作量严重不成比例。加入我们仅仅修改某个页面中的一个字节的数据,但是InnoDB是以页为单位进行磁盘IO的,显然是小题大做;

          ②加入我们需要将工资为5000的员工工资改为6000,但是有可能这些员工信息是存储在不同的不连续的页中,那么刷新到磁盘中时,需要很多随机IO,效率低

怎么做更好呢?那就是redo log

就是将缓冲池中修改的数据,先保存到文件中(也就是redo log,是真实存在的文件)。假如有更新的数据没来得及刷入到磁盘。数据库重启之后,就直接读取redo log,就能保证commit的数据的操作能体现在磁盘中,保证持久性

Redo log的优点

  • .redo日志降低了刷盘频率(相比如之前那个简单方法,刷入磁盘的频率不必再是一更新就刷)
  •    redo日志占用的空间非常小(存储表空间ID、页号、偏移量以及需要更新的值,所需的存储空间是很小的,刷盘快。)

Redo log的特点

  • redo日志是顺序写入磁盘的
  • 事务执行过程中,redo log不断记录,不是commit之后才记录

Redo log的执行过程

 针对步骤3的刷盘策略 170-Redo日志的刷盘策略与过程剖析_哔哩哔哩_bilibili

171-写入Redo Log Buffer和Redo Log File的写入策略_哔哩哔哩_bilibili

Undo log

 什么是Undo log?

每当我们要对一条记录做改动时(这里的改动可以指TINSERT、DELETE、UPDATE ),都需要把回滚时所需的东西记下来。MysQL把这些为了回滚而记录的这些内容称之为撤销日志或者回滚日志(即undo log )。注意,由于查询操作( SELECT )并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的undo日志。

redo log是事务持久性的保证,undo log是事务原子性的保证。

作用?

  • 回滚数据,注意undo是逻辑日志,是指将修改的逻辑取消,必须insert一条数据时,需要开辟一个新页,但是回滚的时候,只是将该数据删除,并不会把新增的页也删除。再比如说手机电池不行了,需要更换电池,更换之后,是好了,但是不是之前的电池了

Undo log的生命周期

 8 和 9 的顺序取决于你如何设置

Mysql如何处理事务的

持久性:redoLog保证,一定会先写入redoLog日志才算真正提交,之后一定机制刷数据到磁盘,如果出现意外redoLog将会起到作用;
原子性、一致性:undoLog日志实现,每个事物修改一条数据前会留下一个旧版本的数据副本在undoLog里,如果事物回滚就将undoLog里对应的数据覆盖回去;
隔离性:MySQL默认是RR级别,是mvcc去实现的,同时也保证了最大的并发性,每行有个tx_id,undoLog_指针,undoLog日志,
在每个事务进行事务读的时候会加锁,比如select * from t where id = 1 for update;会把id1锁住,
对于写写并发事务,第二个事务如果也要修改这个id1的数据就会阻塞,有行锁间隙锁表锁实现,
对于读写、写读事务mvcc机制则可以保证并发执行,比如另一个事务锁住的是id2他并不修改id1的数据但是它看到的id1的数据一定是稳定的(即其他事务已经提交了的),
对于事务1对id1在未提交前的任何操作事务2是看不见的,实现了可重复读(快照读),
事务2在第一次读取数据的时候会生成一个ReadView对象(如果是读已提交则每次都生成一个ReadView(当前读)),
里面有记录了当时的事务id列表,根据这个列表事务2可以知道哪些数据可见哪些不可见,如果事务1提交了,事务2是可以看到的因为事务2并不关心id1的数据变化,
同理对于事务1来说,事务2对于id2的数据修改只有事务2能看到

BinLog

 binlog是二进制日志文件,他记录所有的更新语句(包括DDL/DML),不包括select/show等

binlog文件有三种格式

  • Statement :每一条修改数据的sql都会记录在binlog中
  • Row :不记录sql语句,仅保存哪条记录被修改
  • Mixed :就是Statement与Row的结合

binlog主要应用场景:

  • 一是用于数据恢复,如果MySQL数据库意外停止,可以通过二进制日志文件来查看用户执行了哪些操作,对数据库服务器文件做了哪些修改,然后根据二进制日志文件中的记录来恢复数据库服务器。(undo log 是回滚用的,不要搞混了)

  • 二是用于数据复制,由于日志的延续性和时效性,master把它的二进制日志传递给slaves来达到master-slave数据一致的目的。

如何查看日志?

mysqlbinlog -v                 将行事件以伪SQL的形式表现出来

show binlog events        可以查看事件已经pos点信息

如何使用?

可以先使用show binlog events 找出需要修改的数据对应的起始pos点和终止pos点,然后使用mysqlbinlog命令进行操作

Binlog和redolog的区别

  • redo log 它是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎层产生的。

  • binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层

  • 虽然它们都属于持久化的保证,但是则重点不同。

    • redo log让InnoDB存储引擎拥有了崩溃恢复能力。

    • binlog保证了MySQL集群架构的数据一致性。

索引

索引是一种数据结构

简介,索引从何而来

①最初没几个数据,他们搞成链表

我们将一行数据,做为一个结点,按主键顺序,连成一个链表(record_type记录头信息的一项属性,表示记录的类型, 0 表示普通记录、 2 表示最小记 录、 3 表示最大记录、 1目录项)

一页的大小是16KB,即存储16KB的数据,为了方便,我们这里就假设一页只能存三个数据。

②数据突然多了,一个地方放不下了

当我们再存入数据(4,4,‘a’)时,发现第一页已经满了,那就开辟第二页,然后将新数据插到第二页,注意,新分配的数据页编号可能并不是连续的。它们只是通过维护着上一个页和下一个页的编号而建立了链表关系

 但我们发现,此时并不满足按主键的顺序排列,所以要调整顺序

 ③开辟的地方越来越多,查询也只能顺序查找,很慢,搞个目录

因为这些16KB的页在物理存储上是不连续的,所以如果想从这么多页中根据主键值快速定位某些记录所在的页,我们需要给它们做个目录,每个页对应一个目录项,每个目录项包括下边两个部分:

  • 页的用户记录中最小的主键值,我们用key来表示。
  • 页号,我们用page_no表示。

 ④索引的雏形已经出现,完善

 目前的问题就是

Ⅰ目录项以数组存储,空间上连续,因为lnnoDB是使用页来作为管理存储空间的基本单位,一页满了开辟下一个,还要连续,浪费空间。

Ⅱ 删除不方便,如果目录项2下的页28的三个数据删除了,那么目录项2也要删除,数组的删除很麻烦

解决方法:使用链表

从图中可以看出来,我们新分配了一个编号为30的页来专门存储目录项记录。这里再次强调 目录项记录 和普通的 用户记录 的不同点

  • 目录项记录 的 record_type 值是1,而 普通用户记录 的 record_type 值是0。
  • 目录项记录只有 主键值和页的编号 两个列,而普通的用户记录的列是用户自己定义的,可能包含 很 多列 ,另外还有InnoDB自己添加的隐藏列。
  • 了解:记录头信息里还有一个叫 min_rec_mask 的属性,只有在存储 目录项记录 的页中的主键值 最小的 目录项记录 的 min_rec_mask 值为 1 ,其他别的记录的 min_rec_mask 值都是 0 。

相同点:两者用的是一样的数据页,都会为主键值生成 Page Directory (页目录),从而在按照主键 值进行查找时可以使用 二分法 来加快查询速度。

现在以查找主键为 20 的记录为例,根据某个主键值去查找记录的步骤就可以大致拆分成下边两步:

1. 先到存储 目录项记录 的页,也就是页30中通过 二分法 快速定位到对应目录项,因为 12 < 20 < 209 ,所以定位到对应的记录所在的页就是页9。

2. 再到存储用户记录的页9中根据 二分法 快速定位到主键值为 20 的用户记录。

  ④索引的雏形基本形成,继续完善

我们发现目录项多了也成问题,继续搞目录项

 ⑤B+树

 不论是存放用户记录的数据页,还是存放目录项记录的数据页,我们都把它们存放到B+树这个数据结构中了,所以我们也称这些数据页为节点。从图中可以看出,我们的实际用户记录其实都存放在B+树的最底层的节点上,这些节点也被称为叶子节点,其余用来存放目录项的节点称为非叶子节点或者内节点,其中B+树最上边的那个节点也称为根节点。
一个B+树的节点其实可以分成好多层,规定最下边的那层,也就是存放我们用户记录的那层为第О层,之后依次往上加。之前我们做了一个非常极端的假设:存放用户记录的页最多存放3条记录,存放目录项记录的页最多存放4条记录。其实真实环境中一个页存放的记录数量是非常大的,假设所有存放用户记录的叶子节点代表的数据页可以存放100条用户记录,所有存放目录项记录的内节点代表的数据页可以存放1000条目录项记录,那么:

  • 如果B+树只有1层,也就是只有1个用于存放用户记录的节点,最多能存放 100 条记录。
  • 如果B+树有2层,最多能存放 1000×100=10,0000 条记录。
  • 如果B+树有3层,最多能存放 1000×1000×100=1,0000,0000 条记录。
  • 如果B+树有4层,最多能存放 1000×1000×1000×100=1000,0000,0000 条记录。相当多的记 录!!!

你的表里能存放 100000000000 条记录吗?所以一般情况下,我们 用到的B+树都不会超过4层 ,那我们 通过主键值去查找某条记录最多只需要做4个页面内的查找(查找3个目录项页和一个用户记录页),又 因为在每个页面内有所谓的 Page Directory (页目录),所以在页面内也可以通过 二分法 实现快速 定位记录。

聚簇索引和非聚簇索引

聚簇索引:所有的用户记录都存在了叶子节点,数据即索引,索引即数据

非聚簇索引:按非主键

总结

Mysql使用的是什么索引?为什么?

Q:Mysql使用的是什么索引?为什么?

A:从MysQL的角度讲,数据库数据都是存放在磁盘中的,读取数据库数据就是要去磁盘中读取(为什么不全部存放在内存中?查找都是索引操作,一般来说索引非常大,尤其是关系型数据库,当数据量比较大的时候,索引的大小有可能几个G甚至更多,为了减少索引在内存的占用,数据库索引是存储在外部磁盘上的。当我们利用索引查询的时候,不可能把整个索引全部加载到内存,只能逐一加载,那么MysQL衡量查询效率的标准就是磁盘l0次数),不得不考虑一个现实问题就是磁盘IO。如果我们能让索引的数据结构尽量减少硬盘的I/o操作,所消耗的时间也就越小。可以说,磁盘的 IO操作次数对索引的使用效率至关重要。

加速查找速度的数据结构有两种:树和hash表

首先Hash表的CRUD时间复杂度是O(1)

树,例如平衡二叉树,查询/插入/修改/删除的平均时间复杂度都是0(log2N);
那为什么不用hash呢?

原因1: Hash索引仅能满足(=) (<>)和IN查询。如果进行范围查询,哈希型的索引,时间复杂度会退化为o(n);而树型的“有序"特性,依然能够保持o(log2N)的高效率。
原因2:Hash 索引还有一个缺陷,数据的存储是没有顺序的,在ORDER BY的情况下,使用Hash索引还需要对数据重新排序
原因3:对于联合索引的情况,Hash值是将联合索引键合并起来计算的,无法对单独的一个键或者几个索引键进行查询。
原因4∶对于等值查询来说,通常Hash索引的效率更高,不过也存在一种情况,就是索引列的重复值如果很多,效率就会降低。这是因为遇到Hash冲突时,需要遍历桶中的行指针来进行比较,找到查询的关键字,非常耗时。比如年龄,性别

如果是MyIsam或者是InnoDB存储引擎,那么对应的底层的数据结构为B+树,如果是Memory存储引擎,那么对应的底层的数据结构为Hash表。

树,二叉排序树可能会退化成链表,就不存在二分查找,查询时间复杂度O(n);所以为了控制深度来看看平衡二叉树,能保证查询时间复杂度一定是O(log2N),但是为了保证平衡,在插入数据的时候必须要旋转,插入性能有所损失。但是一个结点最多只有两个子节点,数据库数据庞大会导致树的深度很大,磁盘 IO 的次数多,性能低;

那么我们变成三叉树,四叉树呢

那如果用红黑树呢?

  • 平衡二叉树的左右子树的高度差绝对值不超过1,但是红黑树在某些时刻可能会超过1,只要符合红黑树的五个条件即可。
  • 二叉树只要不平衡就会进行旋转,而红黑树不符合规则时,有些情况只用改变颜色不用旋转,就能达到平衡,修改性能较高

但是还是存在一个大问题,就是深度过大,磁盘IO效率低

如果把有序二叉树变成有序多叉树,就能降低树的高度,这个就是演变出来的B树的核心思想。

B+树和B-树有什么区别,

①区别就是B+树只有叶子结点存放数据,B-树非叶子结点也存放数据。这样就导致,遍历的时候B-树需要中序遍历,但是B+树直接顺序遍历

 ②最主要的就是B+树平均IO次数会比B-树少,B-树的非叶子结点存放数据,那么目录页的结点树就少,会导致B-树高瘦;B+树非叶子结点不存放数据,一页可以存更多结点,B+树就会矮胖,IO效率高

所以采用B+树的最根本的原因是由于二叉树的树太高,树太高则直接影响到磁盘IO的次数,影响数据查询的效率,采用B+树的数据结构,可以在某个数据节点里面尽可能多的存储数据,使树的高度尽量的变低,提高效率。

【数据结构】红黑树与平衡二叉树的区别以及原理详解(附图解)_雪花不落的博客-CSDN博客_红黑树与平衡二叉树

Q:聚簇索引和非聚簇索引

B-树和B+树区别

①B-树非叶子节点存放数据,B+树只有叶子结点存放数据,非叶子结点只存放key

②B-树的叶子结点在物理上没有关系,B+树的叶子结点是用指针连在一起的,形成链表结构

思考 

B+树的优缺点

优势

  • 类似于书籍的目录索引,提高数据检索的效率,降低数据库的IO成本。
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

劣势

  • 实际上索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引列也是要占用空间的。
  • 虽然索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行 INSERT、 UPDATE、 DELETE。因为更新表时,MSQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。

索引又分为聚簇索引和非聚簇索引

聚簇索引:所有的用户记录都存在了叶子节点,数据即索引,索引即数据 ,一般是按主键排序的

非聚簇索引:叶子结点只存放主健和一个非主键记录

在 mysql分别创建 以myisam 和 Innodb 作为存储引擎的数据表。
Innodb 创建表后生成的文件有:

  1. frm:创建表的语句
  2. idb:表里面的数据+索引文件

Myisam 创建表后生成的文件有:

  1. frm:创建表的语句
  2. MYD:表里面的数据文件(myisam data)
  3. MYI:表里面的索引文件(myisam index)

Myisam不支持事务原因索引与数据分开存储,两个文件无法做到一致性

 Innodb引擎使用 B+Tree 作为索引结构,叶子节点的data域存放的是 数据记录;

 MyISAM引擎使用 B+Tree 作为索引结构,叶子节点的data域存放的是 数据记录的地址 。并且因为索引与数据分开存储,叶子节点只存放主健和地址记录,所以一定是非聚簇索引,且一定会有回表操作;且MyISAM的回表操作速度快于 Innodb,MyISAM查到地址直接找到数据, Innodb非聚簇索引查完还需要到聚簇索引中查找

Q:什么是慢查询

A:指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10秒以上(不含10秒)的语句,认为是慢查询

Q:聚簇索引和非聚簇索引

A:在innoDB中每个索引都对应一颗B+树,而聚簇索引和非聚簇索引最大的区别在于叶子节点存储的数据不同,聚簇索引叶子节点存储的是行数据,因此通过聚簇索引可以直接找到真正的行数据,而非聚簇索引叶子节点存储的一个是非主键信息,一个是主键信息,利用非聚簇索引查询,还需要回表查询

Q:联合索引

A:我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引,比方说有一个表有三个字段(c1(主键),c2,c3),以 c2 和 c3 列的大小为排序规则建立的 B+ 树称为 联合索引 。那么这颗B+树的特点是

每条目录项记录都由c2、c3、页号这三个部分组成,各条记录先按照c2列的值进行排序,如果记录的c2列相同,则按照c3列的值进行排序。
B+树叶子节点处的用户记录由c2、c3主键c1列组成。

Q:索引失效的情况

①非最左匹配(最左匹配原则:最左优先,在检索数据时从联合索引的最左边开始匹配):假如我们以A+B+C的顺序建立联合索引,如果以A+C/A+B/A+B+C的顺序查询符合最左匹配,如果以B+C/C+B/C+A则不符合最左匹配

②如果索引列涉及计算、函数、类型转换(自动或手动)导致索引失效

③范围条件右边的列索引失效,如果以A+B+C顺序建立联合索引,SQL语句WHERE a=10 AND b>10 AND c<9 ,那么索引之后涉及到前两个字段,即b>10后面的索引全部失效

如何知道的,使用EXPLAIN,查看key_len

 ④如果索引列涉及不等于(!= 或者 <>),索引失效

⑤is null可以使用索引,is not null无法使用索引

⑥like 以通配符 % 开头索引失效

⑦ OR 前后存在非索引的列,索引失效,也就是说,OR 前后的两个条件中的列都是索引时,查询中才使用索引。.

Q:什么是MVCC

索引的创建和设计原则

联合索引,129-表中添加索引的三种方式_哔哩哔哩_bilibili 27分钟开始

性能分析工具的使用

《MySQL高级篇》七、性能分析工具的使用(慢查询日志 | EXPLAIN | SHOW PROFILING | 视图分析 )_爱编程的大李子的博客-CSDN博客

2. 查看系统性能参数

可以使用 SHOW STATUS 语句查询一些数据库服务器的性能参数和使用频率。。

其语法如下:

SHOW [GLOBAL][SESSION] STATUES LIKE '参数';

其中GLOBAL是全局的意思,SESSION是当前表的意思

 Connections:连接MySQL服务器的次数。
Uptime:MySQL服务器的上线时间。
Slow_queries:慢查询的次数。
Innodb_rows_read:Select查询返回的行数
Innodb_rows_inserted:执行INSERT操作插入的行数
Innodb_rows_updated:执行UPDATE操作更新的行数
Innodb_rows_deleted:执行DELETE操作删除的行数
Com_select:查询操作的次数。
Com_insert:插入操作的次数。对于批量插入的 INSERT 操作,只累加一次。
Com_update:更新操作的次数。
Com_delete:删除操作的次数。

慢查询

MySQL的慢查询日志,用来记录在MysQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10秒以上(不含10秒)的语句,认为是慢查询。

慢查询日志分析工具:Mysqldumpslow

首先要进入慢查询日志的目录 ,慢查询日志文件是以 -slow.log结尾的

image-20220812160221292

 然后输入指令

 Explain语句

定位了查询慢的SQL之后,我们就可以使用EXPLAIN或DESCRIBE,工具做针对性的分析查询语句
具体看看都有哪些步骤,标都读取顺序是什么,那些索引被使用和了,每张表多少行被优化器查询

注意:EXPLAIN 仅仅是查看执行计划,不会真实的执行 sql

 语法:EXPLAIN +SQL语句,如

EXPLAIN SELECT * FROM s1 INNER JOIN s2;

 就会出现一张表

下面详细解释一下每一列参数的含义,配合一下链接一起食用《MySQL高级篇》七、性能分析工具的使用(慢查询日志 | EXPLAIN | SHOW PROFILING | 视图分析 )_爱编程的大李子的博客-CSDN博客

table 

table 列代表着该表的表名 

就是这个SQL语句涉及到了哪些表 

  id

在查询语句中每出现一个SELECT关键字,MySQL就会为它分配一个唯一的id ,代表着一次查询。这个id 就是 EXPLAIN语句的第一列。

小结 

  1. id如果相同,可以认为是一组,从上往下顺序执行
  2. 在所有组中,id值越大,优先级越高,越先执行
  3. 关注点:id号每个号码,表示一趟独立的查询, 一个sql的查询趟数越少越好

select_type

select_type: SBLECT关键字对应的那个查询的类型,确定小查询在整个大查询中扮演了一个什么角色

 大概意思就是比如下面这个嵌套语句,第一个SELECT的角色就是外层查询,第二个SELECT就是内层查询

EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field = 'a');

 type

索引优化和查询优化

《MySQL高级篇》八、索引优化与查询优化_爱编程的大李子的博客-CSDN博客

索引失效的案例

全值匹配我最爱

全值匹配可以充分的利用组合索引~

系统中经常出现的sql语句如下,当没有建立索引时,possible_keyskey都为NULL

# SQL_NO_CACHE表示不使用查询缓存。
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';

此时执行SQL,数据查询速度会比较慢,耗时0.12s

mysql> SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';
Empty set, 1 warning (0.12 sec)

接下来我们建立索引

CREATE INDEX idx_age ON student(age);

CREATE INDEX idx_age_classid ON student(age,classId);

CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);

💌Q 上面三个索引有什么区别,为什么这么建立索引?
上面建立索引是与三条sql的使用场景对应的,遵守了全值匹配的规则,就是说建立几个复合索引字段,最好就用上几个字段。且按照顺序来用。

建立索引后执行,发现使用到了联合索引,且耗时较短 0.00s 

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';
+----+-------------+---------+------------+------+----------------------------------------------+----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys                                | key                  | key_len | ref               | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------------------------------------+----------------------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | idx_age,idx_age_classid,idx_age_classid_name | idx_age_classid_name | 73      | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+----------------------------------------------+----------------------+---------+-------------------+------+----------+-------+
1 row in set, 2 warnings (0.00 sec)

mysql> SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';
Empty set, 1 warning (0.00 sec)

**注意:**上面的索引可能不生效哦,在数据量较大的情况下,我们进行全值匹配SELECT *,优化器可能经过计算发现,我们使用索引查询所有的数据后,还需要对查找到的数据进行回表操作,性能还不如全表扫描。这里我们没有造这么多数据,所以就不演示效果咯。 

**注意:**下面的SQL也会使用索引idx_age_classid_name,虽然顺序不一样,看似不满足最左匹配原则,但是优化器会对其优化,改成索引对应的顺序

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';

最左匹配原则

在 MySQL 建立联合索引时会遵守最佳左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

已知三个索引

CREATE INDEX idx_age ON student(age);

CREATE INDEX idx_age_classid ON student(age,classId);

CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);

 1. 下面的SQL将使用索引idx_age         ——自认为这个例子能很好地说明最左匹配原则

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name='abcd';

 2. 下面的sql不会使用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classId=4 AND student.name='abcd';

如果要使其使用索引,需要添加如下索引

CREATE INDEX idx_age_classid_name ON student(classId,name);

 Q:那为什么不会使用索引idx_age_classid_name?结合思考五一起看

A:因为最左匹配原则,首先就没有age,就匹配不了

4. 思考:下面sql会不会使用索引呢? 

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE  student.age = 30 AND student.name='abcd';

 答案是会!因为优化器会执行优化的哦,会调整查询条件的顺序。不过在开发过程中我们还是要保持良好的开发习惯哟。

5. (重要)思考:删去索引idx_age_classididx_age,只保留idx_age_classid_name,执行如下sql,会不会使用索引? 

即只存在索引(如下)

CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);

 下面的SQL语句还会使用索引吗?

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE  student.age = 30 AND student.name='abcd';

 答案是会,但是只会用一部分。看看执行结果

image-20220824161042002

 使用了idx_age_classid_name,但是key_len是5,也就是说只使用了age部分的排序,因为age是int类型,4个字节加上null值列表一共5个字节哦。想想就知道,B+树是先按照age排序,再按照classid排序,最后按照name排序,因此不能跳过classId的排序直接就使用name的排序哦。

结论:MySQL 可以为多个字段创建索引,一个索引可以包括 16 个字段,对于多列字段,过滤条件要使用索引那必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法使用。如果查询条件中没有使用这些字段中的第一个字段时,多列索引不会被使用。

主键插入顺序(随便看看)

对于一个使用 InnoDB 存储引擎的表来说,在我们没有显式的创建索引时,表中的数据实际上都是存储在 聚簇索引 的叶子节点的。而记录又是存储在数据页中,数据页和记录又是按照 记录主键值从小到大 的顺序进行排序,所以如果我们 插入 的记录的 主键是依次增大 的话,那我们每插满一个数据页就换到下一个数据页继续插,而如果我们插入的 主键值忽大忽小 的话,就比较麻烦了,假设某个数据页存储的记录已经满了,它存储的主键值在 1~100 之间:

image-20220824161409652

如果此时再插入一条主键值为 9 的记录,那它插入的位置就如下图:

image-20220824161420173

可这个数据页已经满了,再插进来咋办呢?我们需要把当前 页面分裂 成两个页面,把本页中的一些记录移动到新创建的这个页中。页面分裂和记录移位意味着什么?意味着:性能损耗!所以如果我们想尽量避免这样无谓的性能损耗,最好让插入的记录的 主键值依次递增 ,这样就不会发生这样的性能损耗了。 所以我们建议:让主键具有 AUTO_INCREMENT ,让存储引擎自己为表生成主键,而不是我们手动插入,比如person_info表:

image-20220824161706495

我们自定义的主键列 id 拥有 AUTO_INCREMENT 属性,在插入记录时存储引擎会自动为我们填入自增的主键值。这样的主键占用空间小,顺序写入,减少页分裂。

计算、函数、类型转换(自动或手动)导致索引失效

详细见《MySQL高级篇》八、索引优化与查询优化_爱编程的大李子的博客-CSDN博客

举例1:当使用函数时

CREATE INDEX idx_name ON student(NAME); 
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc'; 

第二个SQL失效

 举例2:当条件有计算时

CREATE INDEX idx_sno ON student(stuno);

 直接失效:

EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001; 

这是因为我们需要把索引字段的值都取出来,然后一次进行表达式的计算来进行条件判断,因此采用的就是全表扫描 的方式,运行时间也会慢很多。 

去掉上面SQL的计算,索引优化生效:

EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno = 900000; 

 范围条件右边的列索引失效

CREATE INDEX idx_age_classId_name ON student(age,classId,NAME);

1. 如果系统经常出现的sql如下,那么索引 idx_age_classId_name 这个索引还能正常使用么?

EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE student.age=30 AND student.classId>20 AND student.name = 'abc' ;

 能使用,但是不正常了

image-20220824174623086

 有同学会好奇,我改变下WHERE后面字段的顺序呢,是否可以正常使用呢?

EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE student.age=30 AND student.name ='abc' AND student.classId>20;

 答案也是不能~,因为 优化器会自动帮你满足最左前缀原则 ,即优化器会 先根据联合索引进行排序 ,联合索引的顺序才能决定~

解释一下为什么范围查询会导致索引失效:
因为根据范围查找筛选后的数据,无法保证范围查找后面的字段是有序的。

例如:a_b_c这个索引,你根据b范围查找>2的,在满足b>2的情况下,如b:3,4,c可能是5,3、因为c无序,那么c的索引便失效了

不等于(!= 或者 <>)索引失效 

CREATE INDEX idx_name ON student(NAME);

下面SQL全失效

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc' ;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name != 'abc' ;

 is null可以使用索引,is not null无法使用索引

  • S NULL:可以触发索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NULL;

image-20220824182818489

  • IS NOT NULL:无法触发索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NOT NULL;

image-20220824182851982

结论:最好在设计数据库的时候就将 字段设置为 NOT NULL 约束。比如可以将 INT 类型的字段,默认设置为 0。将字符串的默认值设置为空字符串(“”)。

扩展:同理,在查询中使用 not like 也无法使用索引,导致全表扫描

like 以通配符 % 开头索引失效

在使用 LIKE 关键字进行查询的查询语句中,如果匹配字符串的第一个字符为“%”,索引就不会其作用。只有“%”不在第一个位置,索引才会起作用。

  • 使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE 'ab%'; 

image-20220824183323044

  • 未使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE '%ab%';

image-20220824183400615

拓展:Alibaba《Java 开发手册》

【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

OR 前后存在非索引的列,索引失效

在WHERE 子句中,如果在 OR 前的条件列进行了索引,而在 OR 后的条件列没有进行索引,那么索引会失效。也就是说,OR 前后的两个条件中的列都是索引时,查询中才使用索引。

因为 OR 的含义就是两个只要满足一个即可,因此 只有一个条件列进行了索引是没有意义的,只要有条件列没有进行索引,就会进行全表扫描,因此索引的条件列也会失效。

查询语句使用 OR 关键字的情况:

# 创建索引(只有OR前面的字段有索引)
CREATE INDEX idx_age ON student(age);
# 未使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;

image-20220824183640699

# 再为OR后面的字段创建一个索引
CREATE INDEX idx_cid ON student(classid);
# 使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;

image-20220824184608531

解释下 OR 前后存在非索引的列,索引失效

因为 OR前后一个使用索引,一个进行全表扫描,还没有直接进行全表扫描更快~

索引优化 

1 优化左外连接

对于驱动表来说,因为是左外连接,LEFT JOIN左边的表的数据无论是否满足条件都会保留,因此全表扫描也是不赖的。LEFT JOIN 条件用于确定如何从被驱动表搜索行,所以 被驱动表是我们的关键点,一定需要建立索引。右外连接也是如此

左外链接左表是驱动表右表是被驱动表,右外链接和此相反,内链接则是按照数据量的大小,数据量少的是驱动表,多的是被驱动表

2 优化内连接

对于内连接来说,在两个表的连接条件都存在索引的情况下,会选择小表作为驱动表。“小表驱动大表”

如果有一个表不存在索引一个表存在索引,就将存在索引的表作为被驱动表

3. 子查询优化

执行子查询时,MySQL 为内层查询语句的查询结果建立一个临时表,对查询性能有一定的影响

在 MySQL 中,可以使用连接(JOIN)查询来替代子查询。 连接查询 不需要建立临时表,其 速度比子查询要快,如果查询中使用索引的话,性能就会更好。

4. 排序优化

在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中 避免全表扫描,在 ORDER BY 子句 避免使用 FileSort 排序

尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列;如果不同就使用联合索引。

5. GROUP BY优化

Order by、group by、distinct 这些语句较为耗费 CPU,尽量减少使用

6. 优化分页查询

一般分页查询时,通过创建覆盖索引能够比较好地提高性能。一个常见有非常头疼的问题就是 limit 2000000,10,此时需要 MySQL 排序前 2000010 记录,仅仅返回 2000000-2000010 的记录,其他记录丢弃,查询排序的代价非常大。

EXPLAIN SELECT * FROM student LIMIT 2000000,10;

优化思路一

在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。

EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a
WHERE t.id = a.id;

优化思路二

该方案适用于主键自增的表,可以把 Limit 查询转换成某个位置的查询 。

EXPLAIN SELECT * FROM student WHERE id > 2000000 LIMIT 10;

  1. 从数据操作的类型(读、写)分
    • 读锁共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
    • 写锁排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
  2. 从对数据操作的颗粒度
    • 表锁(偏读)
    • 行锁(偏写)

读锁-表锁

  • 添加锁
lock table 表名1 read(write), 表名2 read(write), ...;
  • 释放表锁
unlock tables;

  1. 当前 session 和其他 session 均可以读取加了加读锁的表
  2. 当前 session 不能读取其他没有加读锁的表,并且不能修改加了读锁的表
  3. 其他 session 想要修改加了读锁的表必须等待其读锁释放

写锁-表锁

  • 添加锁
lock table 表名1 read(write), 表名2 read(write), ...;
  • 释放表锁
unlock tables;
  1. 当前 session 可以读取和修改加了写锁的表
  2. 当前 session 不能读取其他表
  3. 其他 session 想要读取加了写锁的表,必须等待其读锁释放

总结:

MyISAM表进行操作,会有以下情况:

  1. 对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
  2. 对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作
  3. 简而言之,就是读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞。

行锁

行锁的特点

  1. 偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高
  2. InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁

如何手动行锁

  • select xxx ... for update 锁定某一行后,其它的操作会被阻塞,直到锁定行的会话提交

MVCC -多版本并发控制

什么是隐藏字段

184-MVCC三剑客:隐藏字段、UndoLog版本链、ReadView规则_哔哩哔哩_bilibili

3分钟左右

什么是MVCC?

MVCC (Multiversion Concurrency Control),多版本并发控制

什么是当前读?

当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据),读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。加锁的 SELECT,或者对数据进行增删改都会进行当前读。比如:

什么是快照读?

快照读又叫一致性读,读取的是快照数据。 **不加锁的简单的 SELECT 都属于快照读** ,即不加锁的非阻塞读

详细讲一个MVCC

MVCC 的实现依赖于: 隐藏字段、Undo Log版本链、Read View** 。

聚簇索引记录中都包含两个必 要的隐藏列(字段)

- trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id隐藏列。
- roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到`undo日志`中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

举例讲一下这两种字段

 

 

 MVCC实现原理之ReadView

什么是ReadView

ReadView就是事务在使用MVCC机制进行快照读操作时产生的读视图 。在MVCC机制中,多个事务对同一个行记录进行更新会产生多个历史快照,这些历史快照保存在Undo Log里。如果一个事务想要查询这个行记录,需要读取哪个版本的行记录呢?这时就需要用到ReadView了,它帮我们解决了行的可见性问题。——总结:是不是就是为了能查到已提交的最新数据,而且能看到哪些事务正在对数据进行修改,但没有提交

注意:MVCC是用于使用`READ COMMITTED`和`REPEATABLE READ`隔离级别的事务;

为什么呢?使用`READ UNCOMMITTED`隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了。

使用SERIALIZABLE隔离级别的事务,InnoDB规定使用加锁的方式来访问记录。串行化已经是能读到最新的数据了

使用`READ COMMITTED`和`REPEATABLE READ`隔离级别的事务,都必须保证读到已经提交了的事务修改过的记录。假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问题就是需要判断一下版本链中的哪个版本是当前事务可见的,这是ReadView要解决的主要问题。

这个ReadView中主要包含 4 个比较重要的内容,分别如下:

1. `creator_trx_id`,创建这个 Read View 的事务 ID。(一个事务对应一个Read View )

   > 说明:只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为 0 。

2. `trx_ids`,表示在生成ReadView时当前系统中活跃的读写事务的`事务id列表`。

3. `up_limit_id`,活跃的事务中最小的事务 ID。

4. `low_limit_id`,表示生成ReadView时系统中应该分配给下一个事务的`id`值。`low_limit_id` 是系统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。

注意:low_limit_id并不是trx_ids中的最大值,事务id是递增分配的。比如,现在有id为 1 ,2 , 3 这三个事务,之后id为 3 的事务提交了。那么一个新的读事务在生成ReadView时,trx_ids就包括 1 和 2 ,up_limit_id的值就是 1 ,low_limit_id的值就是 4 。

ReadView的规则

有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见。

  • 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。

  • 如果被访问版本的trx_id属性值小于ReadView中的up_limit_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。

  • 如果被访问版本的trx_id属性值大于或等于ReadView中的low_limit_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。

  • 如果被访问版本的trx_id属性值在ReadView的up_limit_id和low_limit_id之间,那就需要判断一下trx_id属性值是不是在trx_ids列表中。

  • 如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。

  • 如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

### MVCC整体操作流程

了解了这些概念之后,我们来看下当查询一条记录的时候,系统如何通过MVCC找到它:

1. 首先获取事务自己的版本号,也就是事务 ID;

2. 生成 ReadView;
3. 查询得到的数据,然后与 ReadView 中的事务版本号进行比较;
4. 如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照;
5. 最后返回符合规则的数据。

  通用查询日志

通用查询日志用来`记录用户的所有操作`,包括启动和关闭MySQL服务、所有用户的连接开始时间和截止时间、发给 MySQL 数据库服务器的所有 SQL 指令等。当我们的数据发生异常时, **查看通用查询日志,还原操作时的具体场景** ,可以帮助我们准确定位问题。

BIN log

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
java面经-百度准入职老哥整理.pdf》是一份关于百度准入职面试的Java面经整理。这份面经是由百度准入职的老哥整理而成,其中记录了一些面试时可能遇到的问题以及解答方法。 这份面经对于准备参加百度准入职面试的人来说非常有价值。首先,它列出了一些常见的面试问题,涵盖了Java语言的各个方面,包括基础知识、数据结构与算法、设计模式、多线程、网络编程等等。通过仔细研究和复习这些问题的答案,可以帮助面试者全面了解Java语言的特性和应用。 其次,这份面经还提供了问题的解答思路和方法,帮助面试者理清思路,正确回答问题。这对于很多面试者来说特别有帮助,因为在面试时有时遇到一些棘手的问题,有了这份面经的指导,面试者可以更好地掌握应对策略。 不过需要注意的是,面经作为一份参考资料,不能完全依赖于它来准备面试。面试官可能问一些不在面经中列出的问题,因此考生还是需要自己对Java语言有充分的了解,并能够熟练运用。同时,面试官还关注考生的沟通能力、解决问题的能力以及对新技术的学习和掌握能力。 总体来说,《java面经-百度准入职老哥整理.pdf》是一份非常宝贵的资料,可以帮助面试者对Java面试中可能遇到的问题有更深入的了解,提供了解答思路和方法。但记住,面试准备还需要多方面的知识积累和实践经验的积累,才能在面试中展现自己的优势。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值