MVCC核心思想:
MVCC的实现原理:
1、每一个事务在开启和查询的时候,都需要建立一个ReadView(一致性视图)
#锁
一个事务能够成功地给一张表加上表锁的前提,是没有其他任何一个事务已经锁定了这张表的任何一行数据。
加锁之前为了确定没有加上任意一行数据,需要遍历一张表的数据从头到尾部(全表扫描)。
任何一个事务加行锁的时候,会先加一 个表级别的意向锁,
决定能不能加表锁成功地标志 (意向锁),为了提升加表锁的效率。
MySQL中 行锁解决了不可重复读的问题,gap锁解决了幻读的问题。MySQL中默认使用邻键锁,Next key Lock =record Lock+gap Lock。
由于可重读的隔离级别下,默认采用Next-Key Locks,就是Record lock和gap lock的结合,即除了锁住记录本身,还要再锁住索引之间的间隙,
九、查询速度慢的原因,如何解决?
数据库重启,能够释放资源,比如:连接,以及内存占用的资源
Mysql优化的维度:
(1)连接:增加服务端可用连接数,减少客户端可用连接数
(2)架构方面:减轻数据库的压力:redis缓存
(3)架构方面:集群,基于主从复制的读写分离
(4)架构方面:分库分表:垂直分库(业务主题),水平分库分表
(5) SQL和索引的优化
(6)表结构和存储引擎的优化(不同存储引擎适用于不同的存储引擎,选用能用的最小的结构,行政区号编码,卡号,建表; 表结构分区(天,月,年)商户信息表(基础信息表,联系信息表,结算信息表,附件信息表))
(7)操作系统,MySQL配置参数,操作系统
(8)硬件:升级硬件型号
(9)业务层面的优化,例如:双十一期间,蚂蚁庄园用户饲料发放延迟(降级,用来保证最核心的业务;银行转账只能查看月份,双十一预售(分流 ))
(交易历史表,根据月份,建立12个分区)
十、具体SQL慢查询分析
1、MySQL会开启慢查询日志 slow query log记录,慢查询默认关闭
2,我们通常使用mysqldumpslow工具统计慢SQL信息
3,找到非常慢的SQL信息后,可以使用Explain来分析
Explain中有几个比较重要的字段
Type
对于主键索引或者唯一索引,只能查询一条数据
myisame,memory的查询结果:type的种类为system ;
InnoDB存储引擎的查询结果:type的种类为const
- 我们至少要优化到range级别
- Possible_keys
可能用到的索引,对于联合索引(A,B)来说,有可能两个都用到 - Key
是最终使用的索引(用不到索引,但是优化器对于覆盖索引,索引下推,但是优化器最终优化后直接走索引)。 - Extra 额外的信息
Useing Where存储引擎层返回的数据不全是客户端所需要的,所以需要在server层过滤后再返回客户端
(A,B) Select B返回server层
Using Index Condition:索引条件下推,本来是需要返回server层,但是在某些特殊情况下,下推到存储引擎,
Using filesort: 不能直接用索引排序(order by id; )
Using temporary: 得到最终数据之前,数据需要放到临时表里边存储,再去做相应的过滤操作。 (Distinct字段,Group by操作;需要优化,为字段建立索引,或者联合索引)
对于翻页 Select语句如何优化,
Select * from user_innodb limit 90000,10; 0.3 s
Select * from user_innodb where id>90000 limit 10
SQL优化的目的,是为了用到索引
十一、SQL优化的案例分析
服务端状态分析:
如果出现连接变慢,查询被阻塞,无法获取连接的情况
1,重启!
2,Show processlist查看线程状态,连接数数量,连接时间,状态
3,查看锁的状态
4,Kill有问题的线程
对于具体的慢SQl:
一,分析查询基本情况
涉及到的表的表结构,字段的索引情况,每张表的数据量,查询的业务含义;这个非常重要,因为有的时候你会发现SQL根本没必要这么写,或者表设计有问题。
二,找出慢的原因
1,查看执行计划,分析SQL的执行情况,了解表访问顺序,访问类型,索引,扫描行数等信息。
2,如果总体的时间很长,不确定哪一个影响因素最大,通过条件的递减,顺序的调整,找出慢查询的主要原因,不断地尝试验证。
找到原因:比如是没有走索引引起的,还是关联查询引起的,还是order by引起的。
找到原因之后:
二,对症下药
1,创建索引或者联合索引
2,改写SQL,这里需要平时积累经验,例如:
1)使用小表驱动大表
为什么小表驱动大表比较优呢?
假设有n条记录的id索引,遍历搜寻单值的算法复杂度大概是O(lgn),
设A表记录数m,B表记录数n,m>>n(远大于),两表的id上都有索引。
当A驱动B查询时,复杂度大概是O(mlgn);反之,B驱动A查询时,复杂度大概是O(nlgm)。
最后,比较下mlgn和nlgm的增长趋势,设m=an(a>>1),有anlgn/(nlg(an)) = algn/(an),
最后归结为na和an的量级,显然na>>an(可由无穷大的求导法则推测两者的增长速度),
所以O(mlgn)>O(nlgm),也就是小表驱动大表比较优。
2)用Join来代替子查询
(子查询需要建立临时表)
连接(JOIN)… 之所以更有效率一些,是因为 MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作
3)Not exist 转换为left join is Null
MySQL优化–NOT EXISTS和LEFT JOIN方式差异
两种执行方式对比:
1、从执行计划来看,两个表都使用了索引,区别在于NOT EXISTS使用“DEPENDENT SUBQUERY”方式,而LEFT JOIN使用普通表关联的方式
2、从执行过程来看,LEFT JOIN方式主要消耗Sending data的上,在NOT EXISTS方式主要消耗在"executing"和“Sending data”两项上,受限于PROFILE只能记录100行结果,因此超过57万个"executing"和“Sending data”的组合项没有显示,虽然每次"executing"和“Sending data”的组合项消耗时间较少(约50毫秒),但由于执行次数较高,导致最终执行时间较长(50μs*578436=28921800us=28.92s)
如何在NOT EXISTS和LEFT JOIN中选择:
1、当外层数据较少时,子查询循环次数较少,使用NOT EXISTS并不会导致严重的性能问题,推荐使用NOT EXISTS方式。
2、当外层数据较大时,子查询消耗随外层数据量递增,查询性能较差,推荐使用LEFT JOIN方式
总结:
按照存在即合理是客观唯心主义的理论,NOT EXISTS以更直观地方式实现业务需求,在SQL复杂度上要远低于LEFT JOIN,且在生产执行计划时,NOT EXISTS方式相对更稳定些,LEFT JOIN可能会随统计信息变化而生产不同的执行计划。
4)Or 改为union (or进行全表扫描)
可以用 union 或 union all 代替 or ,以避免因为 or 引起的全表扫描
5)如果结果集允许重复的话,使用Union All代替Union
Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;
Union All:对两个结果集进行并集操作,包括重复行,不进行排序;
因为 UNION ALL 不去重,效率高于 UNION。
6)大偏移的limit,先过滤再排序
对于翻页 Select语句如何优化,
Select * from user_innodb limit 90000,10; 0.3 s
Select * from user_innodb where id>90000 limit 10
小结一下,在上述的执行过程中,造成 limit 大偏移量执行时间变久的原因有:
查询所有列导致回表
limit a, b会查询前a+b条数据,然后丢弃前a条数据
综合上述两个原因,MySQL 花费了大量时间在回表上,而其中a次回表的结果又不会出现在结果集中,这才导致查询时间变得越来越长。
如果SQL语句本身解决不了,就要上升到表结构和架构上
3,表结构(冗余,拆分,not null等)、架构优化(UAN存读写分离分库分表)
业务层面的优化