如何定位慢查询
- 当发现存在慢查询的时候,首先如果系统中采用了如Skywalking之类的运维工具,那么就可以使用这些运维工具的图形化界面来监控分析接口的运行时长组成,这样就可以发现是不是SQL造成的接口访问缓慢的问题。
- 也可以使用MySQL自带的慢查询日志,这个慢查询日志不是默认开启的,需要在MySQL的配置文件里面手动开启和设置查询超过多少时间就记录,比如说可以设置成2秒,意思就是如果一个SQL语句执行的时间超过2秒就会被记录到日志里面。不过这种方法最好只在调试阶段使用,因为开启这个日志会损耗一定的性能
分析SQL为什么执行得慢
可以查看SQL的执行计划来帮助分析SQL语句执行慢的原因,具体操作就是在Select语句前加上“EXPLAIN”关键字来查看分析。
- 可以关注key和key_len来检查这条语句是否命中索引或者判断索引是否失效,如果出现这些情况的话就可以添加索引或者检查SQL语句是否出错来优化语句。
- 通过type字段来检查是不是还有优化空间,是否存在全盘扫描等情况
- 通过extra来判断是不是出现了回表的情况,如果出现了话可以尝试添加索引或者修改返回字段来优化SQL
索引是什么?
索引是可以帮助数据库高效查询的数据结构,主要用于提高数据查询的效率,降低性能开销。
索引的底层数据结构
MySQL默认存储引擎InnoDB采用的索引数据结构是B+树,原因是B+树查询的路径更短,硬盘读写的开销低,只在叶子结点存储数据,其他节点只存储指针。
B树和B+树的区别
B树在非叶子节点和叶子结点上都会存储数据,而B+树只会在叶子结点上存储数据,在查询的时候B+树的查找效率会更加稳定。 在进行范围查询的时候,因为B+树只在叶子结点存储数据的特点,而所有的叶子结点组成的是一个双向链表,所以存储的效率会更高。
什么是聚簇索引?什么是非聚簇索引?
- 聚簇索引是索引和数据放在一起,B+树的叶子节点存储的是索引和数据,一个表中只能有一个聚簇索引。
- 非聚簇索引是指索引和数据分开存储,B+树的叶子节点保存的是索引和主键,一个表可以有多个非聚簇索引。
什么是回表查询
- 回表查询是跟聚簇索引和非聚簇索引有关系的,回表查询就是通过二级索引找到对应的主键值,然后再通过主键值找到聚集索引中所对应的整行数据,这个操作就是回表。
- 简单来说就是使用索引查询到主键之后,为了获取完整的行数据需要再去原来的表里面通过主键来查找剩下的信息
什么是覆盖索引?
覆盖索引是指使用了索引查询的时候返回的列可以一次在全找到,满足覆盖索引的查询性能会较高,但是有可能会触发回表查询,这样的话性能就比较低了,要避免发生这种情况的话最好在编写SQL语句的时候尽量手动指定要查询的列,避免使用select * 。
超大分页怎么处理?
使用覆盖索引+子查询 先分页查询获取表中的ID,并且对这个ID进行排序,就可以筛选出分页之后的ID集合,因为ID是覆盖索引,因此操作的效率会高很多。最后通过ID集合去到原来的表中再进行关联查询。
索引的创建要注意什么?
- 索引一般建立在需要频繁查询的字段上,索引可以提高查询的效率,但会降低增删改的效率,因为需要动态的维护索引
- 正因为索引需要花性能和空间去维护,所以需要控制索引的数量
- 在单表数据超过10万这个前提才推荐去创建索引,并且最好创建联合索引,并且注意不要在低区分度的字段上创建索引。
- 推荐有唯一性的列作为索引,并且尽量不使用无序的值作为索引
索引失效的情况
- 违反最左前缀法则
- 使用范围查询时,右边的列的索引会失效
- 在索引列上进行运算操作的时候索引会失效
- 条件中字符串不加单引号导致发生类型转换,索引会失效
- 以%号开头的模糊查询索引会失效,但是%号在最后面的模糊查询索引不会失效
SQL优化的经验?
(个人觉得答两三点就够了)
- 首先是表设计的优化,参考阿里开发手册中的数据库表设计规范来进行表设计,主要就是根据具体业务来选择合适的数据类型,比如说存储字符串,根据业务中存储字符串可能出现的长度大小来选择到底是使用varchar还是text类型。
- 索引优化(参考索引的创建)
- SQL语句优化,比如说select语句尽量指明需要查询的字段,不要直接使用
select *,还有就是要注意避免出现可能造成索引失效的写法,比如说聚合查询尽量使用 union all 来代替 union,因为union 会额外多一次过滤,效率会比较低。如果是表关联的话,尽量使用inner join,如果必须要使用左、右连接的话,尽量使用小表来驱动关联查询。 - 还有就是主从复制和读写分离的方面,在生产环境中最好将生产数据库设置多个库来组成集群,提高数据库的可用性,将读、写操作分离到不同的数据库中,可以有效防止写操作阻塞读操作。
- 分库分表,对于一些单表数据达到500万的大表,可以考虑分库分表来提高操作效率,如果数据量较小,或预估数据增长量没有达到必须要分库分表的情况下,最好不进行分库分表操作。
主从同步的原理是什么?
主从同步的核心就是把二进制日志binlog中的内容进行复制
- 主库在提交事务的时候,会把数据变更记录在二进制日志文件binlog中。
- 从库读取主库的binlog,写入到从库的中继日志 Relay Log。
- 从库重做中继日志里面的事件,这样就实现了主从库的同步操作了。
关于分库分表
当单表数据超过1000万数据或大小超过20G才会考虑进行分库分表
分库分表分为垂直拆分和水平拆分
其中垂直拆分分为垂直分库和垂直分表
其中水平拆分分为水平分库和水平分表
- 垂直分库:根据具体业务,按照业务的不同将不同的表拆分到不同的数据库中。
- 垂直分表:以字段为依据,根据字段的属性将不同的字段拆分到不同表中。
拆分规则为:- 把不常用的字段单独放在一张表中。
- 把text,blob等大字段拆分出来放在附表中。
- 水平分库:将一个库中的数据拆分到多个库中,每个库中的数据都不一样,所有库的数据加一起才是一个业务的完整的数据库。应用需要按照一定的路由规则来访问正确的数据库获取正确的信息,一般来说按照ID的范围来判定需要查找哪个数据库。
- 水平分表:将一个表中的数据拆分到多个表中,可以在同一个库内进行操作,应用也需要通过一定的路由规则来访问正确的表以获得正确的数据。
事务的特性
事务的特性有四个,ACID,也就是原子性,一致性,隔离性,持久性。
- 原子性指一个事物不可以再分割,事务中的操作要么全部成功,要么全部失败。
- 一致性指事务完成之后必须要让所有的数据都保持一致的状态。
- 隔离性指事务不受数据库外界并发操作的影响,保证事务在独立环境下运行。
- 持久性指事务一旦提交或者回滚,对数据库中数据的改变是永久的。
例如转账操作,A向B转账100块钱,A扣除100块钱,B增加100块钱,这两个操作要么全成功,要么全失败,体现原子性。在转账过程中,数据的一致性可以举例为A扣了100块钱,B就必须增加100块钱,而这个A向B转账的操作不能被其他事务干扰,必须是独立运行的,体现了独立性,而持久性的话,在事务提交之后,A和B的数据在数据库中都是被持久化保存下来了的。
并发事务的问题
- 脏读:一个事务读取到另外一个事务还未提交的数据。
- 不可重复读:一个事务先后读取同一条数据,但两次读取结果的数据不同
- 幻读:一个事务按照条件查询数据时,查找不到对应的数据,但是在插入数据时发现这一行数据已经存在。
事务隔离级别
事务的隔离级别一共有四种
- 未提交读 (可能出现脏读、不可重复读、幻读)
- 读已提交 (可能会出现不可重复读、幻读)
- 可重复读 (可能会出现幻读,是MySQL默认的事务隔离级别)
- 串行化 (不会出现并发事务问题,但性能最差)
undo log和redo log的区别?
- redo log记录的是数据页的物理变化,如果服务宕机可以使用它来同步数据。
- undo log记录的是逻辑日志,当事务回滚的时候通过逆操作来恢复原来的数据
redo log保证了事务的持久性,undo log保证了事务的原子性和一致性
MVCC相关
MVCC是MySQL中的多版本并发控制,用于保证事务中的隔离性。 MVCC维护一个数据的多个版本,使得读写操作没有冲突。 MVCC主要有以下三个主要内容:
- 隐藏字段:
- trx_id(事务id),用于记录每一次操作的事务id,这个字段是自增的。
- roll_pointer(回滚指针),这个指针指向上一个版本的事务版本记录。
- undo log:
- 回滚日志,用于存储老版本数据。
- 版本链:用于在多个事务并行操作某一行记录的时候,记录不同事务修改数据的版本,通过回滚指针形成一个链表,链表尾存着的是最早的版本,链表头存着的是最新的版本。
- readView(读视图):
- 根据读视图的匹配规则和当前的事务id判断应该要访问哪个版本的数据。
- 不同的隔离级别快照读是不一样的,最终的访问结构不一样 RC:每一次执行快照读的时候生成ReadView RR:仅在事务中第一次执行快照读的时候生成ReadView,后续复用这个ReadView。
