MySQL
# MySQL
# 存储引擎
# mysql 存储引擎有哪几种?
tag:
万得、恒生、字节、邮政、百度、淘天、小米、传音、美团、大华、淘天、腾讯、三一重工、探探、亚信、顺丰、软通count:29
as:MySQL 的存储引擎的区别
MySQL 的数据库引擎是什么?
- Innodb:现在的 mysql 默认存储引擎就是 innodb,主要就是因为它是唯一一个支持事务的存储引擎,支持表级锁和行级锁,其索引的底层结构使用的是 B + 树,在数据,索引,表结构都存储到.idb 中。
- Myisam:其不支持事务,仅支持表级锁,其索引的底层结构为 B + 树,表结构存储到.sdi 中,索引存储到.myi,数据存储到.myd 中。表使用全文索引,适合于读取密集的应用程序。
- Memory:基础内存进行存储的,主要就是用 sdi 存储表结构。
# InnoDB 和 MyISAM 存储引擎区别
tag:
知乎、好未来、瑞幸、小米、百度、数字马力、传音、美团、亚信、快手、字节、顺丰、软通、4399、tp-link、亚信、shopeecount:26
as:mysql 中 MyIsam 相对于 innodb 更适合那些场景
是否支持行级锁:MyISAM 只有表级锁 (table-level locking),而 InnoDB 支持行级锁 (row-level locking) 和表级锁,默认为行级锁。也就说,MyISAM 一锁就是锁住了整张表,这在并发写的情况下是性能不太乐观。
是否支持事务:
- MyISAM 不提供事务支持。
- InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别,具有提交 (commit) 和回滚 (rollback) 事务的能力。并且,InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的(基于 MVCC 和 Next-Key Lock)。
是否支持外键:MyISAM 不支持,而 InnoDB 支持。
是否支持数据库异常崩溃后的安全恢复:MyISAM 不支持,而 InnoDB 支持。
是否支持 MVCC:MyISAM 不支持,而 InnoDB 支持。
索引实现不一样:MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。
InnoDB 引擎中,其数据文件本身就是索引文件。相比 MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录。
性能有差别:InnoDB 的性能比 MyISAM 更强大,不管是在读写混合模式下还是只读模式下,随着 CPU 核数的增加,InnoDB 的读写能力呈线性增长。MyISAM 因为读写不能并发,它的处理能力跟核数没关系。
数据缓存策略和机制实现不同:InnoDB 使用缓冲池(Buffer Pool)缓存数据页和索引页,MyISAM 使用键缓存(Key Cache)仅缓存索引页而不缓存数据页。

# InnoDB
索引分为聚簇索引与二级索引
- 聚簇索引:主键值作为索引数据,叶子节点还包含了所有字段数据,索引和数据是存储在一起的
- 二级索引:除主键外的其它字段建立的索引称为二级索引。被索引的字段值作为索引数据,叶子节点还包含了主键值
支持事务
- 通过 undo log 支持事务回滚、当前读(多版本查询)
- 通过 redo log 实现持久性
- 通过两阶段提交实现一致性
- 通过当前读、锁实现隔离性
支持行锁、间隙锁
支持外键
# MyISAM
索引只有一种
- 被索引字段值作为索引数据,叶子节点还包含了该记录数据页地址,数据和索引是分开存储的
不支持事务,没有 undo log 和 redo log
仅支持表锁
不支持外键
会保存表的总行数
# innodb 引擎和 hash 引擎有什么区别
tag:
用友count:1
as:
# 数据库的三个范式
tag:
美团、美的、数字马力、招行、字节、竞技世界count:12
as:数据库三范式,生产环境一般使用那种
设计表的时候不遵循三大范式会出现什么问题,反范式
数据库范式的设计,作用
- 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。
- 第二范式(2NF):首先满足 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
- 第三范式(3NF):首先是满足 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况
# 1NF (第一范式)
属性(对应于表中的字段)不能再被分割,也就是这个字段只能是一个值,不能再分为多个其他的字段了。1NF 是所有关系型数据库的最基本要求 ,也就是说关系型数据库中创建的表一定满足第一范式。
# 2NF (第二范式)
2NF 在 1NF 的基础之上,消除了非主属性对于码的部分函数依赖。如下图所示,展示了第一范式到第二范式的过渡。第二范式在第一范式的基础上增加了一个列,这个列称为主键,非主属性都依赖于主键。
第二范式
一些重要的概念:
- 函数依赖(functional dependency):若在一张表中,在属性(或属性组)X 的值确定的情况下,必定能确定属性 Y 的值,那么就可以说 Y 函数依赖于 X,写作 X → Y。
- 部分函数依赖(partial functional dependency):如果 X→Y,并且存在 X 的一个真子集 X0,使得 X0→Y,则称 Y 对 X 部分函数依赖。比如学生基本信息表 R 中(学号,身份证号,姓名)当然学号属性取值是唯一的,在 R 关系中,(学号,身份证号)->(姓名),(学号)->(姓名),(身份证号)->(姓名);所以姓名部分函数依赖于(学号,身份证号);
- 完全函数依赖 (Full functional dependency):在一个关系中,若某个非主属性数据项依赖于全部关键字称之为完全函数依赖。比如学生基本信息表 R(学号,班级,姓名)假设不同的班级学号有相同的,班级内学号不能相同,在 R 关系中,(学号,班级)->(姓名),但是(学号)->(姓名) 不成立,(班级)->(姓名) 不成立,所以姓名完全函数依赖与(学号,班级);
- 传递函数依赖:在关系模式 R (U) 中,设 X,Y,Z 是 U 的不同的属性子集,如果 X 确定 Y、Y 确定 Z,且有 X 不包含 Y,Y 不确定 X,(X∪Y)∩Z = 空集合,则称 Z 传递函数依赖 (transitive functional dependency) 于 X。传递函数依赖会导致数据冗余和异常。传递函数依赖的 Y 和 Z 子集往往同属于某一个事物,因此可将其合并放到一个表中。比如在关系 R (学号,姓名,系名,系主任) 中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖。
# 3NF (第三范式)
3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。符合 3NF 要求的数据库设计,基本上解决了数据冗余过大,插入异常,修改异常,删除异常的问题。比如在关系 R (学号,姓名,系名,系主任) 中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖,所以该表的设计,不符合 3NF 的要求。
# Statement 和 Prepared Statement 的区别?哪个性能更好?
与 Statement 相比
- PreparedStatement 接口代表预编译的语句,它主要的优势在于可以减少 SQL 的编译错误并增加 SQL 的安全性(减少 SQL 注射攻击的可能性)
- PreparedStatement 中的 SQL 语句是可以带参数的,避免了用字符串连接拼接 SQL 语句的麻烦和不安全
- 当批量处理 SQL 或频繁执行相同的查询时,PreparedStatement 有明显的性能上的优势,由于数据库可以将编译优化后的 SQL 语句缓存起来,下次执行相同结构的语句时就会很快
# 索引
tag:
美团、Meta App、京东、小药药、浩鲸、小米、来未来、超聚变、快手、税友、淘天、哔哩哔哩、淘天、招行、捷运达、百度、亚信、阿里、哈啰、软通、青书count:21
# mysql 数据库的索引类型
tag:
携程、哈啰、用友、数字马力、字节、shopee、快手、哔哩哔哩、途虎养车、腾讯、大智慧、满帮、携程、饿了么、货拉拉、青书、泓齐网科、美团、京东count:35
as:主键索引和唯一索引的区别是
hash 索引
索引数据结构:二叉树、红黑树、hash 表、B-tree
MyISASM 和 InnoDB 类型的表默认创建的都是 BTREE 索引
按照数据结构维度划分:
- BTree 索引:MySQL 里默认和最常用的索引类型。B+Tree 只有叶子节点存储 value,非叶子节点只有指针和 key。存储引擎 MyISAM 和 InnoDB 实现 BTree 索引都是使用 B+Tree,但二者实现方式不一样。
- 哈希索引:类似键值对的形式,一次即可定位。
- RTree 索引:一般不会使用,仅支持 geometry 数据类型,优势在于范围查找,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
- 全文索引:对文本的内容进行分词,进行搜索。目前只有
CHAR、VARCHAR,TEXT列上可以创建全文索引。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
按照底层存储方式角度划分:
- 聚簇索引(聚集索引):索引结构和数据一起存放的索引,InnoDB 中的主键索引就属于聚簇索引。
- 非聚簇索引(非聚集索引):索引结构和数据分开存放的索引,二级索引 (辅助索引,除主键外的其它字段建立的索引称为二级索引) 就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。
按照应用维度划分:
- 主键索引:加速查询 + 列值唯一(不可以有 NULL)+ 表中只有一个。
- 普通索引:仅加速查询。
- 唯一索引:加速查询 + 列值唯一(可以有 NULL)。
- 覆盖索引:一个索引包含(或者说覆盖)所有需要查询的字段的值。
- 联合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并。
- 全文索引:对文本的内容进行分词,进行搜索。目前只有
CHAR、VARCHAR,TEXT列上可以创建全文索引。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
MySQL 8.x 中实现的索引新特性:
- 隐藏索引:也称为不可见索引,不会被优化器使用,但是仍然需要维护,通常会软删除和灰度发布的场景中使用。主键不能设置为隐藏(包括显式设置或隐式设置)。
- 降序索引:之前的版本就支持通过 desc 来指定索引为降序,但实际上创建的仍然是常规的升序索引。直到 MySQL 8.x 版本才开始真正支持降序索引。另外,在 MySQL 8.x 版本中,不再对 GROUP BY 语句进行隐式排序。
- 函数索引:从 MySQL 8.0.13 版本开始支持在索引中使用函数或者表达式的值,也就是在索引中可以包含函数或者表达式。
# InnoDB 索引实现(聚集)
tag:
美团、百度count:2
- 数据文件本身就是索引文件
- 表数据文件本身就是按 B + 树组织的一个索引结构文件
- 聚集索引的叶子节点包含了完整的数据记录
- 表必须有主键,且推荐使用整型的自增主键
- 普通索引结构叶子节点存储的是主键值
InnoDB 主键索引查找流程:通过.ibd 文件找到对应的索引,索引的 value 即为那行对应的完整数据
# 主键索引有什么特点?innodb 和 myisam 的区别?
tag:
美团、百度count:2
myisam 索引树结构
# 为什么 myisam 不被弃用?
tag:
美团count:1
# 聚集索引和非聚集索引的区别?
tag:
字节、知乎、浩鲸、小米、来未来、超聚变、快手、哔哩哔哩、亚信、Fabrie、数字马力、携程、4399、喜马拉雅、百度、讯飞count:35
as:如果一个列即使单列索引,又是联合索引,单独查它的话先走哪个?
聚簇索引的原理?
聚集索引:表中那行数据的索引和数据都合并在一起了。
聚簇索引(Clustered Index)即索引结构和数据一起存放的索引,并不是一种单独的索引类型。InnoDB 中的主键索引就属于聚簇索引。
在 MySQL 中,InnoDB 引擎的表的
.ibd文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引 (B + 树) 的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。非聚集索引:表中那行数据的索引和数据是分开存储的。
非聚簇索引 (Non-Clustered Index) 即索引结构和数据分开存放的索引,并不是一种单独的索引类型。二级索引 (辅助索引,除主键外的其它字段建立的索引称为二级索引) 就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。
非聚簇索引的叶子节点并不一定存放数据的指针,因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
聚簇索引的优缺点
优点:
- 查询速度非常快:聚簇索引的查询速度非常的快,因为整个 B + 树本身就是一颗多叉平衡树,叶子节点也都是有序的,定位到索引的节点,就相当于定位到了数据。相比于非聚簇索引, 聚簇索引少了一次读取数据的 IO 操作。
- 对排序查找和范围查找优化:聚簇索引对于主键的排序查找和范围查找速度非常快。
缺点:
- 依赖于有序的数据:因为 B + 树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或 UUID 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
- 更新代价大:如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚簇索引的叶子节点还存放着数据,修改代价肯定是较大的,所以对于主键索引来说,主键一般都是不可被修改的。
非聚簇索引的优缺点
优点:
更新代价比聚簇索引要小 。非聚簇索引的更新代价就没有聚簇索引那么大了,非聚簇索引的叶子节点是不存放数据的。
缺点:
- 依赖于有序的数据:跟聚簇索引一样,非聚簇索引也依赖于有序的数据
- 可能会二次查询 (回表): 这应该是非聚簇索引最大的缺点了。 当查到索引对应的指针或主键后,可能还需要根据指针或主键再到数据文件或表中查询。

