1. 对MySQL性能优化的理解
MySQL性能优化可以分为四大部分:硬件和操作系统层面、架构设计层面、MySQL程序配置层面、SQL层面。
- 硬件和操作系统层面优化。
硬件层面:影响MySQL性能的因素有CPU、内存、磁盘读写速度、网络带宽等。
操作系统层面:应用文件句柄数、操作系统网络的配置等都会影响MySQL性能。
这部分的优化一般是由DBA或运维工程师完成。 - 架构设计层面优化
MySQL是一个磁盘IO访问量非常频繁的关系型数据库,在高并发和高性能应用场景中,MySQL会承受巨大的并发压力,我们从架构设计层面可以优化一下几个方面:
①搭建MySQL主从集群,单点的MySQL服务容易出现故障,一旦宕机,依赖MySQL的全部应用均无法响应。主从集群或主主集群可以保证服务的高可用。
②读写分离,在读多写少的场景中,通过读写分离的方案,可以避免读写冲突导致的性能影响。
③引入分库分表机制,通过分库可以降低单点的MySQL服务IO压力,通过分表的方式可以降低单表数据量,提成SQL查询效率。
④针对热点数据,引入高效的分布式数据库,如Redis、MongoDB等,他们可以很好的缓解MySQL访问压力,同时还能提升数据检索性能。 - MySQL程序配置优化
结合硬件信息对MySQL程序配置做出响应的优化调整。 - SQL优化
①慢SQL的定位和排查,通过慢查询日志和慢查询日志分析工具得到有问题的SQL列表。
②执行计划分析,针对慢SQL,使用关键字explain来查看当前SQL的执行计划,可以重点关注type、key、rows、filterd等字段,从而定位改SQL执行慢的原因。再做针对性的优化。
③使用show profile工具,该工具可以用来分析当前会话中SQL语句资源消耗情况。可以用于SQL调优的测量。在当前会话中,默认情况下show profile处于关闭状态,打开后保存最近15天的运行结果。
常见的SQL优化原则:
- SQL的查询一定要基于索引进行数据扫描
- 避免索引列上使用函数或运算,这样会造成索引失效
- where字句中like%,应尽量放到最右边
- 使用索引扫描,联合索引中的列从左往右,命中越多越好
- 尽可能使用SQL语句用到索引完成排序,避免使用文件排序的方式
- 查询有效的列信息即可,少用*代替列信息
- 永远用小结果集驱动大结果集
2. MySQL事务的实现原理
MySQL中的事务满足ACID特性。
原子性(Atomicity):需要保证多个DML操作的原子性,要么都成功要么都失败,失败就意味着要对原本执行成功的数据进行回滚。InnoDB设计了一个UNDO_LOG表,在事务执行过程中把修改之前的数据快照保存到UNDO_LOG中,一旦出现错误就直接从UNDO_LOG中读取数据进行反向操作即可。
一致性(Consistency):数据的完整性约束没有被破坏,数据库本省提供了主键唯一约束、字段长度和类型的一些保障等,但是更多的是依赖业务层面的一些保障。
隔离性(Isolation):InnoDB实现了SQL92的一个标准,提供了4中隔离级别的实现,分布是读未提交(RU)、读已提交(RC)、可重复读(RR)、串行化(serializable),默认级别是可重复读(RR),然后使用了MVCC机制去解决了脏读和不可重复读的问题。使用了行锁或表锁解决了幻读的问题
持久性(Durability):InnoDB引入了REDO_LOG文件,改文件存储了数据库变更之后的一个值,当我们通过事务进行数据更改时出了修改内存缓冲区的数据外,还需要把本次修改的值追加到REDO_LOG里面,当时事务提交时,直接把REDO_LOG里面的日志刷新到磁盘中,进行持久化,一旦出现宕机,MySQL重启后可以直接用REDO_LOG中的重写日志读取后再去执行一遍,保证数据的持久性。
事务的实现原理的核心本质就是如何保证事务的ACID特性,用到了MVCC、行锁、表锁、UNDO_LOG、REDO_LOG等机制保证了事务的ACID特性。
3. MySQL的事务隔离级别
首先,事务隔离级别是为了解决多个并行事务竞争导致的数据安全问题的一种规范。
具体来说多个事务竞争可能会产生三种不同的现象。
①两个事务T1/T2同时执行,T1事务有可能会读取到T2事务未提交的数据,但是未提交的事务T2可能会回滚,也就导致了T1事务读取了最终不一定存在的数据,产生了脏读现象。
②两个事务T1/T2同时执行,事务T1在不同的时刻独特同一行数据是的结果可能不一样,从而导致不可重复读的问题。
③两个事务T1/T2同时执行,事务T1执行范围查询或者范围修改过程中,事务T2插入了一条属于事务T1范围内的数据并且提交了,这时候在事务T1查询发现多了一条数据,或者事务T1发现这条数据没有被修改,看起来像是产生了幻觉,这种现象成为幻读。
以上三种现象在实际应用中,可能有些场景不能接受某些现象的存在,所以在SQL标准中定义了四种个隔离级别,分别是:
①读未提交(RU),在这种隔离级别下,可能会产生脏读、不可重复读、幻读。
②读已提交(RC),在这种隔离级别下,可能会产生不可重复读、幻读。
③可重复读(RR),在这种隔离级别下,可能会产生幻读。
④串行化(serializable),在这种隔离级别下,多个并行事务串行化执行,不会产生安全问题。
这四种隔离级别中,只有串行化解决了全部的问题,但也意味着这种隔离级别的性能是最低的。在MySQL中,InnoDB引擎默认的隔离级别是可重复读(RR),因为它需要保证事务ACID特性中的隔离性特征。
4. 对MySQL中的行锁、临键锁、间隙锁的理解
这三种锁都是InnoDB引擎下去解决事务隔离性的一系列排它锁。
行锁:也称为记录锁,当我们针对组件或唯一索引加锁的时候,MySQL默认会对查询的这一行数据增加行锁,避免其他事务对这一行数据进行修改。
间隙锁:锁定一个索引区间,在普通索引或唯一索引的列上,因为索引基于B+树结构存储,默认会存在一个索引的区间。间隙锁就是某个事务对索引列加锁的时候,默认锁定对应索引的左右开区间的一个范围,在基于索引列的范围查询中无论是否唯一索引都会自动触发一个间隙锁,例如使用between查询时。
临键锁:它相当于是行锁+间隙锁的一个组合,它的锁定范围既包含了索引记录也包含了一个索引区间,他会锁定一个左开右闭区间的一个数据范围。
总的来说,这三种表示锁的数据的一个范围,最终目的是解决幻读这个问题。
5. MySQL为什么使用B+Tree作为索引结构
首先,常规的数据库存储引擎,一般都是采用B树或者B+树来实现索引的存储。
因为B树是一种多路平衡树,用这种存储结构来存储大量数据,它的整个高度会比二叉树矮很多。而对于数据库来说,所有的数据必然都是存储在磁盘上的,而磁盘的IO的效率实际上是很低的,特别是在随机磁盘IO的情况下效率更低。所以树的高度能够决定磁盘IO的次数,磁盘IO次数越少,对性能的提升就越大。这也就是为什么采用B树作为索引存储结构的原因。
但是现在MySQL的InnoDB存储引擎起来使用了一种增强的B树结构,就是B+树来作为索引和数据的存储结构。
相较于B树,B+树做了几个方面的优化。B+数据的所有数据都存储在叶子节点,非叶子节点只存储索引;叶子节点中的数据使用双向链表的方式进行关联。
使用B+树实现索引的原因:
①B+树非叶子节点不存储数据,所以每一层能够村塾的索引数量会有增加,意味着B+树在层高相同的情况下村塾的数据量要比B树多,使得磁盘IO次数更少。
②范围查询是MySQL中常用操作,而B+树的所有存在叶子节点的数据都是用了双向链表来关联,所以查询范围的时候只需要查两个节点进行遍历即可,而B树需要获取所有节点,所以B+树在范围查询时效率更高。
③在数据检索方面,B+树的所有数据都存储在叶子节点,IO次数更稳定一些,全局扫描能力更强一些;而B树需要遍历整个树。
④B+树如果采用自增的整形数据作为主键,能更好的避免增加数据的时候,带来的叶子节点分裂导致的大量运算的问题。
总的来说,技术方案选型更多的是解决党项场景的特定问题,并不一定是说B+树就是最好的选择。MongoDB里采用的就是B树结构。