索引 —— 本质上是一个 B+ 树
1. MySQL InnoDB索引为什么用B+树而不用二叉树
B+树的每个节点可以表示的信息更多,因此整个树更加“矮胖”,这在从磁盘中查找数据(先读取到内存、后查找)的过程中,可以减少磁盘 IO 的次数,从而提升查找速度。
2. 什么是回表?为什么要回表?回表有什么缺点?怎么避免回表?
回表是指:InnoDB 在普通索引 a 上查到主键 id 的值后,再根据一个个主键 id 的值到主键索引上去查整行数据的过程。但凡你的查询结果不能从索引本身拿到数据,就需要回表
聚簇索引的叶子节点里面存放的是数据
非聚簇索引存放的就是主键 ID
3. 如果ORBER BY的列不是索引,会发生什么?
把所有的结果拿出来,然后排序,比较慢
尽量在ORBER BY的列建立索引,或者尽量使用到已有索引
4. 为什么要用自增组件?不自增怎么办?
主键如果不是自增,就会从树的中间插入,于是引起树节点分裂。树节点分裂可以看做是数据页分裂,一分裂,就会有磁盘IO把原来的数据挪到别的位置。
自增主键会导致相邻的数据在磁盘上总是相邻的,能充分利用顺序IO。
5. 为什么没有命中索引的时候会使用表锁
行锁是要用到索引的,如果没命中索引,会退化为表锁
索引 —— 特点
1. 索引的最左匹配原则是什么?
类似路由树的最长最左匹配原则
最左前缀匹配原则是指,MySQL会按照联合索引创建的顺序,从左至右开始匹配,直到遇到了范围查询 (<, <=, >, >=, between, like),再后面的查询条件就不会使用索引了。 可以理解为,在索引执行的多重循环中,但凡一层循环不是等值查询,那么内部的循环就不会再用到。
索引下推是例外,比如说 A = 123 AND B > 11 AND C like '%abc'
2. 什么是索引下推
以组合索引(name, age)为例。如果现在有一个需求:检索出表中“名字第一个字是张,而且年龄是 10 岁的所有男孩”。那么,SQL 语句是这么写的:
mysql> select * from tuser where name like '张%' and age=10 and ismale=1;
根据前缀索引规则,所以这个语句在搜索索引树的时候,只能用 “张”,找到第一个满足条件的记录 ID3。
在 MySQL 5.6 之前,只能从 ID3 开始一个个回表。到主键索引上找出数据行,再对比字段值。而 MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
3. 你可以用简单的话概括InnoDB查询时是怎么利用索引的吗?
数据库使用索引类似多重for循环
4. 非聚簇索引是不是一定会回表?
不一定,如果索引中已经有被查询的字段则不会回表
索引 —— 各种类型
1. 索引的类型有哪些?
所谓的各种类型,是指站在不同角度进行分类。比如说一个索引可以是联合索引,同时也是非聚簇索引。
• 聚簇索引和非聚簇索引:核心是叶子节点放的是数据本身,还是只是放了一个主键
• B + 树索引和哈希索引:innodb 引擎本身不支持哈希索引
• 联合索引(组合索引)和非联合索引:使用了多个列的就是联合索引
• 唯一索引和非唯一索引:
• 前缀索引:使用了列的一部分的索引。比如说在 varchar(128) 的字段上,值利用前32个字符创建索 引;
• 全文索引
• 覆盖索引:其实是指你查询的列,都是某个索引的列。覆盖索引最大的好处就是不用回表;
索引 —— 使用索引的各种异常
1. 为什么明明创建了索引,但是查询就是没用?
1)sql语句没写好
2)条件写的很宽松,如果InnoDB发现这个条件基本上是属于全表扫描,就不会用到索引
例如:select * from xxx where a >= 0
3)对索引使用左或者左右模糊匹配,例如:like “%xx” 或者 like “%xx%”
4)使用函数:select * from t_user where length(name)=6;
5)隐式类型转换:select * from t_user where phone = 1300000001;
6)进行表达式计算:explain select * from t_user where id + 1 = 10;
7)联合索引非最左匹配:联合索引 (a, b, c) ,where b=2;where c=3;where b=2 and c=3;
8)where语句中的OR:在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效
2. 为什么用了索引,查询还是很慢?
1)索引不在内存里边,索引比较大(100G),内存16G,查询索引都是走磁盘IO,用了等于没用
2)用了索引,但在等待锁
3)用了索引,数据量很大
3. 索引是不是越多越好?索引的列是不是越多越好?
索引的维护是有成本的,开销大
1)插入/更新都要调整这些树
2)占用空间大
4. 使用索引有什么缺点?
额外的维护成本
内存CPU开销
插入更新的影响
5. 查询索引本身会引起磁盘 IO 吗?
当然会
6. NULL 对索引会有什么影响?
索引区分度低,基本导致索引失效
索引 —— 设计和优化索引
1. 怎么设计和优化索引
选择合适的列创建索引,可以遵循这些原则:
• 在 WHERE 条件里面经常出现的。比如说外键;
• 经常在 JOIN 关联条件里面出现的。可以忽略这一条,因为一般我们都不建议使用 JOIN 语句;
• 使用有很多不同值的列:所以类似于 Status 这种枚举的效果就不是很好;
• 不要使用很长的列:比如说 BLOB 这种,或者很长的 varchar。一定要用的话,创建前缀索引;
创建联合索引,确定索引的顺序:
• 选择性高的在前面;
• 经常用作范围查询(也就是会中断索引使用的)放在后面;
MVCC —— 多版本控制协议
1. 什么是MVCC?
多版本控制协议,它主要依赖于每一行都有的2个隐藏列:trx_id、roll_ptr,在事务执行的时候,每一个事务对数据的修改都会生成一个新的记录,新的记录的roll_ptr会和之前的数据连在一起组成一个回滚段,当你在不同的隔离级别之下,会使用不同的策略去生成Read view,在Read Committed的级别下,每一次去读数据会新生成一次Read view,所以如果多次读之间有新的事务提交,会导致生成的Read view 不一样,所以读出来的数据不一样,Read Repeatable ,事务开始时创建一个Read view,后边多次读都是使用同一个Read view,所以始终读到的都是同一条数据
2. 事务的隔离级别?
• 未提交读
• 已提交读
• 可重复读(默认):通过多版本并发控制(MVCC) + 间隙锁(Next-Key Locking) 防止幻读
• 序列化
• 未提交读,永远都是读最新的数据;
• 已提交读和可重复读被 MVCC 控制,通过 Read View 来实现
• 序列化直接咔嚓一把锁,加锁成功之后读最新的(必然是已经提交的,也必然是可重复读的)
3. 什么是脏读、幻读?
- 脏读:读取未提交数据。A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。
- 幻读:前后多次读取,数据总量不一致。事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。
-
不可重复读:前后多次读取,数据内容不一致。事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。
4. 为什么事务提交了,数据还是丢了?
https://cloud.tencent.com/developer/article/1975521
锁
1. Innodb 引擎的 RR(可重复读) 级别解决了幻读吗?
解决了(官方说法),利用了临键锁
2. 各种锁的概念、含义和使用
MySQL 的锁有点像索引,可以从不同的角度来进行分类:
• 共享锁与排它锁:共享锁就是可以多个人拿着,类似读锁;排他锁类似写锁;
• 意向锁:意向共享锁和意向排它锁。简单理解就是在加锁前对其它事务打个招呼,说我需要什么锁;
• 记录锁、间隙锁和临界锁
• 行锁和表锁
• 记录锁:锁住一行,所以叫做记录锁,也是行锁;
• 间隙锁:锁住记录之间的间隔,或者索引之前的范围,或者所以索引的范围。只在重复读级别产生,(可以在前面隔离级别的地方提)
• 临键锁(Next key lock):记录锁和间隙锁的组合,即锁住记录,又锁住了间隙
其他
1. explain命令 各字段
https://juejin.cn/post/6850418120998256654
2. SQL 幂等案例
对于请求的幂等处理,如果请求本身就是幂等的,比如请求只是查询数据,没有任何的状态修改,或者是像更新头像这样简单的重置操作,那么我们可以什么都不用做。这里我们要注意一个情况,假设有一个请求是为用户的余额增加 5 元,如果采用下面的 SQL 进行处理,我们都知道它不是幂等的:UPDATE table SET balance = balance + 5 WHERE UID = 用户 ID ;但是,如果我们将上面的 SQL 改写为下面的三个操作,你可以思考一下,这个时候我们的请求是否为幂等的呢?在数据库中查询用户的余额:SELECT balance FROM table WHERE UID = 用户 ID ;在内存中计算用户的余额:balance = balance + 5 ,假设计算结果为 10 。更新用户的余额到数据库:UPDATE table SET balance = 10 WHERE UID = 用户 ID ;
3. 什么是快照读和当前读
当前读的规则,就是要能读到所有已经提交的记录的最新值,普通的select语句和insert、delete、update语句都是快照读,而select * from xxx for update/ for share 是当前读
4. 什么是关系型数据库
关系型数据库,是指采用了关系模型来组织数据的数据库,其以行和列的形式存储数据,以便于用户理解,关系型数据库这一系列的行和列被称为表,一组表组成了数据库。用户通过查询来检索数据库中的数据,而查询是一个用于限定数据库中某些区域的执行代码。关系模型可以简单理解为二维表格模型,而一个关系型数据库就是由二维表及其之间的关系组成的一个数据组织
关系型数据库是信息的集合,它以预定义的关系组织数据,数据存储在一个或多个由列和行构成的表(或“关系”)中,用户可以轻松查看和理解不同数据结构之间的关系。关系是不同表之间的逻辑连接,根据这些表之间的交互建立。
5. 为什么不推荐使用 join
当表中的数据量增加时,JOIN 语句的查询速度可能会下降。这是因为 JOIN 语句需要执行大量的数据匹配操作,以找到相关的行。当表中的数据量非常大时,这些匹配操作可能会非常耗时,从而导致查询速度下降。
https://cloud.tencent.com/developer/article/1810434
MySQL高并发/高可用
1. MySQL读写分离的用途
MySQL读写分离是指将读操作和写操作分别分配到不同的MySQL实例(主库和从库)上进行处理的技术。在典型的数据操作中,写操作比读操作更频繁且更复杂。因此,将读操作和写操作分别分配到不同的MySQL实例上,可以提高整个数据库系统的性能和可扩展性,减轻主库的负担,降低主从同步的延迟,减少单点故障风险。
MySQL读写分离适用于高并发访问的网站,数据库读写压力非常大的情况下。使用读写分离可以提高数据库并发处理能力,并减轻主数据库的负载,从而提升网站访问速度和性能。
https://open.oceanbase.com/blog/5489417984
2. MySQL主从同步延迟怎么办
解决主从延迟主要有以下方案:
1、配合 semi-sync 半同步复制;
2、一主多从,分摊从库压力;
3、强制走主库方案(强一致性);
4、sleep 方案:主库更新后,读从库之前先 sleep 一下;
5、判断主备无延迟方案(例如判断 seconds_behind_master 参数是否已经等于 0、对比位点);
6、并行复制 — 解决从库复制延迟的问题
https://cloud.tencent.com/developer/article/2240328
https://developer.aliyun.com/article/894940
3. MySQL和MongoDB适用场景的区别
https://blog.csdn.net/abckingaa/article/details/134392590