# 回表
tag:
腾讯、人人网、浩鲸、来未来、哔哩哔哩、传音、美团、七牛云、百度、tp-link、字节count:14
as:如何避免回表
什么情况下要回表查询
通过二级索引查到的主键再去聚簇索引中查询数据行的过程就是回表。而直接查询聚簇索引则不会出现回表的情况。
- 回表查询:先到普通索引上定位主键值,再到聚集索引上定位行记录,它的性能较扫一遍索引树低(一般情况下)。
在 InnoDB 存储引擎中,非主键索引的叶子节点包含的是主键的值。这意味着,当使用非主键索引进行查询时,数据库会先找到对应的主键值,然后再通过主键索引来定位和检索完整的行数据。这个过程被称为 “回表”。
详细说明:
一般我们自己建的索引不管是单列索引还是联合索引,都称为普通索引,相对应的另外一种就是聚簇索引。 每个普通索引就对应着一颗独立的索引 B + 树,索引 B+ 树的节点仅仅包含了索引里的几个字段的值以及主键值。
根据索引树按照条件找到了需要的数据,仅仅是索引里的几个字段的值和主键值,如果用 select * 则还需要很多其他的字段,就得走一个回表操作,根据主键再到主键的聚簇索引里去找,聚簇索引的叶子节点是数据页,找到数据页里才能把一行数据的所有字段值提取出来。
假设 select * from table order by a,b,c 的语句,(table 有 abcdef 6 个字段),首先得从联合索引的索引树里按照顺序 a、b、c 取出来所有数据,接着对每一条数据都根据主键到聚簇索引的查找,其实性能不高。
有时候 MySQL 引擎会觉得用了既用了联合索引和聚簇索引来查找指定的字段,太慢了,那不不如直接全表扫描得了,只用聚集索引就行。
聚簇(聚集)索引补充:
只有一个聚集索引,聚簇索引的叶子节点存储行记录。根据聚簇索引的 key 查找是非常快的。
- 如果表定义了主键,则主键就是聚集索引;
- 如果表没有定义 PK,则第一个 not NULL unique 列是聚集索引;
- 否则,InnoDB 会创建一个隐藏的 row-id 作为聚集索引。
# 一个 SQL 执行的很慢,我们要分两种情况讨论
- 偶尔很慢,则有如下原因
- 数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘。
- 执行的时候,遇到锁,如表锁、行锁。
- 这条 SQL 语句一直执行的很慢,则有如下原因
- 没有用上索引:例如该字段没有索引;由于对字段进行运算、函数操作导致无法用索引。
- 数据库选错了索引。
# 建立索引的优势、负面影响和原则
tag:
美团、小米、用友、携程、蔚来、4399、京东、快手、明朝万达、数字马力、中通、招行、喜马拉雅、得物、字节、tp-link、阿里、货拉拉、浩鲸、moka、百度、邦盛科技、卓望数码、顺丰、蔚来count:41
索引的优势:
- 检索速度:快速访问数据表中的特定信息,提高检索速度。
- 唯一性:创建唯一性索引,保证数据库表中每一行数据的唯一性。
- 加速连接:加速表和表之间的连接。
- 减少分组和排序的时间:使用分组和排序进行数据检索时,可以显著减少查询中分组和排序的时间。
索引的负面影响:
- 耗时:创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加。
- 占空间:索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间。
- 维护速度:当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度。
数据表建立索引的原则有哪些?
- 选择性高的列:选择那些选择性高的列(即不同值的比例较高的列)建立索引,这样可以减少索引的大小,提高查询效率。
- 频繁出现在 WHERE 子句中的列:经常用于过滤条件的列应该建立索引,因为这样可以更快地筛选出符合条件的记录。
- 排序和分组列:经常用于 ORDER BY 和 GROUP BY 的列也应该建立索引,因为这样可以提高排序和分组的速度。
- 复合索引的列顺序:如果需要建立复合索引(即多个列的组合索引),应该根据列的选择性和查询条件的使用频率来决定列的顺序。
什么情况下不适合建立索引?
- 对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。
- 避免在经常更新的列上建立索引:频繁更新的列不适合建立索引,因为每次更新都需要重建索引,这会影响性能。
- 对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)。
# 选择合适的字段创建索引
- 不为 NULL 的字段:索引字段的数据应该尽量不为 NULL,因为对于数据为 NULL 的字段,数据库较难优化。如果字段频繁被查询,但又避免不了为 NULL,建议使用 0,1,true,false 这样语义较为清晰的短值或短字符作为替代。
- 被频繁查询的字段:我们创建索引的字段应该是查询操作非常频繁的字段。
- 被作为条件查询的字段:被作为 WHERE 条件查询的字段,应该被考虑建立索引。
- 频繁需要排序的字段:索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。
- 被经常频繁用于连接的字段:经常用于连接的字段可能是一些外键列,对于外键列并不一定要建立外键,只是说该列涉及到表与表的关系。对于频繁被连接查询的字段,可以考虑建立索引,提高多表连接查询的效率。
# 被频繁更新的字段应该慎重建立索引
虽然索引能带来查询上的效率,但是维护索引的成本也是不小的。 如果一个字段不被经常查询,反而被经常修改,那么就更不应该在这种字段上建立索引了。
# 限制每张表上的索引数量
索引并不是越多越好,** 建议单张表索引不超过 5 个!** 索引可以提高效率同样可以降低效率。
索引可以增加查询效率,但同样也会降低插入和更新的效率,甚至有些情况下会降低查询效率。
因为 MySQL 优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划,如果同时有很多个索引都可以用于查询,就会增加 MySQL 优化器生成执行计划的时间,同样会降低查询性能。
# 尽可能的考虑建立联合索引而不是单列索引
因为索引是需要占用磁盘空间的,可以简单理解为每个索引都对应着一颗 B + 树。如果一个表的字段过多,索引过多,那么当这个表的数据达到一个体量后,索引占用的空间也是很多的,且修改索引时,耗费的时间也是较多的。如果是联合索引,多个字段在一个索引上,那么将会节约很大磁盘空间,且修改数据的操作效率也会提升。
# 注意避免冗余索引
冗余索引指的是索引的功能相同,能够命中索引 (a, b) 就肯定能命中索引 (a) ,那么索引 (a) 就是冗余索引。如(name,city )和(name )这两个索引就是冗余索引,能够命中前者的查询肯定是能够命中后者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
# 字符串类型的字段使用前缀索引代替普通索引
前缀索引仅限于字符串类型,较普通索引会占用更小的空间,所以可以考虑使用前缀索引带替普通索引。
# 使用索引的推荐原则有哪些?
tag:
美团、用友、青书、字节、京东count:6
as:MySQl 索引失效的场景有哪些?
SELECT *不会直接导致索引失效(如果不走索引大概率是因为 where 查询范围过大导致的),但它可能会带来一些其他的性能问题比如造成网络传输和数据处理的浪费、无法使用索引覆盖;- 避免在索引列上进行计算、函数、类型转换等操作;
- 最适合索引的列是出现在 WHERE 子句中的列,或连接子句中指定的列,而不是出现在 SELECT 关键字后的选择列表中的列。
- 使用唯一索引。唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
- 不要过度索引。因为每个索引都要占用额外的磁盘空间,并降低写操作的性能,增加维护成本。在修改表的内容时,索引必须进行更新,有时也可能需要重构,因此,索引越多,维护索引所花的时间也就越长。
- 为经常需要排序、分组和联合操作的字段建立索引。
- 删除不再使用或者很少使用的索引。 MySQL 5.7 可以通过查询 sys 库的
schema_unused_indexes视图来查询哪些索引从未被使用。 - 利用最左原则。mysql 建立多列索引(联合索引)有最左前缀的原则 即最左优先,如: 如果有一个 2 列的索引 (col1,col2), 则已经对 (col1)、(col1,col2) 上建立了索引; 如果有一个 3 列索引 (col1,col2,col3),则已经对 (col1)、(col1,col2)、(col1,col2,col3) 上建立了索引。
- 使用短索引。
- 避免以 % 开头的 LIKE 查询比如
LIKE '%abc';; - 避免查询条件中使用 OR,且 OR 的前后条件中有一个列没有索引,涉及的索引都不会被使用到;
- 避免发生隐式转换;
- 避免 IN 的取值范围较大时会导致索引失效,走全表扫描 (NOT IN 和 IN 的失效场景相同);
# 覆盖索引
tag:
浩鲸、小米、来未来、淘天、百度count:5
as:
概念: 如果一个索引包含(或者说覆盖了)所有满足查询所需要的数据,那么就称这类索引为覆盖索引(Covering Index)。在 MySQL 中,可以通过使用 explain 命令输出的 Extra 列来判断是否使用了索引覆盖查询。若使用了索引覆盖查询,则 Extra 列包含 Using index 字符串。
覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。
大白话解释: select 的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。
使用 id 主键进行查询就是覆盖索引查询,因为聚簇索引的数据中包含 id 主键,性能高。
在做查询的时候如果返回列吧全部存在于索引中就会回表查询,所以尽量避免使用 select * 。
优点:
- 覆盖索引能有效地提高查询性能,因为覆盖索引只需要读取索引而不需要再回表读取数据。MySQL 查询优化器在执行查询前会判断是否有一个索引能执行覆盖查询。
- 索引项通常比记录要小,所以 MySQL 会访问更少的数据。
如主键索引,如果一条 SQL 需要查询主键,那么正好根据主键索引就可以查到主键。再如普通索引,如果一条 SQL 需要查询 name,name 字段正好有索引, 那么直接根据这个索引就可以查到数据,也无需回表。

