MySQL 知识点概括
索引:
-
索引用来快速地寻找那些具有特定值的记录,如果没有索引,一般来说执行查询时会遍历整张表
-
哈希索引:底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快
-
优点:查询性能最快
-
缺点:
-
不支持范围查询
-
不支持索引值的排序
-
不支持联合索引的最左匹配原则
-
-
-
B+ Tree索引:大部分场景使用
事务:
-
定义:事务是逻辑上的一组操作,要么都执行,要么都不执行,保证一组操作要么全部成功,要么全部失败
-
为什么要有事务:当需要对数据表执行一系列多个操作的情况下,为了防止这些操作中的部分操作执行成功而另一些操作执行失败,从而导致数据不正确,数据不一致,我们就需要使用事务了
-
事务四大特性:
-
原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用
-
一致性(Consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的
-
隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
-
持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响
-
并发事务的四大问题:
-
脏读:A事务读取了B事务在修改数据后未提交事务之前的数据,并且B事务回滚,也就是A事务读取到了未提交的数据
-
不可重复读:A事务先读取a变量后,B事务将a变量修改后提交事务,此时A事务在提交之前再次读取a变量,前后两次读的a值不同
-
幻读:A事务读取到B事务已经提交的新增数据,比如A事务先查询a表的行数,B事务插入一条数据给a表,此时A事务在提交之前再次查询a表的行数,前后读到的a表行数不同,相比于不可重复读是数据被修改,幻读在乎的是数据的新增
-
丢失修改:指在A事务读取数据a时,B事务也访问了数据a,那么在A事务中修改了数据a后,B事务也修改了数据a。这样第A事务内的修改结果就被丢失,因此称为丢失修改
隔离级别:
-
读未提交:最低级别,任何情况都会发生
-
读已提交:避免脏读
-
可重复读:避免脏读、不可重复读(MySQL默认级别)
-
串行化:避免脏读、不可重复读、幻读
锁机制:
-
MyISAM :采用表级锁
-
InnoDB:支持行级锁和表级锁,默认为行级锁
-
表级锁和行级锁对比:
-
表级锁: MySQL中锁定「粒度最大」的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁
-
行级锁: MySQL中锁定「粒度最小」的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁,仅InnoDB引擎支持行锁
-
MVCC:
-
MVCC 多版本并发控制
-
目的:提高数据库并发场景下的吞吐性能
-
MVCC协议下,每个读操作会看到一个一致性的快照,「这个快照是基于整个库的」,并且实现非阻塞的读取,用于支持「读已提交」和「可重复读」隔离级别的实现
-
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常⻅的优化措施如下
-
限定查询数据的范围:使用limit
-
分页查询
-
读/写分离:主库负责写,从库负责读
-
垂直分区
-
水平分区
-
MySQL架构、SQL底层执行原理:
-
MySQL架构分为3层:
-
Client
-
Server(连接器,查询缓存,分析器,优化器,执行器)
-
连接器:身份认证和权限相关(登录 MySQL 的时候)
-
查询缓存:执行查询语句的时候,会先查询缓存(MySQL会校验这个SQL是否执行过,SQL语句为Key,返回值为Value,如果Key命中则直接返回Value数据给客户端,如果没有则下一步),MySQL8.0之后移除,用处不大
-
分析器:没有命中缓存的话,就会进入分析器,检查SQL语法是否正确(词法分析、语法分析)
-
优化器:按照MySQL认为最优的方式执行(优化执行过程:如优化最左查询)
-
执行器:校验用户有没有执行的权限,有则执行语句,从存储引擎返回数据
-
-
存储引擎(Innodb、MyISAM)
-
MySQL日志种类:
-
bin log(归档日志)归属于MySQL架构的Server层,记录了所有的 DDL 和 DML 语句以及语句所执行的消耗的时间
-
redo log(前滚日志)实现持久性,归属于MySQL架构的存储引擎层,Innodb独有
-
undo log(回滚日志)实现原子性,归属于MySQL架构的存储引擎层,Innodb独有
-
relay log 主从复制
-
error log(错误日志)
MySQL中redo log/bin log:
-
提及数据时先写bin log,后写redo log
-
redo log(重做日志):MySQL的Innodb引擎特有的,且是通过redo log才能支持事务的「持久性」,数据保存到内存中后,记录redo log并进入prepare状态,告诉执行器随时可以提交
-
bin log(归档日志):MySQL真实记录进数据的日志,收到上述redo log进入prepare状态的通知后,提交redo log为commit状态
-
-
SQL 等执行过程分为两类:
-
查询过程如下:权限校验 -> 查询缓存 -> 分析器 -> 优化器 -> 权限校验 -> 执行器 -> 引擎
-
更新过程如下:分析器 -> 权限校验 -> 执行器 -> 引擎 -> redo log prepare -> binlog -> redo log commit
-
MySQL中undo log(回滚日志)实现原子性 A:
-
原子性:事务中的所有操作作为一个原子,要么成功要么失败
-
undo log是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用undo log来实现多版本并发控制(MVCC),操作数据之前,首先将数据备份到 undo log,然后进行对数据的修改,如果出现了错误或者用户执行ROLLBACK语句,系统可以利用undo log中的备份数据恢复到事务开始前的状态
-
undo log为回滚日志,是实现原子性的关键,当事务回滚时能够「撤销」所有「已经成功执行的SQL语句」,需要记录你要回滚的相应日志信息
例如:-
当delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
-
当update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
-
当insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作
-
-
undo log记录了这些回滚「需要的信息」,当事务「执行失败」或调用了「rollback」,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子
MySQL中ACD共同实现一致性 C:
-
一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的
-
原子性、持久性、隔离性一起共同实现一致性
MySQL中锁实现隔离性 I:
-
隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
-
隔离性是通过MySQL锁机制和MVCC(多版本并发控制)实现的
MySQL中redo log(前滚日志)实现持久性 D:
-
持久性:事务一旦提交,其对数据库的更新就是持久的,任何事务或系统故障都不会导致数据丢失(内存中的数据会丢失,持久化到磁盘就不会丢失了)
-
MySQL先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上,如果刷回的过程中宕机了,内存中的数据会丢失
-
采用「redo log」解决此问题,当事务中做数据修改时,不仅是在内存中操作,还会在「redo log」中记录此次操作。当事务提交的时候,会将「redo log」日志刷入磁盘(「redo log」一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将「redo log」中的内容恢复到数据库中,再根据「undo log」和「bin log」内容决定回滚数据还是提交数据
-
持久性由内存 + redo log保证:
-
Innodb redo log写盘,InnoDB事务进入prepare状态
-
如果prepare成功,bin log写盘,再继续将事务的日志持久化到bin log
-
如果持久化成功,Innodb事务进入commit状态,并在redo log里写一个commit记录
-
MySQL优化:
-
索引优化:
-
建立聚集索引
-
常查询数据建立索引或组合索引
-
最左前缀原则
-
较长数据列建立前缀索引
-
不要建立无意义的索引
-
-
查询优化:
-
Explain进行查询SQL语句分析
-
优化数据访问(不要使用*尽量使用limit)
-
-
重构查询方式:
-
切分大查询
-
分解大连接查询
-
-
分库分表
Innodb 和 MyISAM 比较:
-
Innodb支持事务,MyISAM不支持事务
-
Innodb默认支持行级锁、也支持表级锁,MyISAM只支持表级锁
-
Innodb底层数据和索引是放在一起存储的,MyISAM底层数据和索引是分开存储的
-
MyISAM:
-
table_name.frm 存储表结构
-
table_name.MYD 存储数据
-
table_name.MYI 存储索引文件
-
-
Innodb:
-
table_name.ibd 存储数据和索引
-
table_name.frm 存储表结构
-
-
-
底层索引区别(聚簇索引和非聚簇索引 == 聚集索引和非聚集索引):
-
Innodb底层索引为聚簇索引和非聚簇索引(主键索引的 B+ Tree 为聚簇索引,叶子结点为Key主键和Value数据本身,Innodb的索引文件和数据文件在一起,辅助索引的 B+ Tree 叶子结点Value为主键,Innodb必须有主键,也就是必须含有主键索引)
-
MyISAM底层索引只有非聚簇索引(B+ Tree叶子结点的Key为主键,Value为数据的地址,因为MyISAM的索引文件和数据文件是分开的,MyISAM主键索引和辅助索引没有任何区别)
-
聚簇索引和非聚簇索引区别:
-
无论有多少个索引,数据只存储一份
-
聚簇索引和非聚簇索引不是单独的索引类型,而是一种数据存储的方式
-
Innodb中索引的组织为B+ Tree,非叶子结点存放Key,叶子结点存放Key + Data,叶子结点之间通过指针联通
-
聚簇索引:叶子结点的Key为主键,Value存放的是 当前行的数据(总结:数据和索引放在一起)
-
非聚簇索引:叶子结点的Value存放的是主键的值,得到主键之后还需要回表,回到聚簇索引再查询一次,才能查到数据(总结:数据和索引分离开)
-
B Tree和B+ Tree的区别:
-
B Tree叶子结点和非叶子结点都存在(主键、主键对应的索引、数据),B+ Tree非叶子结点只有主键对应的索引,叶子结点包括(Key:主键、Value:数据)
-
B+ Tree每个节点可以包含更多的节点,因为非叶子结点只有主键对应的索引,这样做第一是降低树的高度、第二是将数据范围变成多个区间。叶子结点两两指针相互连接,加快顺序查找速度。因此 B+ Tree 比 B Tree更加「矮胖」
-
B Tree每次查询的深度不一定一致,而B+ Tree每次查询的深度都是Tree的高度
回表、索引覆盖、最左匹配原则、索引下推:
-
回表:针对「普通索引」来说的,先对普通索引的B+ Tree进行搜索,由于普通索引(非聚簇索引)的B+ Tree叶子结点的Value值为主键,然后再次通过主键索引(聚簇索引)的B+ Tree进行搜索,匹配叶子结点相同的Key主键值,获取Value数据值,总共需要一次非聚簇索引和一次聚簇索引的搜索,这个过程叫做回表
-
索引覆盖:针对「普通索引」来说的,查找的列刚好是主键,所以对普通索引的B+ Tree第一次搜索后,就获得了所需要的主键的值,也就不需要回表了。实际上就是想要查出的数据,刚好在非聚簇索引的叶子节点上都存在
-
最左匹配原则:针对「组合索引」来说的,例如有索引(a,b,c,d)查询条件:a=1 and b=2 and c>3 and d=4,则会在每个节点中依次命中a,b,c但是不会命中d,先匹配最左边的,索引只能用于查找key是否存在 / 相等,遇到范围查找(>、<、between、like左匹配)等就不能再进一步匹配了,后续退化为线性查找「全盘扫描」
-
索引下推:数据返回server的时候,数据引擎就将where name =1 和 age= 18的数据进行了过滤。在版本5.7之前的数据引擎先过滤掉name =1的数据返回,在server处过滤age=18的数据。相比较之前的返回,返回数据少,减少IO
联合索引(组合索引)底层实现:
-
联合索引底层还是使用B+ Tree索引,并且还是只有一颗树,只是此时的排序会首先按照第一个索引排序,在第一个索引相同的情况下,再按第二个索引排序,以此类推
-
最左匹配原则:针对于组合索引,因为右边的索引都是在左边的索引的排序基础上进行排序的,如果没有左边的索引,单独看右边的索引,其实是无序的
MySQL的基本索引类型:
-
单列索引:
-
普通索引:允许被索引的数据列包含重复值和空值
-
唯一索引:索引列中的值必须唯一,但允许有空值
-
主键索引:一种特殊的唯一性索引,不允许有空值,一张表只能有一个主键索引
-
-
组合索引:索引可以覆盖多个数据列,只有在查询条件中使用了这些字段的左边字段时,索引才会生效。使用组合索引时遵循「最左匹配原则」
-
全文索引:只有MyISAM存储引擎才能使用,且只能在char、varchar、text类型字段上使用,可以通过某个关键字,查询到该字段所属的记录行。底层通过建立倒排索引,可以极大的提升查询效率
drop、delete、truncate分别在什么场景使用:
-
不再需要一张表的时候,用drop
-
想删除部分数据行时候,用delete,并且带上where子句
-
保留表而删除所有数据的时候用truncate
数据库的三大范式:
-
高级别范式的依赖于低级别的范式,1NF 是最低级别的范式
-
第一范式(1NF):属性不可分
-
第二范式(2NF):非主属性完全函数依赖于键码(消除非主属性键码的部分函数依赖)
-
第三范式(3NF):非主属性不传递函数依赖于键码(消除非主属性对键码的间接传递依赖)
-
B+ Tree一个节点多大合适:
-
一个节点为「1页或者页的倍数」最为合适,因为如果一个节点的大小小于1页,也会读出1页,所以应该为整数倍
-
这里页指的是 MySQL 自定义的单位,Innodb引擎中一页的默认大小为16kb,也就是一个节点为16kb
-
Innodb中的B+ Tree一般为3层时,就能满足千万级存储,查找数据时,一页的查找代表一次IO,所以通过主键索引查询通常只需要1-3次IO即可查找数据
B+ Tree优势:
-
InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗3)。也就是说一个深度为3的B+Tree索引可以维护103 * 10^3 * 10^3 = 10亿 条记录
-
实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2
4层。mysql的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要13次磁盘I/O操作
Explain关注的字段:
-
type:表的连接类型
-
key:实际使用到的索引
-
row:估计要检查的行数
-
extra:附加信息
慢SQL查询优化:
-
使用Explain分析语句的执行计划
-
查看索引的使用情况
-
查看是否加载了额外的列
-
查看是否表中数据量过大,可考虑「垂直拆分」或者「水平拆分」
-
union 和 union all的区别:
-
Union会求两张表的并集,过滤掉有重复的,并且会自动排序
-
Union All会求两张表的并集,并且可能会有重复的,并且不会排序
count(1) 和 count(*) 和 count(列名)的区别:
-
count( *) 和 count(1) 都是将返回表格中所有存在的行的总数包括值为 null 的行
-
count(列名) 将返回表格中除去 null 以外的所有行的总数
-
有主键或联合主键的情况下,count( *) 比 count(1) 快一些
-
没有主键的情况下 count(1) 比 count( *) 快一些
where 和 on 的区别:
-
使用where关联两表会先对两表进行笛卡尔积操作,然后根据过滤条件过滤出结果数据
-
使用on关联两表是直接对于on的字段进行相互关联操作,效率更高
为什么推荐使用自增 id 作为主键:
-
聚集索引和非聚集索引的 B+ 树的叶子节点的 Key/Value 值存放的是主键的值,所以如果主键值较大,会导致索引的存储空间较大
-
使用自增 id 做主键索引新插入的数据只要放在该页的最尾端就可以,直接「按照顺序插入」,不用刻意维护
-
页分裂容易维护,当插入数据的当前页快满时,会发生页分裂的现象,如果主键索引不为自增 id,那么数据就有可能会从页的中间插入,页的数据频繁变动
MySQL索引建立原则:
-
定义主键的数据列一定要建立索引
-
定义有外键的数据列一定要建立索引
-
对于经常查询的数据列最好建立索引
-
对于需要在指定范围内的快速或频繁查询的数据列
-
经常用在WHERE子句中的数据列
-
经常出现在关键字order by、group by、distinct后面的字段,建立索引。如果建立的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,否则索引不会被使用
-
对于那些查询中很少涉及的列,重复值比较多的列不要建立索引
-
对于定义为text、image和bit的数据类型的列不要建立索引
-
对于经常存取的列避免建立索引
MySQL索引优化原则:
-
前导模糊查询不能用索引(like %a)
-
数据出现隐式转换,比如 1 和 ‘1’,不会用索引
-
组合索引只能用最左边的(最左匹配原则)
-
被or分割的条件,or前面有索引,后面没索引,也会全表扫描
-
!=、not in、not like都不会走索引,可以优化为in
-
数据库执行计算不会用到索引(where age+1 > 24)
-
尽量查询「覆盖索引」,避免回表
-
更新频繁不能建立索引(需要频繁更新索引B + Tree)
-
区分度不大不能建立索引(sex字段:男/女)
MySQL索引何时失效:
-
有or时,or的左右必全有索引,否则全盘扫描
-
遇到范围查找后续索引失效,最左匹配原则
-
like以%开头
-
触发类型的隐式转换
-
where中索引有运算(+、-)
-
索引列有函数计算
MySQL的分库分表:
-
分表:
-
目的:为了解决单表数据量太大,SQL语句查询数据时,即使走了索引也非常耗时问题。此外还可以解决消耗CPU资源问题
-
方法:
-
垂直拆分,按照列拆分
-
水平拆分,根据id取模(方法:雪花算法),时间,地点分表
-
-
-
分库:
-
目的:为了解决数据库连接资源不足问题,和磁盘IO的性能瓶颈问题
-
方法:
-
主从复制
-
读写分离
-
-
MySQL的主从如何实现同步:
-
Master主库将此次更新的事件类型,写入到主库的「bin log」文件中
-
Master创建 log dump 线程通知 slave 需要更新数据
-
slave 向 Master节点发送请求,将该 bin log 文件内容存到本地的 relay log 中
-
slave 开启 SQL 线程,读取 relay log 中的内容,将其中的内容在本地重新执行一边,完成主从数据库的同步
SQL 执行顺序:
-
FROM、JOIN
-
WHERE
-
GROUP BY
-
聚合函数
-
HAVING
-
窗口函数
-
SELECT
-
DISTINCT
-
UNION、INTERSECT、EXCEPT、MINUS
-
ORDER BY
-
OFFSET
-
LIMIT、FETCH、TOP