这篇就和普通面试题就不一样了,和面试官对着唠、
select distinct < select_ list> 6.
from <left_table> <join_table> 1.
join <right_table> 2.
on <join_condition>
where <where_condition> 3.
group by <group_by_list> 4.
having <having_condition> 5.
order by <order_by_condition> 7.
limit < limit_condition> 8.
以序号为查询顺序
explain + SQL 就可以查看索引的执行计划,我们最主要的关注应该是 type 列 。
type 类型: (从上到下,执行速度)
但是别的列我们也应该关注
SIMPLE 简单的select查询,查询中不包含子查询或者UNION
PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY
SUBQUERY 在SELECT或WHERE列表中包含了子查询
DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中
UNION 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
UNION RESULT 从UNION表获取结果的SELECT
possible_keys 显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。
key:实际使用的索引,如果为NULL,则没有使用索引。
1 Using filesort(九死一生):说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”。
2.Using temporary(十死无生):使用了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by
3. Using index(发财了):表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作
4 Using where:表明使用了where过滤
5 Using join buffer:表明使用了连接缓存,比如说在查询的时候,多表join的次数非常多,那么将配置文件中的缓冲区的join buffer调大一些。
6 impossible where: where子句的值总是false,不能用来获取任何元组
7 select tables optimized away:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
8 distinct : 优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作
索引是关系型数据库为了加速对表中行数据检索的数据结构(磁盘存储)。
一般索引存储的空间也很大。
优点:
缺点:
因为两者数据结构上的差异导致它们的使用场景也不同,哈希索引一般多用于精确的等值查找,B+索引则多用于除了精确的等值查找外的其他查找。在大多数情况下,会选择使用B+树索引。
因为在操作系统在和磁盘交互的时候,交换的基本单位页,一页大小为4KB,在MySQL的数据库中每个数据库页的大小为16KB,因为在操作系统进行缺页中断交互的时候满足空间局部性原则。会有一个预读的过程。所以每个节点的索引数据可以存放更多,大大提升了IO的饱和度和IO的利用率,同时也降低了IO次数。
那在什么情况适合使用B树呢,因为B树的内部节点也可以存储值,所以可以把一些频繁访问的值放在距离根节点比较近的地方,这样就可以提高查询效率。综上所述,B+树的性能更加适合作为数据库的索引。
当两张表联查的时候进行索引分析,将索引建立在另端。(就是比如是 左连接, 会优先去查左表,通过左表的数据,去查右表,因为左表肯定要全查出来,所以索引级别是index ,对索引树进行全查,但是再通过连接条件去查询右表的时候就会很慢,所以要建立在右表 ,对性能上有所提高,所以是在连接的另一端进行建立索引)
同上理 ,索引应该建立在非主表的其余两个子表的连接字段上
聚簇索引和非聚簇索引最主要的区别是数据和索引是否分开存储。
聚簇索引:将数据和索引放到一起存储,索引结构的叶子节点保留了数据行。
非聚簇索引:将数据进和索引分开存储,索引叶子节点存储的是指向数据行的地址。
在InnoDB存储引擎中,默认的索引为B+树索引,利用主键创建的索引为主索引,也是聚簇索引,在主索引之上创建的索引为辅助索引,也是非聚簇索引。为什么说辅助索引是在主索引之上创建的呢,因为辅助索引中的叶子节点存储的是主键。查询的时候先查询到主键的值进行回表查询主键索引数据。
在MyISAM存储引擎中,默认的索引也是B+树索引,但主索引和辅助索引都是非聚簇索引,也就是说索引结构的叶子节点存储的都是一个指向数据行的地址。并且使用辅助索引检索无需访问主键的索引。
上面是说了非聚簇索引的叶子节点存储的是主键,也就是说要先通过非聚簇索引找到主键,再通过聚簇索引找到主键所对应的数据,后面这个再通过聚簇索引找到主键对应的数据的过程就是回表查询,那么非聚簇索引就一定会进行回表查询吗?
答案是不一定的,这里涉及到一个索引覆盖的问题,如果查询的数据再辅助索引上完全能获取到便不需要回表查询。例如有一张表存储着个人信息包括id、name、age等字段。假设聚簇索引是以ID为键值构建的索引,非聚簇索引是以name为键值构建的索引,select id,name from user where name = ‘zhangsan’;这个查询便不需要进行回表查询因为,通过非聚簇索引已经能全部检索出数据,这就是索引覆盖的情况。如果查询语句是这样,select id,name,age from user where name = ‘zhangsan’;则需要进行回表查询,因为通过非聚簇索引不能检索出age的值。那应该如何解决那呢?只需要将索引覆盖即可,建立age和name的联合索引再使用select id,name,age from user where name = ‘zhangsan’;进行查询即可。
所以通过索引覆盖能解决非聚簇索引回表查询的问题。
select * from table_name where a = 1 or b = 3
select * from table_name where a + 1 = 2
select * from table_name where a = '1'
会使用到索引,如果写成select * from table_name where a = 1
则会导致索引失效。select * from table_name where abs(a) = 1
select * from table_name where a != 1
select * from table_name where a is null
索引优化的一般性建议:
口诀:
优化原则:对于MySQL数据库而言,永远都是小表驱动大表。
in和exists一般用于子查询。
ORDER BY子句,尽量使用索引排序,避免使用Using filesort排序。
MySQL支持两种方式的排序,
FileSort
和Index
,Index的效率高,它指MySQL扫描索引本身完成排序。FileSort方式效率较低。
ORDER BY满足两情况,会使用Index方式排序:
结论:尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀原则。
如果不在索引列上,File Sort有两种算法:MySQL就要启动
双路排序算法
和单路排序算法
但是单路排序算法有问题:如果SortBuffer缓冲区太小,导致从磁盘中读取所有的列不能完全保存在SortBuffer缓冲区中,这时候单路复用算法就会出现问题,反而性能不如双路复用算法。
单路复用算法的优化策略:
提高ORDER BY排序的速度:
ORDER BY时使用SELECT *是大忌,查什么字段就写什么字段,这点非常重要。在这里的影响是:
当查询的字段大小总和小于max_length_for_sort_data而且排序字段不是TEXT|BLOB类型时,会使用单路排序算法,否则使用多路排序算法。
两种排序算法的数据都有可能超出sort_buffer缓冲区的容量,超出之后,会创建tmp临时文件进行合并排序,导致多次IO,但是单路排序算法的风险会更大一些,所以要增大sort_buffer_size参数的设置。
尝试提高sort_buffer_size:不管使用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的。
尝试提高max_length_for_sort_data:提高这个参数,会增加用单路排序算法的概率。但是如果设置的太高,数据总容量sort_buffer_size的概率就增大,明显症状是高的磁盘IO活动和低的处理器使用率。
GROUP BY实质是先排序后进行分组,遵照索引建的最佳左前缀。
当无法使用索引列时,会使用Using filesort进行排序,增大max_length_for_sort_data参数的设置和增大sort_buffer_size参数的设置,会提高性能。
WHERE执行顺序高于HAVING,能写在WHERE限定条件里的就不要写在HAVING中了。
当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件。
查看执行计划,看是否有索引,生成最优的执行计划 desc / explain +SQL
当主键自增时候新增数据,只会变动最右子树。
对数据库性能消耗大,维护不方便。
因为重复度高的查询到的也是一个范围,可能会进行多次回表。
由于数据类型不匹配,导致索引失效,变成全局扫描的模式。
最左前置原则:最常用列 > 离散度高的列 > 最少空间
最左前缀匹配:联合索引,会从第一个索引开始等值匹配,知道遇到范围性的字段就开始停止搜索。
索引覆盖:通过索引项的信息可直接返回所需的查询列,则该索引称之为覆盖索引,减少回表操作
CSV存储引擎
特点:
应用场景:
Archive存储引擎
特点:
应用场景:
Memory 存储引擎
特点:
应用场景:
Myisam存储引擎
5.1之前默认的数据库存储引擎
特点:
应用场景:
Innodb存储引擎
特点:
应用场景
无脑选择
上图就是整在存储引擎在访问数据库之前干的事情,因为查询缓存存在很多不好的限制,判断限制 和失效规则 都十分苛刻 所以关闭了缓存在8.0之后。
一条SQL语句查找的时候,首先经过Mysql的服务层也就是解析和优化之后。在Innodb的存储引擎下,进行查找。其中先去Buffer Pool中寻找,未命中才会去数据库中查找。
Buffer Pool用于缓存数据表数据与索引数据,把磁盘上的数据加载到bufferPool ,避免每次都进行磁盘IO,起到加速访问的作用。Buffer Pool 在内存中。
基于空间局部性原理(预读),磁盘访问按照数据库页大小读取到能够提高性能,缓冲池按数据库页(Innodb_page_size) 缓冲数据,所以在BufferPool中,不仅要缓存时间使用到的数据,而且还需要缓存非目标数据和预读的数据
Buffer Pool 的存储结构设计总体要遵循LRU 的特性
Buffer Pool 需要解决两大难题 预读失效 和 缓存池污染
预存失效: 由IO操作的特性将未实际使用的数据加载到了Buffer Pool中缓存了,实际上后续却并未使用
缓存池污染: 可能SQL的执行需要扫描大量的页数据,为了缓存这些页数据,导致缓冲池中大量热点数据替换出去。
一个数据来了后先放在老年带的头部,当被多次访问,而且停留时间达到相应的时间阈值就会到新生带 。
innodb的一次事务的提交过程中,事务的操作并不会马上将变更的数据刷新到磁盘中。
在Innodb中事务的完结只需要将数据的变更记录在Buffer Pool 中形成脏页 ,完成Redo Log记录即可
脏页是指在Buffer Pool 中记录的数据与磁盘中不一致的情况。
刷脏是指刷脏线程将Buffer Pool 的脏页刷新落到磁盘的过程。
所以当用户在执行一项DML事务操作的时候,通常是分为四个步骤
十分像分布式事务中2PC的操作
Redo Log 日志是重做日志,记录的是事务提交过程中数据的变更情况,是物理日志。其主要用于保证数据库服务意外中断,导致脏页未及时刷脏,数据的恢复手段。
而Redo Log Buffer为Redo Log 在内存中日志缓存区,一旦Redo Log Buffer 中的日志内容刷新到磁盘中的Redo Log日志文件中,标志着事务的成功。Redo Log Buffer 中的大小为16M
Redo Log 是一组固定大小的文件,记录日志信息循环使用。
因为在Log Buffer 向磁盘中刷的时候会经过两个过程,一个是用户态,一个是内核态,先刷到用户磁盘上,然后操作系统在刷到盘上文件中。这个操作两个过程不是原子性的。有可能失败
刷盘策略的参数 innodb_flush_log_at_trx_commit 有三个 0,1 ,2
0 : 表示在向用户盘中刷新的时候,以每秒几个事务进行刷新,然后在向文件中写时每一笔事务为单位进行刷盘提交。
2.:表示向用户盘中刷新的时候,以每个事务为单位进行刷新,再向系统磁盘中刷新的时候以每秒几个事务为单位进行刷新
1 :在两个过程中都是每一笔作为单位提交。
所以1是最安全的
Binlog是Mysql Serrver层逻辑过程日志文件。
Binlog以事件的形式记录的所有的DDL,DML语句操作。
Binlog以文件追加的方式进行记录,没有大小的限制。
Binlog是主从复制(搭建集群),和数据恢复的神器。(生产环境开启)
参数主要有三种记录方式
Change Buffer 是一块特殊的数据缓存区。
Change Buffer 是保存唯一索引页的数据变更并且这些数据不存在与Buffer Pool 中的空间。
主要是为了缓存变更记录,当查询操作到达的时候将数据合并加载到Buffer Pool,减少其IO次数。
原子性(Atomicity)
提交:Innodb 事务的提交过程
回滚: Undo Log
一致性(Consistency)
WAL(write ahead logging)日志预写机制
脏页未刷脏 通过Redo Log 来保证数据不会丢失
刷脏出现页断裂情况下, 通过双写机制保证数据的不丢失
隔离性(Isolation)
锁的机制
MVCC机制
持久性(Durability)
Redo Log Redo Log Buffer 刷盘到file的策略
Undo Log 是物理日志,记录的是每个事务的过程中每条数据的变化情况。
Undo Log默认保存在系统的空间表中
Undo Log的设计初衷主要的作用是在事务的异常中断,或主动(rollback)过程中,我们可以基于UndoLog中记录的数据进行数据的回滚,保证原子性的数据回滚
Undo Log 记录了事务过程中各个版本历史,所以事务在版本控制层面,Undo Log数据可以作为数据旧版本快照,可以提供其他并发事务进行快照读。
因为数据库页的大小和操作系统IO每次的操作的页的大小不同,会分多次写入。
多次写入的过程中就会出行断裂,
双写机制就类似与之前Redo Log 写入一样,是两次过程写入,当在双写磁盘中写好之后才会刷脏
MVCC(multiple version concurrent control)是一种控制并发的方法,主要用来提高数据库的并发性能
MVCC
的实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB
通过数据行的 DB_TRX_ID
和 Read View
来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR
找到 undo log
中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View
之前已经提交的修改和该事务本身做的修改
隐藏字段:
在内部,InnoDB
存储引擎为每行数据添加了三个 隐藏字段:
DB_TRX_ID(6字节)
:表示最后一次插入或更新该行的事务 id。DB_ROLL_PTR(7字节)
回滚指针,指向该行的 undo log
。如果该行未被更新,则为空DB_ROW_ID(6字节)
:如果没有设置主键且该表没有唯一非空索引时,InnoDB
会使用该 id 来生成聚簇索引ReadView: 主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”
undo-log:
MVCC
,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log
读取之前的版本数据,以此实现非锁定读不同事务或者相同事务的对同一记录行的修改,会使该记录行的 undo log
成为一条链表,链首就是最新的记录,链尾就是最早的旧记录
脏读: 事务一 读取了事务二未提交的数据
不可重复读 : 在同一个事务中两次读取同一个数据时,因为有别的事务进行了修改导致,两次读取的事务不一致问题
幻读:在同一次事务中两次读取的数据个数不一致 ; 有别的线程进行对其添加
(第一次查询到了一个数据),(另外一个线程进行了新增的操作,第二次查询的时候出现了两个数据,出现了幻读)
MySQL默认的隔离级别是可重复读(repeatable-read),MySQL事务隔离级别有以下四种:
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | innodb中不存在 |
串行化(serializable) | 否 | 否 | 否 |
事务并发访问数据的时,即对数据加锁,阻止其他事务对数据进行操作,利用锁的排他性独占数据的操作限权。
当前读
事务并发访问数据的时候,对正在事务内处理的数据做多版本管理,避免写操作堵塞,从而引发读操作的并发阻塞问题,将数据在当下时间点进行一份数据快照的备份。并用这个快照来提供给其他事务进行一致性读取
快照读 ----》 在undo log 中
使用方式
select * from t_table where id= 1 lock in share mode ---- 加锁
commit / rollback ----------释放
使用方式:
DML 都是加锁
select * from t_table where id=1 for update
提交回滚进行释放
innodb的行锁是通过给索引的索引项加锁实现
SQL的执行基于索引的检查,Innodb 才使用行锁,未使用索引检索的SQL执行Innodb使用表锁
基于辅助索引检索的SQL,辅助索引对应的主键索引都将锁定指定的索引项
注意
DML语句默认加X锁,如果没有索引就会将整个表进行锁住。
意向锁(IX,IS)是Innodb引擎操作数据之前自动加的,不需要用户干预。
意义:当事务想要进行锁表的时候,可以先判断意向锁是否存在,存在的时候可以快速返回,该表不能启动表锁。
SQL 的执行按索引等值匹配过程且能命中数据 ------ 记录锁:实际将某个数据列锁住
SQL的执行按照索引检索但是无法命中数据 --------间隙锁 :锁住数据不存在的区间(左开右开)
SQL的执行使用索引的范围查找方式 -----------临键锁 :锁住命中几率区间和下一个区间(左开右闭)
Record lock:对索引项加锁
Grap lock:对索引之间的“间隙”、第一条记录前的“间隙”或最后一条后的间隙加锁。
Next-key lock:当sql按照索引范围查找的时候,锁住命中几率区间和下一个区间(左开右闭)
默认的临界锁实现导致不能幻读
垂直分表的优势:
避免IO竞争减少锁表的概率。因为大的字段效率更低,第一数据量大,需要的读取时间长。第二,大字段占用的空间更大,单页内存储的行数变少,会使得IO操作增多。
可以更好地提升热门数据的查询效率。
垂直分库的优势:
降低业务中的耦合,方便对不同的业务进行分级管理。
可以提升IO、数据库连接数、解决单机硬件资源的瓶颈问题。
垂直拆分(分库、分表)的缺点:
主键出现冗余,需要管理冗余列
事务的处理变得复杂
仍然存在单表数据量过大的问题
水平分表的优势:
解决了单表数据量过大的问题
避免IO竞争并减少锁表的概率
水平分库的优势:
解决了单库大数据量的瓶颈问题
IO冲突减少,锁的竞争减少,某个数据库出现问题不影响其他数据库(可用性),提高了系统的稳定性和可用性
分片事务一致性难以解决
跨节点JOIN性能差,逻辑会变得复杂
数据扩展难度大,不易维护
在系统设计时应根据业务耦合来确定垂直分库和垂直分表的方案,在数据访问压力不是特别大时应考虑缓存、读写分离等方法,若数据量很大,或持续增长可考虑水平分库分表,水平拆分所涉及的逻辑比较复杂,常见的方案有客户端架构和恶代理架构。
读写分离主要依赖于主从复制,主从复制为读写分离服务。
读写分离的优势:
MySQL复制:为保证主服务器和从服务器的数据一致性,在向主服务器插入数据后,从服务器会自动将主服务器中修改的数据同步过来。
主从复制的原理:
主从复制主要有三个线程:binlog线程,I/O线程,SQL线程。
复制过程:
主从复制的作用: