MySQL知识点概括

MySQL 知识点概括

索引:

  • 索引用来快速地寻找那些具有特定值的记录,如果没有索引,一般来说执行查询时会遍历整张表

  • 哈希索引:底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快

    • 优点:查询性能最快

    • 缺点:

      • 不支持范围查询

      • 不支持索引值的排序

      • 不支持联合索引的最左匹配原则

  • B+ Tree索引:大部分场景使用

事务:

  • 定义:事务是逻辑上的一组操作,要么都执行,要么都不执行,保证一组操作要么全部成功,要么全部失败

  • 为什么要有事务:当需要对数据表执行一系列多个操作的情况下,为了防止这些操作中的部分操作执行成功而另一些操作执行失败,从而导致数据不正确,数据不一致,我们就需要使用事务了

  • 事务四大特性:

    1. 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用

    2. 一致性(Consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的

    3. 隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的

    4. 持久性(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性能会明显下降,一些常⻅的优化措施如下

    1. 限定查询数据的范围:使用limit

    2. 分页查询

    3. 读/写分离:主库负责写,从库负责读

    4. 垂直分区

    5. 水平分区

MySQL架构、SQL底层执行原理:

  • MySQL架构分为3层:

    • Client

    • Server(连接器,查询缓存,分析器,优化器,执行器)

      1. 连接器:身份认证和权限相关(登录 MySQL 的时候)

      2. 查询缓存:执行查询语句的时候,会先查询缓存(MySQL会校验这个SQL是否执行过,SQL语句为Key,返回值为Value,如果Key命中则直接返回Value数据给客户端,如果没有则下一步),MySQL8.0之后移除,用处不大

      3. 分析器:没有命中缓存的话,就会进入分析器,检查SQL语法是否正确(词法分析、语法分析)

      4. 优化器:按照MySQL认为最优的方式执行(优化执行过程:如优化最左查询)

      5. 执行器:校验用户有没有执行的权限,有则执行语句,从存储引擎返回数据

    • 存储引擎(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

    1. redo log(重做日志):MySQL的Innodb引擎特有的,且是通过redo log才能支持事务的「持久性」,数据保存到内存中后,记录redo log并进入prepare状态,告诉执行器随时可以提交

    2. 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语句」,需要记录你要回滚的相应日志信息
    例如:

    1. 当delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据

    2. 当update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作

    3. 当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保证:

    1. Innodb redo log写盘,InnoDB事务进入prepare状态

    2. 如果prepare成功,bin log写盘,再继续将事务的日志持久化到bin log

    3. 如果持久化成功,Innodb事务进入commit状态,并在redo log里写一个commit记录

MySQL优化:

  • 索引优化:

    • 建立聚集索引

    • 常查询数据建立索引或组合索引

    • 最左前缀原则

    • 较长数据列建立前缀索引

    • 不要建立无意义的索引

  • 查询优化:

    • Explain进行查询SQL语句分析

    • 优化数据访问(不要使用*尽量使用limit)

  • 重构查询方式:

    • 切分大查询

    • 分解大连接查询

  • 分库分表

Innodb 和 MyISAM 比较:

  1. Innodb支持事务,MyISAM不支持事务

  2. Innodb默认支持行级锁、也支持表级锁,MyISAM只支持表级锁

  3. Innodb底层数据和索引是放在一起存储的,MyISAM底层数据和索引是分开存储的

    • MyISAM:

      • table_name.frm 存储表结构

      • table_name.MYD 存储数据

      • table_name.MYI 存储索引文件

    • Innodb:

      • table_name.ibd 存储数据和索引

      • table_name.frm 存储表结构

  4. 底层索引区别(聚簇索引和非聚簇索引 == 聚集索引和非聚集索引):

    • 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的高度一般都在24层。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索引建立原则:

  1. 定义主键的数据列一定要建立索引

  2. 定义有外键的数据列一定要建立索引

  3. 对于经常查询的数据列最好建立索引

  4. 对于需要在指定范围内的快速或频繁查询的数据列

  5. 经常用在WHERE子句中的数据列

  6. 经常出现在关键字order by、group by、distinct后面的字段,建立索引。如果建立的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,否则索引不会被使用

  7. 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引

  8. 对于定义为text、image和bit的数据类型的列不要建立索引

  9. 对于经常存取的列避免建立索引

MySQL索引优化原则:

  1. 前导模糊查询不能用索引(like %a)

  2. 数据出现隐式转换,比如 1 和 ‘1’,不会用索引

  3. 组合索引只能用最左边的(最左匹配原则)

  4. 被or分割的条件,or前面有索引,后面没索引,也会全表扫描

  5. !=、not in、not like都不会走索引,可以优化为in

  6. 数据库执行计算不会用到索引(where age+1 > 24)

  7. 尽量查询「覆盖索引」,避免回表

  8. 更新频繁不能建立索引(需要频繁更新索引B + Tree)

  9. 区分度不大不能建立索引(sex字段:男/女)

MySQL索引何时失效:

  • 有or时,or的左右必全有索引,否则全盘扫描

  • 遇到范围查找后续索引失效,最左匹配原则

  • like以%开头

  • 触发类型的隐式转换

  • where中索引有运算(+、-)

  • 索引列有函数计算

MySQL的分库分表:

  • 分表:

    • 目的:为了解决单表数据量太大,SQL语句查询数据时,即使走了索引也非常耗时问题。此外还可以解决消耗CPU资源问题

    • 方法:

      • 垂直拆分,按照列拆分

      • 水平拆分,根据id取模(方法:雪花算法),时间,地点分表

  • 分库:

    • 目的:为了解决数据库连接资源不足问题,和磁盘IO的性能瓶颈问题

    • 方法:

      • 主从复制

      • 读写分离

MySQL的主从如何实现同步:

  1. Master主库将此次更新的事件类型,写入到主库的「bin log」文件中

  2. Master创建 log dump 线程通知 slave 需要更新数据

  3. slave 向 Master节点发送请求,将该 bin log 文件内容存到本地的 relay log 中

  4. slave 开启 SQL 线程,读取 relay log 中的内容,将其中的内容在本地重新执行一边,完成主从数据库的同步

SQL 执行顺序:

  1. FROM、JOIN

  2. WHERE

  3. GROUP BY

  4. 聚合函数

  5. HAVING

  6. 窗口函数

  7. SELECT

  8. DISTINCT

  9. UNION、INTERSECT、EXCEPT、MINUS

  10. ORDER BY

  11. OFFSET

  12. LIMIT、FETCH、TOP

猜你喜欢

转载自blog.csdn.net/weixin_44030265/article/details/127801178