Bootstrap

【MySQL】MySQL之八股必考(面试必问!)

目录

当前读和快照读

当前读(Current Read):

快照读(Snapshot Read):

通俗讲!

MVCC是什么?

MVCC的工作原理

MVCC的工作原理由四个东西保证

MySQL有哪些索引

聚簇索引和非聚簇索引

Mysql如何慢SQL进行优化

MySQL中进行数据操作的时候为什么要尽量用内连接而不是外连接

MySQL中整个查询的过程

SQL的执行计划包含哪些字段

MySQL中索引失效的情况!!

MySQL有哪些锁

基于粒度:

基于属性:

Mysql内连接、左连接、右连接的区别

where和having的区别?

三大范式

InnoDB 什么情况下会产生死锁

MySQL 删除自增 id,随后重启 MySQL 服务,再插入数据,自增 id 会从几开始?

 MySQL插入百万级的数据如何优化?


当前读和快照读

当前读(Current Read):

  • 当前读是指在读取数据时获取最新的数据值,并且读取的数据结果会受到其他并发事务所做修改的影响。
  • 当前读使用的是一致性读(Consistent Read)方式,即保证读取的数据是一个一致的时间点上的数据。
  • 当前读可以保证读取到最新的数据,但也可能读取到未提交的数据或其他事务已经提交但未生效的数据。
  • 当前读常用的场景是在需要读取最新数据的情况下,例如查询操作
  • 当前读读取的是数据的最新版本,并且当前读返回的记录都会上锁,保证其他事务不会并发修改这条记录。如update、insert、delete、select for update(排他锁)、select lockin share mode(共享锁) 都是当前读

快照读(Snapshot Read):

  • 快照读是指在读取数据时获取事务开始时的数据快照,并且读取的数据结果不会受到其他并发事务所做修改的影响
  • 快照读使用的是一致性非锁定读(Consistent Non-locking Read)方式,即读取的数据是基于某个时间点的一致性数据快照,不会加锁。
  • 快照读可以保证读取过程不会被其他事务的并发修改所干扰,但不能获得最新的已提交数据。
  • 快照读常用的场景是在需要读取数据的过程中,不希望对数据加锁或受到其他并发事务干扰的情况。
  • 快照读读取的是当前数据的可见版本,可能是会过期数据,不加锁的select就是快照读

通俗讲!

  1. 当前读:假设你正在网上购物,在确认购买前,你想要查看最新的商品信息和价格。你会刷新页面以获取最新的数据,这就是当前读。它可以确保你看到的数据是最新的,但也可能会受到其他人修改数据的影响。所以,如果你在下单之前等待太久,商品信息可能会发生改变。

  2. 快照读:现在假设你已经决定要购买某个商品,并放入购物车中。此时,你不再关心商品信息是否发生了变化,而是看中了你添加到购物车时的那个价格。尽管在你确认订单之前价格可能已经发生了变化,但你仍然希望看到的是添加到购物车时的价格,这就是快照读。它可以确保你查看的数据不会被其他人的修改所干扰。

MVCC是什么?

        MVCC指的是多版本并发控制。它用来保证多个事务在同时读取和修改数据库时的一致性和隔离性——通过在数据库中保存多个版本的数据实现

        简单来说——MVCC允许多个事务同时读取数据库中的数据,并且每个事务都可以看到自己开始执行时的那个版本(数据快照)。当事务做出修改操作时,MVCC会创建一个新的数据版本,而不是直接修改原始数据。这样做的好处是,其他事务可以继续读取原始数据版本,而不会受到正在修改的事务的影响。

MVCC的工作原理

  1. 每个数据行都有一个唯一的时间戳,表示该数据行的版本
  2. 事务开始时,会获取一个事务ID和一个读取的时间戳
  3. 事务在读取数据时,只能看到哪些事务开始之前已经存在的数据版本,不会看到其他人正在修改的事务所创建的版本
  4. 事务在修改数据时,会创建一个新的数据版本,将该版本和事务的时间戳相关联,并将其写入数据库
  5. 其他事务可以继续读取旧版本的数据,而不受新版本的影响

MVCC的工作原理由四个东西保证

undo log日志:记录了数据历史版本,可以方便回滚。

readview:事务进行快照读是动态产生的视图,决定了返回数据的哪一个版本

隐藏字段DB_TRX_ID:记录最近修改的事务ID

隐藏字段DB_ROLL_PTR:回滚指针,配合undo log指向数据的上一个版本

MySQL有哪些索引

主键索引:一张表只能有一个主键索引,主键索引列不能有控制和重复值

唯一索引:唯一索引不能有重复值,但允许为空