补充: 不是所有类型的索引都可以成为覆盖索引。覆盖索引必须要存储索引的列,而哈希索引、空间索引和全文索引等都不存储索引列的值,所以 MySQL 只能使用 B-Tree 索引做覆盖索引。
# 联合索引的各种匹配规则
tag:
美团、万得、用友、携程、阿里、字节、哈啰、腾讯、小米、来未来、小鹅通、快手、税友、信也、腾讯、富途、亚信、Fabrie、明朝万达、数字马力、众安、猫眼、小红书、大智慧、TCL、4399、讯飞、货拉拉、淘米、万得、贝壳、卓望数码、顺丰count:70
as:什么情况索引会失效?
% abc 和 abc% 进行模糊查询那个可以利用索引
where a=1 or b=1 走索引吗
where a=1 or b=1 or ... e=1 走索引吗
为什么避免使用 select *
索引 (a, b),where a = 1 和 where b = 1,效果是一样的吗
多个查询条件的书写的先后顺序对效率有影响吗
联合索引 a, b, c 使用 a = 1 and c = 2 使用索引吗?
(a,b,c) 组合索引,(a,c) 能命中索引吗?(b,c) 能命中索引吗?
一个表在 A 列和 B 列分别建立单独的索引,然后查询条件是 where A=1 and B = 2, 那么会走到哪个索引呢
联合索引(a,b,c)中,a 是等值,b 是范围,c 是等值,它能不能用到联合索引
索引失效场景 为什么会失效 从索引的结构的层面讲
a,b,c,where b=4,where b=4 and a=5 会走索引吗?
使用表中的多个字段创建索引,就是 联合索引,也叫 组合索引 或 复合索引。
以 score 和 name 两个字段建立联合索引:
ALTER TABLE `cus_order` ADD INDEX id_score_name(score, name);
- 等值匹配
- 最左侧列匹配
- 最左前缀匹配原则
- 范围查找规则
- 等值匹配 + 范围匹配的规则
比如建立了 (a、b、c) 联合索引,那么有以下几种情况可以用到索引:

注意:8.0.13 可以使用跳跃索引,bc 和 c 可以使用到索引。
创建以下复合索引(name, status, address)

- 在使用复合索引的时候不遵循最左前缀法则。在做条件查询的时候跳跃某一列字段导致索引失效。

- 在条件查询中的范围查询的右遍的列不能使用索引,使用也会失效。(如果三个都有效的话 key_len 应该为六百多,说明此时 address 字段失效)

- 不能在索引列进行运算操作,这会导致索引失效。

在条件查询的时候如果没有加单引号也会导致索引失效。(就比如:0 和 '0',会进行隐式类型转换,导致索引失效)
在模糊查询的时候,如果字符串中是以 **% 开头的就会导致索引失效 **。(就比如: "% abc")
# 最左匹配原则
tag:
用友、百度、来未来、快手、税友、中通、众安、微派、青书、moka、饿了么count:18
as:在对关键字进行模糊查询的时候应该注意什么?
select * from db where a=1, b>0 如果建立 a 和 b 的联合索引,能不能走索引,时间复杂度
最左匹配原则会一直向右匹配,直到遇到范围查询(如 >、<)为止。对于>=、<=、BETWEEN 以及前缀匹配 LIKE 的范围查询,不会停止匹配。
比如有联合索引 [a、b、c],where 过滤条件中哪些排列组合可以用到索引?(比如这种:where a=xxx b=xxx and c=xxx)
以下排列组合都会走索引: a、ab、ac、ba、ca、abc、acb、bac、bca、cab、cba。 必须有一个 a,排列组合中的顺序会被优化器优化,所以不用关心顺序。
以下排列组合不会走索引: b、c、bc、cb。 因为没有 a。
关于范围查询: a=xxx and b<10 and b> 5 and c =xxx,c 字段用不到索引,因为 b 是一个范围查询,遇到范围查询就停止了。
最左匹配原则的原理: 我们都知道索引的底层是一颗 B+ 树,那么联合索引当然还是一颗 B+ 树,只不过联合索引的健值数量不是一个,而是多个。构建一颗 B + 树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建 B + 树。 例子:假如创建一个(a,b) 的联合索引,那么它的索引树是这样的,如下图所示:

可以看到 a 的值是有顺序的,1,1,2,2,3,3,3,3。b 的值是没有顺序的 1,2,2,5,1,2,3,5。
所以 b = 2 这种查询条件没有办法利用索引,因为联合索引首先是按 a 排序的,b 是无序的。
同时我们还可以发现在 a 值相等的情况下,b 值又是按顺序排列的,但是这种顺序是相对的。所以最左匹配原则遇上范围查询就会停止,剩下的字段都无法使用索引。例如 a=1 and b=2 a,b 字段都可以使用索引,因为在 a 值确定的情况下 b 是相对有序的,而 a>1 and b=2,a 字段可以匹配上索引,但 b 值不可以,因为 a 的值是一个范围,在这个范围中 b 是无序的。
# 二级索引
tag:
来未来、美团count:3
as:
二级索引(Secondary Index)的叶子节点存储的数据是主键的值,也就是说,通过二级索引可以定位主键的位置,二级索引又称为辅助索引 / 非主键索引。
唯一索引,普通索引,前缀索引等索引都属于二级索引。
- 唯一索引 (Unique Key): 唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。 建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
- 普通索引 (Index): 普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。
- 前缀索引 (Prefix): 前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小,因为只取前几个字符。
- 全文索引 (Full Text): 全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MYISAM 引擎支持全文索引,5.6 之后 InnoDB 也支持了全文索引。
二级索引:

