1. 如何定位慢查询?
慢查询一般发生在联表查询或者表中数据量较大时,当响应时间较长或者压测时间超过2s时,就认为是慢查询。定位慢查询的话一般有两种方法,一种是使用专门的分析工具去定位。另一种也是我们项目中之前使用过的方法:MySQL自带的慢查询日志。想要想要使用自带的慢查询日志功能,需要在MySQL的配置文件中将慢查询日志的开关打开,即对应的字段的值置为1.其次需要给定一个上限值,当SQL语句的执行时常超过该值时,就会在日志中记录信息,记录的信息包括该条语句的执行时长以及这条语句具体的SQL代码。这样就可以准确定位到这条SQL语句从而进行优化。
2. 如果一个SQL语句执行很慢,如何分析?
如果一条SQL语句执行很慢的话,一般就通过在查询语句前添加Explain关键字得到这条SQL语句对应的性能分析表格。其中该表格中有四个关键字段需要关注。首先是key和key_len字段,可以检查这条SQL语句是否命中了索引。如果没有命中,试着分析原因,可以通过添加索引或者其他手段使得其根据索引去查询;其次是type字段可以反应这条SQL还有无优化空间,判断是否出现了全表扫描和全索引扫描;还有一个额外建议的字段反映了这条语句是否出现了回表现象。如果出现了,就通过添加索引或者修改查询语句,避免回表。
3. 什么是索引?
索引是一种数据结构,可以帮助SQL语句查询时提高查询效率。索引类似于我们一本书籍的目录,通过目录可以快速找到对应的数据。同样,通过索引也可以实现数据的高效查询,尤其是在数据量较大时,往往效果更加明显。
4. 索引的底层数据结构?
InnoDB引擎下的MySQL索引的数据结构是通过B+树实现的。B+树相比较于二叉树、二叉搜索树、红黑树等,更加矮胖,就是每一层的节点数更多,但是层数较少。在数据不平衡的情况下二叉树可能退化成链表,这样在查询时,查询效率就比较低。并且二叉树每一个节点只有两个子节点,这样在查询时就会导致效率更低。而B树实现的就是每一个节点可以不仅仅只有两个节点,每一个B树的节点既包含数据也包含下一个节点的指针,如果想要查询的数据在最底层时,B树的效率就比较低,因为每次查询都需要将所有节点的数据读取一次,降低了读取效率。而B+树是B树的变种,B+树相比于B树,只有在叶子节点中才存储数据,在非叶子节点中只存储了子节点的指针。并且在叶子节点中,数据之间都拥有指针,类似一个双向链表,所以比较适合范围查询。当查询到一个数据时,就可以通过指针查找到其他数据,不需要返回根节点重新查询,这样可以大大提高查询的效率和CPU的消耗。
5. 什么是聚集索引,什么是非聚集索引?什么是回表查询?
聚集索引就是指数据与索引放在一块,在B+树的叶子节点中,存储了整行数据(B+树是InnoDB引擎下索引的数据结构),这样的索引有且只能有一个一般为主键,因为全部数据已经与该索引存储在一起了。非聚集索引又叫做二级索引,与聚集索引相反即索引与数据不是存储在一起,而是叶子节点中存储了该行数据对应的主键值,如果想要这个索引对应的其他数据,那么就需要回表查询。这样的索引可以有多个。
回表查询就是指,通过二级索引找到主键值,再返回到聚集索引中去查询完整数据,这个过程就是回表查询。
6. 什么是覆盖索引?
覆盖索引就是指所查询的字段中,可以通过索引全部找到,这样的查询就叫做覆盖索引,并且通过覆盖索引可以避免回表查询,从而提高查询的效率。所以在我们编写SQL语句时,也要尽量做到覆盖索引,比如说使用根据主键值进行查询,避免使用select *。
7. MySQL超大分页怎么处理?
超大分页指的是数据量较大时,使用limit进行分页处理查询,这时需要对数据进行排序,所以效率一般都比较低。此时通过覆盖索引+子查询来解决。比如说,先通过查询满足这一页数据的id值,再去查询这部分id值对应的数据即可完成查询。在这次查询中,查询id值是覆盖索引,所以效率较高,再去进行子查询时,查询的数据量就大大减少,这样可以提高查询的效率。
8. 索引创建原则有哪些?
(1) 针对数据量较大且查询比较频繁的表建立索引;
(2) 针对于常作为查询条件(Where)、排序(Order By)、分组(group by)操作的字段建立索引;
(3) 尽量选择区分度较高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的概率就越大;
(4) 如果是字符串数据,当字符串较长时,可以根据字段的特点,建立前缀索引;
(5) 尽量使用联合索引,减少单列索引,查询时联合索引覆盖索引的概率较大;
(6) 要控制索引,并不是越多越好,因为索引越多意味着维护索引结构所需的代价就越大,会影响增删改的效率;
(7) 如果某一列不能出现NULL值,在创建表示给该列添加Not Null约束,优化器会通过是否包含Null值,优化哪个索引可以更有利于查询。
9. 什么情况下索引会失效?
(1) 违反最左前缀原则;
(2) 范围查询右边的列,索引会失效;
(3) 不要在索引列上进行运算操作,否则索引会失效;
(4) 字符串不加单引号会导致索引失效(实际是发生了类型转换);
(5) 以%开头的Like模糊查询,会导致索引失效。
10. 谈一谈你对SQL的优化经验
首先是表的设计优化,在我们项目过程中,所参考的是阿里的开发手册,比如说用到的优化策略有:在创建字段时,尽量给出满足数据的数值类型,比如说整型:tinyint、int、bigint,还有字符串的选择:char、varchar,char的定长效率更高,varchar变长,效率较低。
SQL语句的优化:尽量避免使用select *;尽量避免造成索引失效的写法;避免对where字段后的条件进行表达式操作;在表连接时,尽量使用内部连接,避免使用左右连接。
主从复制,读写分离:如果数据库的使用场景中,使用读数据的操作较多时,为了避免写的操作所造成的影响,可以采用主从分离的结构进行数据库的设计,这样可以避免由于写操作对读操作造成的性能影响。
索引优化(参考索引创建原则);
分库分表。
11. 事务的特性是什么?
首先解释一下什么是事务。事务是一个不可分割的工作单位,事务会将所有操作作为一个整体,一起向系统提交或者撤销。即要么同时成功,要么同时失败。最经典的一个例子就是指,转账业务。A向B转账500元,当A的余额扣减500时,B的余额就必须增加500,不能增加300也不能保持不变。这也体现了事务的特性,接下来就说一下事务的特性:
原子性:原子性就是指事务是一个不可分割的工作单位,事务中的所有操作要么全部成功要么全部失败。
一致性:指事务完成时,所有数据都必须保持一致的状态,比如说A向B转账500元,当事务结束时,要么就是A减少500并且B增加500,要么就是都不变。
隔离性:隔离性是指一个事务运行时不受外部其他事务的影响。
持久性:事务一旦提交或者回滚,那么他对数据库中数据的改变就是持久性的。
12. 并发事务带来哪些问题?怎么解决这些问题?MySQL的默认隔离级别是什么?
并发事务可能带来的问题包括:脏读、不可重复读、幻读。其中脏读是指,某一个事务A在执行时,另外又有一个事务B并发执行,当B事务将数据库中的某条数据修改后,并未提交事务,此时,事务A读取该数据,就可能读到B事务修改后的数据,可是此时B事务还没有提交。不可重复读是指,当A事务刚开始执行时,读取了数据,B事务对数据进行修改后,提交了事务,那么此时A再次查询该数据时,拿到的结果与上次查询得到的结果不一致了。幻读指的是当事务A查询某条数据时提示数据不存在,就在这时B事务向数据库中写入了这条数据,并且提交了事务(默认此时已经解决了不可重复读问题),此时A向数据库写入数据时,就会写入失败,因为此时数据库中已经有这条数据了,就好像出现了幻觉一般。
通过对事物进行隔离,解决上述问题。其中隔离级别包括四级:读未提交、读已提交、可重复读、串行化,越往后隔离界别越高,但是性能就越低。
可重复读是MySQL的默认隔离级别。
13. undo log 和redo log分别是什么?
先解释一下两个概念,一个是缓冲池一个是数据页。其中缓冲池是主内存中的一个区域,里边可以缓存磁盘上经常操作的数据,这样以后每次增删改对应的数据时,就可以直接操作缓冲池中的数据,减少了磁盘IO的次数,提高了效率,以一定频率刷新磁盘即可。而数据页是innoDB存储引擎磁盘管理的最小单元,每个页的大小默认为16KB,页中存储的是行数据。在一个事务中,数据先在内存结构中写好以后,准备写往磁盘中,但是就在这时服务宕机了,就导致数据丢失,这违背了事务的特性持久性。所以引出了redo log和undo log。
redo log记录的是事务提交时数据页的物理修改,是用来实现事务的持久性的,该日志文件有两部分组成,其中一部分是重做日志缓存,是放在内存结构中,负责记录数据页的物理修改。另一部分叫做日志文件,是放在磁盘中,重做日志缓存会每隔一段时间就去将数据写入日志文件中。这样当刷新脏页到磁盘时,即使发生错误,也可以通过日志缓存文件来恢复数据。
undo log又叫做回滚日志,用于记录数据被修改前的信息,主要作用有两个,一个是提供回滚,另一个是MVCC。比如说,当删除一条数据时,在该日志文件中就会记录一条插入语句,在更新数据时,就会写入一条更新前的数据的写入语句。这样,当发生回滚时,就可以通过undo log来进行数据的回滚。undo log保证了事务的一致性和原子性。
14. 解释一下MVCC?
首先说一下事务中的隔离性是如何保证的:锁:排他锁,如一个事务获取了一个数据行的排他锁,那么其他事物就不能再获取该行的其他锁了;MVCC:多版本并发控制。
其中MVCC是多版本并发控制,是指维护一个数据的多个版本,使得读写操作没有冲突,它的底层实现是分为三个部分:第一个是隐藏字段,第二个是undo log日志,第三个是readview读视图。
隐藏字段是指,在mysql中给每个字段都设置了隐藏字段,一个是记录事务id,记录每一条操作的事务id,是自增的。另一个是回滚指针,应该指向的是上个版本事务版本的记录地址。
undo log主要的作用是记录回滚日志,记录老版本数据,在内部会形成一个版本链,记录不同事务修改数据的版本,通过回滚指针形成一个链表。
readview解决的是一个事务查询选择版本的问题,在内部定义了一些选择应该访问哪个版本的规则,不同隔离级别的快照读是不一样的。如果是RC隔离级别,每一次执行快照读时都会生成readview,而rr隔离级别,只会在事务 第一次执行快照读时生成readview,后续都会复用这个readview。
15. MySQL主从同步原理?
首先解释一下主从同步是什么意思。如果在一个业务中,读取数据库的操作较多时,就建议部署MySQL的主从机制,主数据库负责写数据,而从数据库负责读数据,每次写入数据后,主数据库就需要将数据同步到从数据库。这样可以避免由于写对读造成性能影响。
MySQL的主从同步的核心是二进制日志文件。二进制日志中会记录所有的DDL和DML预计,但不包括查询语句。主数据库在事务提交时,会将所有的数据变更记录写入到日志文件BinLog中。从数据库会有一个专门的IO线程从主数据库中的二进制文件读取内容,写入到从库中的中继日志文件中。从库重做中继日志中的事件,就可以实现与主库数据的同步。
16. 什么是分库分表?
先说一下什么时候就需要分库分表了:当项目业务增多,或者业务发展比较快,当数据量超过1000W条或者20G以后,优化已经解决不了问题的时候,比如说读写分离,索引覆盖等,都无法提升性能时,这个时候就要考虑分库分表了。
首先是垂直分库:以表为单位,将不同业务的表差分到不同的库中。这样可以按照业务对数据进行分级管理、维护、监控、扩展。在高并发下,提高磁盘IO和数据量连接数。
垂直分表:以字段为单位,将不同字段拆分到不同表中。比如说把不常用的字段或者text,blob等大字段拆分出来放在另一张表中。这样就可以做到冷热数据分离。
水平分库:将一个库的数据拆分到不同库中,即在每个库中可能表格都是一样的,但是数据不一样。这样可以解决单裤大数量,高并发的性能瓶颈问题。具体在读取数据时,根据id值对每个库中的数据量进行取模,从而判断出是在哪个库中。
水平分表:将一个表的数据拆分到多个表中,当然这些表是可以在一个库中的。优化单一表数据量过大而导致的性能问题。同样在查询时也可以通过取模的形式进行判断。
分库分别也可能带来一些问题:分布式事务一致性问题,跨节点关联查询、跨节点分页、主键避重等问题。所以要使用一些中间件来帮助我们进行分库分表之后的操作,比如说:Mycat、sharding-sphere。