普通索引:允许出现重复值

组合索引:对多个字段建立一个联合索引,遵循最左匹配原则(例如,如果有一个组合索引 (A, B, C),那么当查询条件包含 A,或者同时包含 A 和 B,或者同时包含 A、B 和 C 时,数据库都可以利用该索引进行高效查询。但如果只包含 B 或者只包含 C,那么组合索引将无法有效利用。)

聚簇索引和非聚簇索引

        聚簇索引:聚簇索引的叶子节点存放的是主键和数据行;辅助索引(在聚簇索引上创建的其他索引)的叶子节点存放的是主键值或指向数据行的指针

        优点:根据索引可以直接获取值,所以它获取数据更快;对于主键的排序查找和范围查找效率更高

        缺点:如果主键值很大的话,辅助索引也会变得很大;如果用uuid作为主键,数据存储会很稀疏;修改主键或乱序插入会让数据行移动导致页分裂;所以一般我们定义主键时尽量让主键值小,并且定义为自增和不可修改。

        非聚簇索引(辅助索引):叶子节点存放的是数据行地址,先根据索引找到数据地址,再根据地址去找数据

他们都是B+树结构

         上图为聚簇索引的呈现形式,在叶子节点可以直接返回我们想要找到的数据

         上图是非聚簇索引的呈现形式,在非聚簇索引的叶子节点上存储的并不是真正的行数据,而是主键ID,所以当我们使用非聚簇索引进行查询的时候,首先得到的结果是一个ID,然后拿着ID去主键的聚簇索引上查询真正的行数据,这个过程也叫回表

Mysql如何慢SQL进行优化

常见的慢查询优化有:

(1)尽量减少select的数据列,尽量使用覆盖索引(相当于精简版的索引,包括了需要查询的列)

(3)groupby查询时,同样要用索引,避免使用到临时表

(4)分页查询时,如果limit 后面的数字太大,可以使用子查询查出主键,再limit主键后n条数据就能走覆盖索引

(5) 使用复杂查询时,使用关联查询来代替子查询,并且最好使用内连接

(6)使用count函数时直接使用count的话count(*)的效率最高

count(*)或count(唯一索引)或count(数字):表中总记录数,count(字段)不会统计null

 (7) 在写update语句时,where条件要使用索引,否则会锁会从行锁升级为表锁

(8)表中数据是否太大,是不是要分库分表
 

MySQL中进行数据操作的时候为什么要尽量用内连接而不是外连接

        用外连接的话连接顺序是固定的,例如left join,他必须先对左表进行全表扫描,然后再去跟右表当中做匹配。

        而使用内连接的话,MySQL会自己根据查询优化器去判断用哪个表做驱动。

        子查询的话同样会对驱动表做全表扫描,所以尽量用小表做驱动表

MySQL中整个查询的过程

(1)首先客户端向服务端去发送一条查询请求

(2)服务器会先检查缓存,如果名字缓存,则返回存储在缓存中的结果。否则进入下一阶段

(3)服务器进行SQL解析、预处理、再由优化器生成对应的执行计划

(4)MySQL执行计划,调用存储引擎的API来执行查询

(5)将结果返回给客户端,通俗缓存查询结果

注意:只有在8.0之前才有查询缓存,8.0之后查询缓存被去掉了

SQL的执行计划包含哪些字段

        我们想看一个sql的执行计划使用的语句是explain+SQL(查看SQL详细信息)

表中的字段包括:

  • type:扫描类型,效率从底到高为ALL(全表扫描)>index(全索引扫描,我们的需要的数据在索引中可以获取)>range(使用索引进行范围查找)>ref(使用非唯一索引列进行了关联查询)> eq_ref (使用唯一索引进行关联查询)>const(使用唯一索引查询一行数据)>system(表中只有一行数据)
  •  extra(额外的):mysql如何查询额外信息,常见的有:
  • filesort:在排序缓冲区中进行排序,需要回表查询数据
  • index:表示使用覆盖索引
  • index scan:排序时使用了索引排序,但如果是按照降序排序的话就会使用反向扫描索引
  • temporary:查询时要建立一个临时表存放数据
  • rows:找到了多少行数据
  • key:实际使用到的索引
  • id:select查询的优先级,id越大优先级越高,子查询的id一般会更大
  • select_type:查询的类型,是普通查询还是联合查询还是子查询,常见类型有simple(不包含子查询),primary(标记复杂查询中最外层的查询),union(标记primart只后子查询)
  • table:者一行的数据是数哪张表的
  • possible_keys(可能的):当前查询语句可能用到的索引,可能为null(如果用了索引但是为null有可能是表数据太少innodb认为全表扫描更快)
  • ref(编号):显示索引的哪一行被使用了

MySQL中索引失效的情况!!

(1)where条件中有or,除非所有查询条件都建立了索引,否则失效

SELECT * FROM products WHERE name = 'A' OR category = 'X';

(2)like查询用%开头,索引失效

SELECT * FROM customers WHERE name LIKE '%B';

(3)索引列参与计算,索引失效

SELECT * FROM orders WHERE total_amount * 1.1 > 100;

(4)索引字段发生类型转换,索引失效(age定义的数据类型是整型,传入的为字符串类型,此时先进行隐式类型的转换,将'25'转为整数型,但这样就会导致索引失效降低效率

SELECT * FROM employees WHERE age = '25';

(5)MySQL判断为全表扫描更快时(数据少),会导致索引失效

(6)使用联合索引的时候,违反了最左前缀原则,如果建立了联合索引(多个列组成的索引),那么在查询条件中必须遵循最左前缀原则,即从左到右依次使用联合索引中的列,不能跳过任何一列。否则,也会导致索引失效。例如,如果建立了(name, age, gender)的联合索引,那么在查询条件中可以使用name,或者name和age,或者name,age和gender,但是不能只使用age或gender,也不能只使用age和gender。解决方法是尽量按照最左前缀原则来使用联合索引,并且将区分度高的列放在前面。
 

MySQL有哪些锁

基于粒度:

  1. 表级锁:对整张表加锁,粒度大并发小
  2. 行级锁:对行加锁,粒度小并发大
  3. 间隙锁:锁住表的一个区间,间隙锁之间不会发生冲突,只在可重复读下才生效,避免了幻读

间隙锁——假设有一个名为"products"的表,包含以下列:id(主键)、name、price。

事务A执行如下语句:

BEGIN;
SELECT * FROM products WHERE price BETWEEN 10 AND 20;

此时事务A会对价格在10到20之间的记录进行查询,并且会对该范围的间隙加上间隙锁。

在事务A执行期间,事务B尝试插入一个新的价格为15的产品:

sql
BEGIN;
INSERT INTO products (name, price) VALUES ('New Product', 15);

        由于事务A已经对价格在10到20之间的范围加上了间隙锁,所以事务B的插入操作会被阻塞。事务B必须等待事务A完成或回滚后才能继续执行。

        如果事务A提交了或回滚了,事务B再次执行插入操作时,它会发现被阻塞的间隙锁已经释放,就可以成功地插入新的产品记录。

        通过使用间隙锁,事务A可以确保在查询期间其他事务无法插入新的符合条件的记录,从而避免了幻读问题。

基于属性:

共享锁:又称读锁,一个事务为表加了读锁,其他事务只能加读锁不能加写锁

排他锁:又称写锁,一个事务加了写锁之后,其他事务就不能加任何锁,避免脏读问题。

Mysql内连接、左连接、右连接的区别

内连接取量表交集部分,左连接取左表全部右表匹配部分,右连接取右表全部坐表匹配部分

where和having的区别?

where是约束声明,having是过滤声明,where早于having执行,并且where不可以使用聚合函数,having可以

三大范式

第一范式:每个列都不可以再拆分。

第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。

第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。

InnoDB 什么情况下会产生死锁

        事务1已经获取数据A的写锁,想要去获取数据B的写锁,然后事务2获取了B的写锁,想要去获取A的写锁,相互等待形成死锁。
mysql解决死锁的机制有两个:

1.等待, 直到超时

2.发起死锁检测,主动回滚一条事务
        死锁检测的原理是构建一个以事务为顶点、 锁为边的有向图, 判断有向图是否存在环, 存在即有死锁。
        平时尽量减少事务操作的资源和隔离级别

MySQL 删除自增 id,随后重启 MySQL 服务,再插入数据,自增 id 会从几开始?

innodb 引擎:
        MySQL8.0前,下次自增会取表中最大 id + 1。原理是最大id会记录在内存中,重启之后会重新读取表中最大的id
        MySQL8.0后,仍从删除数据 id 后算起。原理是它将最大id记录在redolog里了

myisam:
        自增的 id 都从删除数据 id 后算起。原理是它将最大id记录到数据文件里了

MySQL 删除自增 id,随后重启 MySQL 服务,再插入数据,自增 id 会从几开始?_mysql自增删除了最后一个新增从哪个开始_血莲丹的博客-CSDN博客

 MySQL插入百万级的数据如何优化?

(1)一次SQL插入多条数据,可以减少写redolog和binlog的io次数(SQL长度是有限制的,但可以调整)

(2)保证数据按照索引进行有序插入

(3)可以分表后多线程插入

;