# 二级索引为何存主键不存数据的内存地址?
tag:
美团count:1
as:
在许多数据库管理系统中,数据行可能会因为插入、删除或更新操作而发生物理位置的变化。例如,在执行紧凑化或碎片整理时,数据行的位置会改变。如果二级索引直接引用了内存地址,那么每次数据行移动都需要更新所有相关的索引条目,这将是非常昂贵的操作。
通过二级索引只存储主键值,当数据行物理位置发生变化时,只需要更新主键对应的记录,而不必更新所有的二级索引条目。这大大减少了索引维护的成本。
# 索引下推
tag:
来未来、淘天、快手count:4
as:
索引下推(Index Condition Pushdown,简称 ICP) 是 MySQL 5.6 版本中提供的一项索引优化功能,它允许存储引擎在索引遍历过程中,执行部分 WHERE 字句的判断条件,直接过滤掉不满足 where 条件的记录,从而减少回表次数,提高查询效率。
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`zipcode` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`birthdate` date NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_username_birthdate` (`zipcode`,`birthdate`) ) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4;
# 查询 zipcode 为 431200 且生日在 3 月的用户
# birthdate 字段使用函数索引失效
SELECT * FROM user WHERE zipcode = '431200' AND MONTH(birthdate) = 3;
2
3
4
5
6
7
8
9
10
11
没有索引下推之前,即使 zipcode 字段利用索引可以帮助我们快速定位到 zipcode = '431200' 的用户,但我们仍然需要对每一个找到的用户进行回表操作,获取完整的用户数据,再去判断 MONTH(birthdate) = 3 。
有了索引下推之后,存储引擎会在使用 zipcode 字段索引查找 zipcode = '431200' 的用户时,同时判断 MONTH(birthdate) = 3 。这样,只有同时满足条件的记录才会被返回,减少了回表次数。


索引下推的下推其实就是指将部分上层(Server 层)负责的事情,交给了下层(存储引擎层)去处理。

可以看出,除了可以减少回表次数之外,索引下推还可以减少存储引擎层和 Server 层的数据传输量。
# 索引为什么能查找快?
索引之所以能加快查找速度,主要是因为它改变了数据的存储方式。索引是一种数据结构,通常使用 B 树或其变种(如 B + 树)来组织数据,使得数据能够按照一定的顺序存储。以下是索引加快查找速度的原因:
- 有序存储:索引将数据按照某种顺序存储,使得查找时可以快速定位到目标位置。例如,B + 树索引允许使用二分查找等算法快速定位数据。
- 减少磁盘 IO:通过索引,可以减少磁盘读取的次数,因为索引结构(如 B 树或 B + 树)可以将数据分层存储,每次查找只需读取一部分数据即可。
- 高效的数据定位:索引结构允许使用指针或其他数据结构快速跳转到所需的数据页,从而提高查找效率。
# B + 树
tag:
美团、万得、字节、京东、Meta App、字节、腾讯、小米、快手、得物、知乎、经纬恒润、shopee、4399、百度、哔哩哔哩、税友、途虎养车、淘天、瑞幸、亚信、Fabrie、数字马力、深信服、传音、猫眼、满帮、大华、TCL、深信服、淘天、招行、华创云鼎、饿了么、货拉拉、七牛云、探探、美的、青书、天源迪科、moka、淘米、贝壳、麦米电气、顺丰、阿里、蔚来、竞技世界count:107
as:为什么使用 B + 树,不适用 B 树或者 Hash 索引或红黑树?
innodb 为什么使用 b + 树,什么时候使用 hash (自适应哈希索引)。
B + 树有啥好处呢?坏处呢?
B + 树一个非叶节点存多少数据
B + 树和 B 树区别、主键索引和普通索引的叶子节点存储的分别是什么数据
MySQL 为什么使用 B + 树作为索引的存储结构,以及它的查询时间复杂度
mysql 索引结构
mysql 插入多个数据的过程,描述 B + 树的变化过程
联合索引,如何在 b + 树体现的
插入了一个数据,给你一个组合查询,mysql 中的索引是如何变化的
b + 树的数据一定是存在叶子节点吗?
B + 索引比 hash 索引、B 树索引、二叉索引好的点
数据库的 B+Tree 和 Hash 如何选择?
按主键和普通索引分别检索的过程
b 树 b - 树 b + 树优劣,各自的作用场景
非叶子节点存储的是地址,是吧?
B 树 & B + 树两者有何异同呢?
- B 树的所有节点既存放键 (key) 也存放数据 (data),而 B + 树只有叶子节点存放 key 和 data,其他内节点只存放 key。
- B 树的叶子节点都是独立的;B + 树的叶子节点有一条引用链指向与它相邻的叶子节点。
- B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B + 树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
- 在 B 树中进行范围查询时,首先找到要查找的下限,然后对 B 树进行中序遍历,直到找到查找的上限;而 B + 树的范围查询,只需要对链表进行遍历即可。

存储结构
- B 树的查询波动会比较大,因为每个节点即存索引又存记录,所以有时候访问到了非叶子节点就可以找到索引,而有时需要访问到叶子节点才能找到索引。
- B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B + 树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少。
维护
- B 树没有冗余节点,删除节点的时候非常复杂,比如删除根节点中的数据,可能涉及复杂的树的变形。
- B+ 树在删除根节点的时候,由于存在冗余的节点,所以不会发生复杂的树的变形
查询
- B 树没有将所有叶子节点用链表串联起来的结构,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作
- B+ 树所有叶子节点间还有一个链表进行连接,这种设计对范围查找非常有帮助,比如说我们想知道 12 月 1 日和 12 月 12 日之间的订单,这个时候可以先查找到 12 月 1 日所在的叶子节点,然后利用链表向右遍历,直到找到 12 月 12 日的节点,这样就不需要从根节点查询了,进一步节省查询需要的时间。
MySQL 的存储方式根据存储引擎的不同而不同,我们最常用的就是 Innodb 存储引擎,它就是采用了 B+ 树作为了索引的数据结构。

但是 Innodb 使用的 B+ 树有一些特别的点,比如:
- B+ 树的叶子节点之间是用「双向链表」进行连接,这样的好处是既能向右遍历,也能向左遍历。
- B+ 树点节点内容是数据页,数据页里存放了用户的记录以及各种信息,每个数据页默认大小是 16 KB。
Innodb 根据索引类型不同,分为聚集和二级索引。他们区别在于,聚集索引的叶子节点存放的是实际数据,所有完整的用户记录都存放在聚集索引的叶子节点,而二级索引的叶子节点存放的是主键值,而不是实际数据。
因为表的数据都是存放在聚集索引的叶子节点里,所以 InnoDB 存储引擎一定会为表创建一个聚集索引,且由于数据在物理上只会保存一份,所以聚簇索引只能有一个,而二级索引可以创建多个。
# 慢查询优化
tag:
美团、小米、字节、知乎、用友、得物、数字马力、途虎养车、百度、神策数据、京东、快手、momenta、苏小妍、用友、信也、腾讯、格创东智、富途、亚信、汇川、立白、小红书、金蝶、大华、携程、微派、招行、淘天、蔚来、哔哩哔哩、华创云鼎、捷运达、阿里、浩鲸、淘米、贝壳、麦米电气、滴滴、京东count:80
as:有过 MySQL 调优经验吗
优化器怎么去看索引的区分度,如果 A 能查出 1000 条数据,B 能查出来两条数据,优化器该怎么设计
MySQL 语句优化经历,以及优化方式和手段
b + 树分库分表高并发除了加锁还有什么解决方案
在我们的项目中,在上线时使用 skywalking 来定位慢的查询,如果发现是某个 SQL 执行速度慢,我们就可以使用 skywalking 的追踪功能,来确定 SQL 语句。
而在测试环境中,我们使用 MySQL 提供的慢日志来确定慢查询的位置。mysql 是默认没有开启慢日志的,需要通过配置文件开启并设置快查询的最大时间,超过这个时间就认为其为慢查询,我们就可以在慢日志中找到慢查询的 sql, 在我们的项目中设置最大的时间为 2 秒。
#开启慢日志
slow_query_log=1
#设置快查询的最大时间
long_query_time=2
2
3
4
# 超大分页查询怎么进行优化?
Mysql 做 limit 分页查询的时候,需要做排序,这个过程非常耗时。
优化方案:覆盖索引 + 子查询。先通过子查询出分页排序完后的 id 主键,因为是主键所以会直接进行覆盖索引查询。通过子查询的 id 关联表中的 id 查询出分页后的数据。
select * from table t
(select id from table limit 0 10 order by id) s
where t.id = s.id
2
3
# mysql 执行计划(explain 语句)
tag:
美团、字节、百度、得物、苏小妍、亚信、腾讯、明朝万达、传音、小红书、大智慧、TCL、微派、得物、阿里、淘米、贝壳、邦盛科技count:33
查索引情况有关命令
分析一条 sql 的执行计划(explain 关注 key type extra 这三个属性,并简单解释了这 3 个属性)
Explain key 和 possible_key 的区别,extra 中出现 Using index condition、Using filesort 是为什么?
我们可以使用 EXPLAIN 命令来分析 SQL 的 执行计划 。执行计划是指一条 SQL 语句在经过 MySQL 查询优化器的优化会后,具体的执行方式。
EXPLAIN 并不会真的去执行相关的语句,而是通过 查询优化器 对语句进行分析,找出最优的查询方案,并显示对应的信息。
EXPLAIN 适用于 SELECT , DELETE , INSERT , REPLACE , 和 UPDATE 语句,我们一般分析 SELECT 查询较多。
我们这里简单来演示一下 EXPLAIN 的使用。
EXPLAIN 的输出格式如下:
mysql> EXPLAIN SELECT `score`,`name` FROM `cus_order` ORDER BY `score` DESC;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| 1 | SIMPLE | cus_order | NULL | ALL | NULL | NULL | NULL | NULL | 997572 | 100.00 | Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
mysql> EXPLAIN SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31';
+----+-------------+-------+--------+-------------------------------------+---------+---------+-------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------------------+---------+---------+-------+------+----------------------------------------------+
| 1 | SIMPLE | orders| range | PRIMARY,idx_order_date | idx_order_date | 4 | NULL | 3 | Using where; Using index |
+----+-------------+-------+--------+-------------------------------------+---------+---------+-------+------+----------------------------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
各个字段的含义如下:
| 列名 | 含义 |
|---|---|
| id | SELECT 查询的序列标识符 |
| select_type | SELECT 关键字对应的查询类型 |
| table | 用到的表名 |
| partitions | 匹配的分区,对于未分区的表,值为 NULL |
| type | 表的访问方法 |
| possible_keys | 可能用到的索引 |
| key | 实际用到的索引 |
| key_len | 所选索引的长度 |
| ref | 当使用索引等值查询时,与索引作比较的列或常量 |
| rows | 预计要读取的行数 |
| filtered | 按表条件过滤后,留存的记录数的百分比 |
| Extra | 附加信息 |
# Type 字段
Type 字段显示了连接类型,它表明了 MySQL 如何找到并返回行。连接类型按从最差到最好的顺序排列如下:
- ALL: MySQL 必须对表中的所有行进行全面扫描。这是最慢的方式。
- index: 类似于 ALL,但只扫描索引树。
- range: 只扫描给定范围的行,使用了一个索引。
- ref: 表示使用非唯一索引或唯一索引前面的部分进行查找。
- fulltext: 全文索引匹配。
- eq_ref: 对唯一索引的引用,通常是外键。
- const: 匹配单行记录,通常用于等值条件。
- system: 系统表,通常是 MyISAM 或 MEMORY 表。
- unique: 查找唯一行,但返回多行。
- unique_subquery: 用 IN 子查询来替换为等值比较。
- index_subquery: 用 IN 子查询来替换为使用索引查找。
- index_merge: 使用多个索引来返回行。
- literal: 常量表,表中有固定数量的行。
- NULL: 不需要表连接,可能是子查询的结果。
# Key 字段
Key 字段显示了 MySQL 使用哪个索引来优化 SQL 查询。如果 Key 字段显示为 NULL ,则表示没有使用索引。
- NULL: 没有使用索引。
- index_name: 显示使用的索引名称。
- PRIMARY: 主键索引。
- UNIQUE: 唯一索引。
- FULLTEXT: 全文索引。
- SPATIAL: 空间索引。
# Extra 字段
Extra 字段包含了 MySQL 解释器在执行查询时的一些额外信息,这些信息可以帮助诊断查询效率问题:
- Using where: 表示使用了 WHERE 子句来过滤结果。
- Using index: 表示使用了覆盖索引(Covering Index),即索引包含了查询所需的全部列。
- Using temporary: 表示使用了临时表来排序结果。
- Using filesort: 表示 MySQL 需要额外的排序操作来获取有序结果。
- Not exists: 表示在子查询中使用了 NOT EXISTS 或 LEFT JOIN 并且 ON 子句中没有关联列。
- Distinct: MySQL 发现第一个匹配后停止搜索。
- Range checked for each record (index map: n): 对于每个记录,MySQL 使用索引来检查范围。
- Impossible WHERE noticed after reading const tables: WHERE 子句不可能为真,因为常量表已经决定了结果。
- Impossible HAVING noticed before execution: HAVING 子句不可能为真,因为在执行前就已经注意到了。
- Using join buffer: 使用了连接缓冲区来优化连接操作。
# 日志
tag:
字节、得物、小米、快手、小红书、美团、神策数据、滴滴、淘天、招行、美团、小红书、用友、富途count:21
as:redoLog,UndoLog 作用是啥对应 ACID 哪个板块,在执行一个 sql 的过程中怎么使用的
MySQL 日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中,比较重要的还要属二进制日志 binlog(归档日志)和事务日志 redo log(重做日志)和 undo log(回滚日志)。
# redo log 和 bin log 区别
tag:
美团、百度、快手、小米、永辉超市count:9
除了 BINLOG 日志还知道其他的日志嘛
MySQL 分两层:Server 层和引擎层。
- Server 层:主要做的是 MySQL 功能层面的事情。Server 层也有自己的日志,称为 binlog(归档日志)
- 引擎层:负责存储相关的具体事宜。redo log 是 InnoDB 引擎特有的日志。
- redo log 是物理日志,记录的是 “在某个数据页上做了什么修改”;redo log 是循环写的,空间固定会用完;用于记录数据页的物理变化,当服务宕机的时候进行数据同步操作。保证了事务的持久性。
- binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如 “给 ID=2 这一行的 c 字段加 1”。binlog 是可以追加写入的。“追加写” 是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
# undo log
tag:
小红书、讯飞count:3
as:undo_log 日志作用,MVCC 中有没有用到
记录逻辑日志,就比如:当做插入操作时会在日志中记录逆向的操作也即是删除,在事务回滚的时候会执行逻辑日志中的指令。保证了事务的持久性和原子性。
- 回滚数据,以行为单位,记录数据每次的变更,一行记录有多个版本并存
- 多版本并发控制,即快照读(也称为一致性读),让查询操作可以去访问历史版本

- 每个事务会按照开始时间,分配一个单调递增的事务编号 trx id
- 每次事务的改动都会以行为单位记入回滚日志,包括当时的事务编号,改动的值等
- 查询操作,事务编号大于自己的数据是不可见的,事务编号小于等于自己的数据才是可见的
- 例如图中红色事务看不到 trx id=102 以及 trx id=101 的数据,只有 trx id=99 的数据对它可见
# redo log
redo log 的作用主要是实现 ACID 中的持久性,保证提交的数据不丢失
- 它记录了事务提交的变更操作,服务器意外宕机重启时,利用 redo log 进行回放,重新执行已提交的变更操作
- 事务提交时,首先将变更写入 redo log,事务就视为成功。至于数据页(表、索引)上的变更,可以放在后面慢慢做
- 数据页上的变更宕机丢失也没事,因为 redo log 里已经记录了
- 数据页在磁盘上位置随机,写入速度慢,redo log 的写入是顺序的速度快
它由两部分组成,内存中的 redo log buffer,磁盘上的 redo log file
- redo log file 由一组文件组成,当写满了会循环覆盖较旧的日志,这意味着不能无限依赖 redo log,更早的数据恢复需要 binlog
- buffer 和 file 两部分组成意味着,写入了文件才真正安全,同步策略由参数 innodb_flush_log_at_trx_commit 控制
- 0 - 每隔 1s 将日志 write and flush 到磁盘
- 1 - 每次事务提交将日志 write and flush(默认值)
- 2 - 每次事务提交将日志 write,每隔 1s flush 到磁盘,意味着 write 意味着写入操作系统缓存,如果 MySQL 挂了,而操作系统没挂,那么数据不会丢失
# 什么时候用 redo log 或 bin log
tag:
美团、字节count:2
- Redo Log:主要用于崩溃恢复,记录对数据库进行的所有修改操作(如插入、更新、删除等),以便在数据库崩溃后可以重新执行这些操作,确保事务的持久性和数据的一致性。
- Binlog:主要用于主从复制和数据恢复,记录了所有对数据库结构和内容所做的变更操作(DDL 和 DML),但不包括 SELECT 查询。它可以用来重建数据库的状态,特别是在发生逻辑错误或者需要回滚到某个时间点时非常有用。
# 事务
tag:
蔚来、字节、快手、数字马力、shopee、小米、百度、微派、腾讯、得物、喜马拉雅、美团、泓齐网科、金山办公、shopeecount:28
as:事务有什么特性?
事务实现的底层原理
# 事务是什么
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
# 数据库 ACID 的特性
tag:
小米、数字马力、字节、快手、汇川、传音、微派、得物、百度、青书、淘米、贝壳、软通、卓望、淘天、美团、蔚来、北森count:30
as:acid 都是什么,mysql 怎么实现的
事务的 acid 特性具体是怎么什么保证的?
项目中事务 ACID 原理怎么实现的
- 原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency):指事务前后数据的完整性必须保持一致。
- 隔离性(Isolation):指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
- 持久性(Durability):指一个事务一旦提交,它对数据库中数据的改变就是永久性的,即便数据库发生故障也不应该对其有任何影响。
# 数据库的隔离级别
tag:
美团、京东、字节、小米、数字马力、快手、浩鲸、shopee、瑞幸、作业帮、哔哩哔哩、税友、百度、Cider、传音、猫眼、淘天、招行、得物、喜马拉雅、小红书、货拉拉、七牛云、联想、讯飞、万得count:75
as:四个隔离级别的原理讲讲?
读已提交隔离级别下是否有使用 readview
读已提交和可重复读这两个隔离级别下,哪个锁的粒度更大,为什么
默认隔离级别
数据库默认隔离级别一般是可重复读,具体怎么实现的
为什么要分四个隔离级别,分别解决什么问题,怎么实现的?
隔离级别哪些解决不了幻读
- 脏读(Dirty Read):事务 1 读取到事务 2 未提交的数据。
- 不可重复读(NonRepeatable Read):事务 2 先后读取事务 1 中的某合个数据,两次的结果不一样。
- 幻读(Phantom Read):一个事务在按条件查询数据时没有查到数据吗,但是插入操作时,又发现该数据已经存在。因为其他事务在这个过程中插入数据。
| 隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
|---|---|---|---|
| 未提交读 | 可能 | 可能 | 可能 |
| 已提交读 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能 |
| 可串行化 | 不可能 | 不可能 | 不可能 |
- 未提交读 (Read Uncommitted):无法解决并发事务带来的问题,允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。
- 已提交读 (Read Committed):可以解决脏读,只能读取到已经提交的数据。Oracle 等多数数据库默认都是该级别 (不重复读)。
- 可重复读 (Repeated Read):可以解决脏读,不可重复读,在同一个事务内的查询都是事务开始时刻一致的,InnoDB 默认级别。在 SQL 标准中,该隔离级别消除了不可重复读,但是还存在幻象读。
- 串行读 (Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。事务只能一个一个执行,可以解决脏读,不可重复读,幻读,隔离级别最高,效率最低。
隔离级别是由排他锁 + MVCC 实现的。
# 默认隔离级别是什么?
tag:
京东、阿里、Cider、快手、淘天、腾讯、完美、万得、富途、字节count:11
as:
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过 SELECT @@tx_isolation; 命令来查看,MySQL 8.0 该命令改为 SELECT @@transaction_isolation;
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
2
3
4
5
6
# 可重复读
tag:
京东、阿里、小米、哔哩哔哩、瑞幸、满帮、完美、百度、携程、富途、4399count:13
as:可重复读怎么实现的?
重复读和幻读的区别是什么?
RR 下,事务 A 正在读一个数据未提交,事务 B 可以读这个数据吗?此时事务 B 可以对这个变量进行写操作吗?
可重复读隔离级别怎么实现的
# 数据库的脏读、幻读、不可重复读
tag:
恒生、字节、同余、得物、百度count:5
as:
# 脏读
tag:
淘天count:1
as:
指一个事务 A 正在访问数据,并且对该数据进行了修改,但是这种修改还没有提交到数据库中(也可能因为某些原因 Rollback 了)。这时候另外一个事务 B 也访问这个数据,然后使用了这个被 A 修改的数据,那么这个数据就是脏的,并不是数据库中真实的数据。这就被称作脏读。(事务 A 读到了事务 B 未提交的数据)
解决办法:把数据库事务隔离级别调整到 READ_COMMITTED
即让用户在更新时锁定数据库,阻止其他用户读取,直到更新全部完成才让你读取。
# 幻读
tag:
字节、数字马力、京东、小米、作业帮、同余、瑞幸、快手、完美、美团、七牛云count:16
as:当前读和快照读
指一个事务 A 对一个表中的数据进行了修改,而且该修改涉及到表中所有的数据行;同时另一个事务 B 也在修改表中的数据,该修改是向表中插入一行新数据。那么经过这一番操作之后,操作事务 A 的用户就会发现表中还有没修改的数据行,就像发生了幻觉一样。这就被称作幻读。(事务 A 修改数据,事务 B 插入数据,A 发现表中还没有修改的数据行)
解决办法:把数据库事务隔离级别调整到 SERIALIZABLE_READ
# 如何解决幻读问题?
tag:
美团、万得、携程、数字马力、同余、快手、完美、小红书、七牛云count:15
as:可重复读如何解决幻读问题?
哪种事务级别会引起幻读?
mvcc + 间隙锁怎么解决幻读
解决幻读的方式有很多,但是它们的核心思想就是一个事务在操作某张表数据的时候,另外一个事务不允许新增或者删除这张表中的数据了。解决幻读的方式主要有以下几种:
- 将事务隔离级别调整为
SERIALIZABLE。 - 在可重复读的事务级别下,给事务操作的这张表添加表锁。
- 在可重复读的事务级别下,给事务操作的这张表添加
Next-key Lock(Record Lock+Gap Lock)。
# 不可重复读
指在一个事务 A 内,多次读同一个数据,但是事务 A 没有结束时,另外一个事务 B 也访问该同一数据。那么在事务 A 的两次读数据之间,由于事务 B 的修改导致事务 A 两次读到的数据可能是不一样的。这就发生了在一个事务内两次读到的数据不一样,这就被称作不可重复读。(事务 A 多次读数据,事务 B 访问数据,A 读到了 B 修改的数据,导致两次读到的数据不一样)
解决办法:把数据库事务隔离级别调整到 REPEATABLE_READ
级别高低:脏读 < 不可重复读 < 幻读
所以设置了最高级别的 SERIALIZABLE_READ 就不需要设置其他的了,即解决了幻读问题那么脏度和不可重复读自然就都解决了。
# 事务级别
tag:
万得、小米、快手、浩鲸、得物、百度count:7
as:两个事务 A,B,A 事务开启之后先查询了一条数据,然后 B 事务修改了这条数据,事务 A 接着第二次查询这条数据,B 事务提交了事务,A 第三次查询了事务,三次事务分别查询出来的是修改前的值还是修改后的值
为什么不直接选用序列化的事务级别?这个等级不是最高的吗?
# 一致性你是怎么理解的?
tag:
count:2
MySQL 事务回滚,怎么知道上一个状态
# innodb 事务讲讲?
tag:
美团count:3
# 为什么要有事务?
tag:
美团count:1
# 能仔细讲讲 buffer pool 吗?change pool 呢?
tag:
美团count:1
# 什么时候会写回盘?
tag:
美团count:1
# 细说两阶段提交
tag:
美团count:1
# 如何保证事务特性
tag:
Meta App、字节、百度count:5
as:如何保证事物的一致性?
# 如何保证并发性
tag:
Meta App、金山count:4
as:怎么处理并发事务
数据库中如何解决的并发问题,数据隔离性如何保证的
MySQL 中并发事务的控制方式无非就两种:锁 和 MVCC。锁可以看作是悲观控制的模式,多版本并发控制(MVCC,Multiversion concurrency control)可以看作是乐观控制的模式。
# 锁
锁 控制方式下会通过锁来显式控制共享资源而不是通过调度手段,MySQL 中主要是通过 读写锁 来实现并发控制。
- 共享锁(S 锁):又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
- 排他锁(X 锁):又称写锁 / 独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条记录加任何类型的锁(锁不兼容)。
读写锁可以做到读读并行,但是无法做到写读、写写并行。另外,根据根据锁粒度的不同,又被分为 表级锁 (table-level locking) 和 行级锁 (row-level locking) 。InnoDB 不光支持表级锁,还支持行级锁,默认为行级锁。行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类。
# MVCC
MVCC 是多版本并发控制方法,即对一份数据会存储多个版本,通过事务的可见性来保证事务能看到自己应该看到的版本。通常会有一个全局的版本分配器来为每一行数据设置版本号,版本号是唯一的。
MVCC 在 MySQL 中实现所依赖的手段主要是: 隐藏字段、read view、undo log。
- undo log : undo log 用于记录某行数据的多个版本的数据。
- read view 和 隐藏字段:用来判断当前版本数据的可见性。
# MVCC
tag:
美团、小米、京东、字节、shopee、得物、用友、喜马拉雅、快手、联想、讯飞、蔚来、金山count:28
as:mysql 的 mvcc 如何保证 rr
mvcc 的实现原理
mvcc 的作用
mvcc 是如何实现 RR 和 RC 的,如何解决幻读的?
为什么 MVCC 可以实现可重复读
mvcc 具体怎么实现事务的隔离性的
MVCC 是一种并发控制机制,用于在多个并发事务同时读写数据库时保持数据的一致性和隔离性。它是通过在每个数据行上维护多个版本的数据来实现的。当一个事务要对数据库中的数据进行修改时,MVCC 会为该事务创建一个数据快照,而不是直接修改实际的数据行。多版本并发控制。维护一个数据的多个版本,使得读写操作没有冲突。
1、读操作(SELECT):
当一个事务执行读操作时,它会使用快照读取。快照读取是基于事务开始时数据库中的状态创建的,因此事务不会读取其他事务尚未提交的修改。具体工作情况如下:
- 对于读取操作,事务会查找符合条件的数据行,并选择符合其事务开始时间的数据版本进行读取。
- 如果某个数据行有多个版本,事务会选择不晚于其开始时间的最新版本,确保事务只读取在它开始之前已经存在的数据。
- 事务读取的是快照数据,因此其他并发事务对数据行的修改不会影响当前事务的读取操作。
2、写操作(INSERT、UPDATE、DELETE):
当一个事务执行写操作时,它会生成一个新的数据版本,并将修改后的数据写入数据库。具体工作情况如下:
- 对于写操作,事务会为要修改的数据行创建一个新的版本,并将修改后的数据写入新版本。
- 新版本的数据会带有当前事务的版本号,以便其他事务能够正确读取相应版本的数据。
- 原始版本的数据仍然存在,供其他事务使用快照读取,这保证了其他事务不受当前事务的写操作影响。
3、事务提交和回滚:
- 当一个事务提交时,它所做的修改将成为数据库的最新版本,并且对其他事务可见。
- 当一个事务回滚时,它所做的修改将被撤销,对其他事务不可见。
4、版本的回收:
为了防止数据库中的版本无限增长,MVCC 会定期进行版本的回收。回收机制会删除已经不再需要的旧版本数据,从而释放空间。
MVCC 通过创建数据的多个版本和使用快照读取来实现并发控制。读操作使用旧版本数据的快照,写操作创建新版本,并确保原始版本仍然可用。这样,不同的事务可以在一定程度上并发执行,而不会相互干扰,从而提高了数据库的并发性能和数据一致性。
# MVCC 实现
MVCC 的实现依赖于:隐藏字段、Read View、undo log。在内部实现中, InnoDB 通过数据行的 DB_TRX_ID 和 Read View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View 之前已经提交的修改和该事务本身做的修改
隐藏字段:在内部,
InnoDB存储引擎为每行数据添加了三个 隐藏字段 open in new window (opens new window):DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id,记录当前事务的 id,其为自增的。此外,delete操作在内部被视为更新,只不过会在记录头Record header中的deleted_flag字段将其标记为已删除DB_ROLL_PTR(7字节)回滚指针,指向该行的undo log上一个版本的事务记录地址。如果该行未被更新,则为空DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB会使用该 id 来生成聚簇索引
undo log:回滚日志,存储老版本的数据,版本链:多个同时修改某条记录,产生多版本的数据,通过 rool-pointer 指针形成链表。
Read View:解决一个事务查询选择版本的问题。
根据 Read View 的匹配规则和当前事务 id 找到对应的版本信息。
规则
- 判断是事务 id 是否为当前事务的 id。
- 是否是活跃事务 id。
- 判断事务是否是在 readview 创建后开启的,也就是事务 id 大于当前事务。
- 判断事务中的数据是否已提交,事务 id 小于最小的事务 id。
不同的隔离级别快照读是不一样的,最终的访问结果也是不一样的。
- 当前读:读取的是最新的数据并且会加锁。快照读:读取的是记录数据的可见版本,不会加锁。
- 读已提交:在每次快照读的时候都会生成 readview。
- 可重复读:在有在第一次快照读的时候才会生成 readview,后续的快照读都是使用该 readview 的复制,保证数据的一致性。
# 事务什么时候会失效
tag:
招行、泓齐网科count:2
as:
# 锁
# 锁的类型
tag:
百度、腾讯、浩鲸、字节、哔哩哔哩、信也、小米、三一重工、招行、喜马拉雅、完美、快手、美团、4399、得物、永辉超市count:27
as:数据库锁按数据操作的颗粒度的分为哪几类
mysql 是怎么获取锁的,是一直等待还是怎么的
项目中怎么优化 sql 锁等待的问题的
select for update 什么时候用
mysql 中哪些语句会加锁
InnoDB 有哪些锁
锁机制说一下,从兼容性和粒度上来说
select for update 加的是什么锁
# InnoDB 的锁类型
- 共享锁(S 锁):允许多个事务同时读取同一数据项,但阻止任何事务对其进行写入。
- 排他锁(X 锁):独占某个数据项,不允许其他事务读取或写入该数据项。
- 意向锁(Intention Locks):表示事务打算在较低层次上获取锁,例如在表上加意向锁表明事务将在表中的某些行上加锁。
- 间隙锁(Gap Locks):锁定记录之间的 “间隙”,用于防止幻读。
- Next-Key 锁:结合了行锁和间隙锁的功能,不仅锁定记录本身,还锁定记录前后的间隙。
# 数据库锁按数据操作的颗粒度分类
- 表级锁(Table-Level Locking):锁定整个表,防止其他事务对表进行读写操作。适用于批量操作或在不需要高并发的情况下。
- 行级锁(Row-Level Locking):只锁定受影响的数据行,允许其他事务访问同一表中未被锁定的其他行。提供更高的并发性能,但管理和维护成本较高。
- 页级锁(Page-Level Locking):介于表级和行级之间,锁定的是存储引擎中的一整页数据(通常包含多行)。它比表级锁更细粒度,但不如行级锁灵活。
- 列级锁(Column-Level Locking):理论上可以锁定单个列,但实际上很少使用,因为实现复杂且开销大。
# InnoDB 有哪几类行锁?
InnoDB 行锁是通过对索引数据页上的记录加锁实现的,MySQL InnoDB 支持三种行锁定方式:
- 记录锁(Record Lock):也被称为记录锁,属于单个行记录上的锁。
- 间隙锁(Gap Lock):锁定一个范围,不包括记录本身。
- 临键锁(Next-Key Lock):Record Lock+Gap Lock,锁定一个范围,包含记录本身,主要目的是为了解决幻读问题(MySQL 事务部分提到过)。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。
在 InnoDB 默认的隔离级别 REPEATABLE-READ 下,行锁默认使用的是 Next-Key Lock。但是,如果操作的索引是唯一索引或主键,InnoDB 会对 Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。
# select for update 什么时候用
SELECT ... FOR UPDATE 语句用于在读取数据的同时对其加排他锁,防止其他事务修改这些数据。这在以下几种情况下特别有用:
- 防止脏读、不可重复读和幻读:确保在同一事务内多次读取相同的数据结果一致。
- 保证数据更新的一致性:当你计划后续对该数据行进行更新时,先锁定可以避免其他事务在此期间对其进行更改。
- 控制并发更新:在某些业务场景下,可能需要确保只有一个事务能对特定的数据行进行操作,比如库存管理中的减库存操作。
SELECT ... FOR UPDATE 在 InnoDB 中加的是排他锁(X 锁),这意味着它不仅阻止其他事务对选定的数据行进行更新或删除操作,同时也阻止其他事务通过 SELECT ... FOR UPDATE 或 SELECT ... LOCK IN SHARE MODE 对相同行加锁。这种锁确保了在一个事务完成之前,其他事务无法改变这些数据行的状态,从而保障了数据的一致性和完整性。
# mysql 中哪些语句会加锁
- DML 语句:如
INSERT、UPDATE和DELETE都会对涉及的数据行加锁。 - DDL 语句:如
CREATE TABLE、ALTER TABLE和DROP TABLE等结构变更操作可能会导致表级别的锁定。 SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE:显式地对查询结果集加锁。- 隐式锁定:在某些情况下,即使是一个简单的
SELECT查询也可能会触发隐式锁定,具体取决于隔离级别和其他配置。
# 锁是怎么实现的
tag:
联想、快手count:2
as:mysql 中的锁记录是怎么保存的?让你设计你怎么设计?
# 什么时候会触发 next-key 锁,行锁呢?
tag:
美团count:1
# 公平锁和非公平锁得区别
tag:
恒生count:1
as:
# 乐观锁和悲观锁
tag:
数字马力、瑞幸、同余、满帮、淘天、招行count:8
as:MySQL 如何实现乐观和悲观锁
for update 是悲观锁还是乐观锁,
怎么用乐观锁实现数据库并发读数据和写数据
# 行锁
tag:
知乎、哔哩哔哩、途虎养车、信也、小米、百度count:12
as:
什么情况会给多行数据加锁,如果数据不存在,select 加行锁,mysql 是怎么处理的。如果另一个事务 insert,会怎样
行锁的粒度划分
怎么实现行级锁的?
insert 插入一条记录是什么锁
MyISAM 仅仅支持表级锁 (table-level locking),一锁就锁整张表,这在并发写的情况下性非常差。InnoDB 不光支持表级锁 (table-level locking),还支持行级锁 (row-level locking),默认为行级锁。
行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。
行级锁: MySQL 中锁定粒度最小的一种锁,是 针对索引字段加的锁 ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的。
InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁。当我们执行 UPDATE 、 DELETE 语句时,如果 WHERE 条件中字段没有命中唯一索引或者索引失效的话,就会导致扫描全表对表中的所有行记录进行加锁。
不过,很多时候即使用了索引也有可能会走全表扫描,这是因为 MySQL 优化器的原因。
# 表锁
tag:
字节、哔哩哔哩、途虎养车、百度count:6
as:什么时候使用表锁
表级锁: MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。表级锁和存储引擎无关,MyISAM 和 InnoDB 引擎都支持表级锁。
# 间隙锁
tag:
字节count:3
as:什么时候使用间隙锁,间隙锁是显示使用还是隐式使用
间隙锁(Gap Lock)是 InnoDB 存储引擎中用于锁定索引记录之间 “间隙” 的一种锁机制。它的主要目的是防止其他事务在这些间隙中插入新记录,从而避免幻读现象的发生。
在 InnoDB 中,索引是按照一定的顺序排列的,每个索引记录代表一行数据。两个相邻的索引记录之间的空间被称为 “间隙”。例如,在一个有序的索引中,如果存在两条记录 A 和 B ,那么 A 和 B 之间的空间就是一个间隙。此外,索引的开头和结尾也可以被视为特殊的间隙。
间隙锁的主要作用是锁定索引记录之间的间隙,而不是具体的记录本身。通过锁定间隙,InnoDB 可以阻止其他事务在这个间隙内插入新的记录,从而确保当前事务在读取或修改数据时不会受到其他事务的影响。这有助于维护数据的一致性和完整性,特别是在高并发环境下。
通常间隙锁(Gap Lock)在 InnoDB 存储引擎中是隐式使用的,而不是由用户显式指定。
间隙锁通常在以下几种情况下被触发:
REPEATABLE READ隔离级别下的范围查询:当你在一个使用REPEATABLE READ隔离级别的事务中执行范围查询(如SELECT ... WHERE column BETWEEN value1 AND value2),并且该查询带有FOR UPDATE或LOCK IN SHARE MODE时,InnoDB 会为查询结果集中的所有记录以及它们之间的间隙加上间隙锁。这样可以防止其他事务在这个范围内插入新记录。- 唯一索引上的等值查询:如果你在唯一索引上执行等值查询(如
SELECT ... WHERE unique_column = value),即使没有明确指定FOR UPDATE或LOCK IN SHARE MODE,InnoDB 也可能应用间隙锁来保护该记录及其前后间隙不被其他事务修改。这是为了防止其他事务插入与现有记录冲突的新记录。 INSERT操作中的间隙锁:在插入新记录时,InnoDB 会在适当的间隙上加锁,以确保同一事务内的后续操作不会与其他事务发生冲突。这种锁可以帮助防止多个事务尝试在同一位置插入相同的数据。UPDATE和DELETE操作:对于更新或删除操作,如果这些操作影响到了索引中的记录,InnoDB 也会对涉及的间隙加锁,以确保数据的一致性。
# 死锁
tag:
4399、快手、携程、淘天count:5
as:死锁解决
mysql 死锁及应用场景
如果字段非索引会发生死锁吗?
数据库并发插入死锁怎么导致的
行级锁的死锁场景?
# 排它锁和共享锁
tag:
信也count:1
as:排他锁和共享锁的作用
不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类:
- 共享锁(S 锁):又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
- 排他锁(X 锁):又称写锁 / 独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。
排他锁与任何的锁都不兼容,共享锁仅和共享锁兼容。
| S 锁 | X 锁 | |
|---|---|---|
| S 锁 | 不冲突 | 冲突 |
| X 锁 | 冲突 | 冲突 |
由于 MVCC 的存在,对于一般的 SELECT 语句,InnoDB 不会加任何锁。不过, 你可以通过以下语句显式加共享锁或排他锁。
# 共享锁 可以在 MySQL 5.7 和 MySQL 8.0 中使用
SELECT ... LOCK IN SHARE MODE;
# 共享锁 可以在 MySQL 8.0 中使用
SELECT ... FOR SHARE;
# 排他锁
SELECT ... FOR UPDATE;
2
3
4
5
6
# 临建锁
tag:
count:1
as:
# 意向锁
如果需要用到表锁的话,如何判断表中的记录没有行锁呢,一行一行遍历肯定是不行,性能太差。我们需要用到一个叫做意向锁的东东来快速判断是否可以对某个表使用表锁。
意向锁(Intention Locks)是 InnoDB 存储引擎中用于表明事务打算在较低层次上获取锁的一种机制。它们并不是直接锁定数据行或间隙,而是作为一种信号,告知其他事务当前事务有意向在表的某个部分加锁。通过使用意向锁,InnoDB 可以更高效地管理和协调不同粒度的锁请求,从而提高并发性能并减少死锁的可能性。
意向锁是表级锁,共有两种:
- 意向共享锁(Intention Shared Lock,IS 锁):事务有意向对表中的某些记录加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
- 意向排他锁(Intention Exclusive Lock,IX 锁):事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。
意向锁是由数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。
意向锁之间是互相兼容的。
| IS 锁 | IX 锁 | |
|---|---|---|
| IS 锁 | 兼容 | 兼容 |
| IX 锁 | 兼容 | 兼容 |
意向锁和共享锁和排它锁互斥(这里指的是表级别的共享锁和排他锁,意向锁不会与行级的共享锁和排他锁互斥)。
| IS 锁 | IX 锁 | |
|---|---|---|
| S 锁 | 兼容 | 互斥 |
| X 锁 | 互斥 | 互斥 |
# 主从复制
tag:
携程、百度、奇安信、shopee、中金所、小红书、杭州银行、字节count:14
as:MySQL 主从复制原理
数据库主从,主主,分库分表这些分布式结构有哪些好处
怎么实现主从复制?
Mysql 主从同步的核心就是 bin log (二进制日志),这个日志中主要记录 DDL (表的操作),DML (表中数据的操作)。

master 中事务提交数据后,会将修改的数据保存到 bin log 中。
slave 有个 iothread 线程会监控的 bin log 的变化,并将变化写入 relay log 中。
slave 有个 SQLthread 线程会监控 relay log,将改变的数据写入 slave 中。
# 分库分表
tag:
美团、哈啰、顺丰、快手count:6
as:分库分表咋实现的?为何不用已有的?
分库分表后如果一次访库操作要从多个库请求数据 / 写入数据,该如何实现?涉及到分布式事务该如何处理?
分库分表后如果要一个任务要访问所有库表的数据,该如何实现?
表里数据过多怎么解决?用什么中间件解决?
在物流项目中的订单服务的数据非常庞大,请求数多且业务累计大。差不多单表的数据有 100w 条,这时我们就使用分库分表。
分库分表有四种策略:
- 水平分库:通过将一个库中的数据拆分到多个库中,解决海量数据存储和高并发的问题。主要通过 sharing-sphere 和 mtycat 实现。
- 水平分表:解决单表存储和性能的问题。
- 垂直分库:根据业务来拆分库中的表,在高并发的情况下提高磁盘 IO 和网络连接数。每个微服务都有自己的表。
- 垂直分表:冷热数据分离,多表不会相互影响。就比如:表中字段为 id,name,des,将 id,name 和 des 分离,id 和 name 都是热数据而 des 为冷数据,访问频率较低。
# 数据库水平切分与垂直切分
tag:
4399、小米、哈啰、阿里count:6
as:mysql 分表方式,订单表是该用水平分还是垂直分。水分平分表的优劣,水平分表后如何连表
分库分表分片键不同怎么保证事务
- 垂直拆分就是要把表按模块划分到不同数据库表中,单表大数据量依然存在性能瓶颈
- 水平切分就是要把一个表按照某种规则把数据划分到不同表或数据库里。
通俗理解:水平拆分行,行数据拆分到不同表中, 垂直拆分列,表数据拆分到不同表中。
# 大数据表如何迁移备份
tag:
4399、字节count:2
as:对于几亿行的表如何迁移备份。(答了停机迁移和双写方案)追问具体步骤,以及备份每一行的操作。
将一个数据库中的增量数据迁移到另一个数据库中,怎么保证数据的一致性。
# 读写分离
tag:
快手、麦米电气count:2
as:怎么实现读写分离
数据库读写分离,数据库怎么做备份
# SQL 执行流程
tag:
字节、知乎、得物、数字马力、众安、小红书、滴滴、三一重工、亚信、浩鲸科技count:13
as:mysql 执行 select 语句进行了什么操作,里面锁处理具体怎么处理的
数据库是怎么解析一条 sql 语句的

连接器:负责建立连接、检查权限、连接超时时间由 wait_timeout 控制,默认 8 小时
查询缓存:会将 SQL 和查询结果以键值对方式进行缓存,修改操作会以表单位导致缓存失效
分析器:词法、语法分析
优化器:决定用哪个索引,决定表的连接顺序等
执行器:根据存储引擎类型,调用存储引擎接口
存储引擎:数据的读写接口,索引、表都在此层实现
# 连接
# 两张表怎么进行连接
tag:
软通、竞技世界count:5
as:内连接,左连接,右连接的区别?
# 为什么不推荐使用 join
tag:
count:1
as:
# 去重复数据
tag:
小米、恒生电子count:2
as:如何去除表中的重复数据行,如果不能新建表要原地删除呢
# SQL 面试题
# 连续登录
# 查询连续登陆 3 天以上的用户
原始数据
| id | date |
|---|---|
| 1 | 2019-01-01 00:00:00 |
| 1 | 2019-01-02 00:00:00 |
| 1 | 2019-01-03 00:00:00 |
| 2 | 2019-02-01 00:00:00 |
| 2 | 2019-02-02 00:00:00 |
| 3 | 2019-03-04 00:00:00 |
| 3 | 2019-03-05 00:00:00 |
| 3 | 2019-03-06 00:00:00 |
| 3 | 2019-03-07 00:00:00 |
先处理日期,再根据 id 和日期分组
select id,substr(date,1,10)as date from test group by id,substr(date,1,10);
使用窗口函数根据 id 分区,日期分组再与 date 列日期增加窗口函数的负数
select id,date,date_add(date,-row_numbert() over(partition by id order by date)as date1 from 上一步结果;
进行 having 过滤
select id,date1,count(*)as day_cnt from 第二步子查询 group by id,date1 having count(*)>3;
# 查询连续登陆最大天数用户
接上面查询连续登陆 3 天以上的用户的第二步结果,查询出每个用户某个开始日期的连续登录天数
select id,date1,count(*) as day_cnt from 第二步子查询 group by id,date1;
根据 id 分组,查询出每个用户连续登录最大天数
select id,max(day_cnt) max_day_cnt from 第三步子查询 group by id;
"连续 - 类题" 的思路:rownumber () over () 减一下,再分组 count
# 用户账户余额大于 1000 的连续天数
# 日期连续
原始数据
第1种场景日期连续
a,小明,2023-01-01,2023-01-03
a,小明,2023-01-04,2023-01-07
b,小明,2023-01-08,2023-01-12
b,小明,2023-01-13,2023-01-16
a,小明,2023-01-17,2023-01-20
输出:
a,小明,01,07
b,小明,08,16
a,小明,17,20
第2种场景日期中间有断
a,小明,2023-01-01,2023-01-03
a,小明,2023-01-05,2023-01-07
b,小明,2023-01-10,2023-01-12
b,小明,2023-01-14,2023-01-16
a,小明,2023-01-18,2023-01-20
输出:
a,小明,01,07
b,小明,10,16
a,小明,18,20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
查询 sql
WITH t1 AS
( SELECT id,NAME,start_date,end_date,
LAG(end_date) OVER (PARTITION BY id, NAME ORDER BY start_date) AS lag_date,
CASE
WHEN DATE_ADD(LAG(end_date) OVER (PARTITION BY id, NAME ORDER BY start_date), INTERVAL 1 DAY) = start_date THEN 0
ELSE 1
END AS new_group_flag
FROM mydb.test_xiaoming ),
t2 AS
( SELECT id,NAME,start_date,end_date,
SUM(new_group_flag) OVER (PARTITION BY id, NAME ORDER BY start_date) AS group_id FROM t1 )
SELECT id,NAME,MIN(start_date) AS start_date,MAX(end_date) AS end_date
FROM t2
GROUP BY id,NAME,group_id
ORDER BY MIN(start_date),id,NAME;
2
3
4
5
6
7
8
9
10
11
12
13
14
15