高性能mysql笔记
每个熟悉mysql的开发者,都读过《高性能MySQL》,其是mysql领域中的经典之作,拥有广泛的影响力,第三版更新了大量的内容,每个dba或开发者都有自己的一份<高性能MySQL笔记>。
目录
- 一 架构与历史
- 四、schema与数据类型优化
- 五、查询性能优化
- 28、为什么查询速度慢
- 29、慢查询基础,优化数据访问
- 30、mysql三种方式应用where条件(从好到坏)
- 31、重构复杂的查询
- 32、查询执行基础
- 33、mysql客户端与服务器通信协议
- 34、查询状态
- 35、mysql引擎能处理的优化类型
- 36、查询执行引擎
- 37、mysql关联子查询的局限性
- 38、explain extended 可以查看查询语句被改写成什么样子
- 39、union结果集的排序及limit
- 40、mysql不支持并行执行
- 41、优化count()查询
- 42、快速/简单/精确
- 43、优化关联查询
- 44、优化group by与distinct
- 45、优化limit分页
- 46、延迟关联
- 47、关于SQL_CALC_FOUND_ROWS
- 48、优化union查询
- 49、redis是一个很好的队列容器
- 50、高级特性 - 分区
- 51、高级特性 - 视图
- 52、外键的约束
- 53、mysql内部存储代码 优缺点
- 54、触发器
- 55、字符集
- 56、服务器和客户端通信时 设置:
- 57、校对规则
- 58、全文索引
- 59、执行计划缓存与查询缓存
- 第八章
- 十、复制
- 十四、应用层优化
一 架构与历史
1、并发控制mvvc
读写锁、行锁、表锁
2、事务
acid、死锁、事务日志
3、存储引擎
.frm文件定义数据库表的数据字典
innodb
数据与索引在同一个文件
扩展性好、并发高、数据安全
myisam
数据与索引在两个不同的文件 .myd .myi
不支持事务与行锁
全文索引:myisam 或 innodb+sphinx组合
4、引擎选择
事务、备份、崩溃恢复
日志型应用:myisam 开销低、插入快
订单处理:innodb
聚簇索引 innodb比myisam快,不要迷信 myisam比innodb快
5、数据库大小标准
innodb数据库的数据量达到3~5T,单台机器,系统还可以运行的不错(合理选择物理机器、物理设计、IO规划)
初步估算:假设50张表,每一张表平均100GB的数据量,假设一行记录平均1KB,一张表平均可支持100M条记录(10010241024 = 104857600,达到亿级别)
如果继续增长到10TB以上,需要建立数据仓库。
6、只读测试
mysql5.1 with innodb plugin 简单语句只读测试效果最好,之后5.5 5.6逐步下降
因为是简单sql语句只读测试,所以趋势是效果越来越不好的, 因为新版本的解析越来越复杂,但对于复杂的sql语句效果趋势反而会是越来越好。
四、schema与数据类型优化
1、简单就好
简单数据类型的操作需要更少的cpu周期及其他资源
整型会比字符串操作代价更低,因为字符集及校对规则是字符比整型会更为复杂
2、尽量避免NULL
NULL使得索引和值比较更为复杂
3、大类型优先顺序
数字 => 字符串 => 时间
4、int的宽度
int的宽度对大多数应用是没有意义的,不会限制范围,只是部分mysql交互工具用来显示个数
5、浮点运算
cpu直接支持原生浮点数运算,所以浮点运算明显更快
6、财务精确数据
decimal用于存储财务数据,如果数据量比较大,可以考虑int或bigint代替decimal,避免浮点计算不精确及decimal精确计算代价高的问题
7、varchar什么时候使用比较合适
字符串列的最大长度比平均长度大很多,列的更新少,碎片不是问题的情况下适用;使用了utf-8复杂的字符集。
8、char
适合存储很短的字符串,或者接近同一个长度;对于经常变更的数据,char也比varchar更好,不容易产生碎片。
9、blob与text
仅有的区别是:blob类型存储的是二进制数据,没有排序规则和字符集,而text有
10、选择存储类型考虑什么
选择存储类型时,考虑数据库如何计算和比较
11、整数类型
整数类型通常是标识列最好的选择,因为很快且可以使用auto_increment
12、随机字符串
随机字符串不适合用于标识列,其会导致写入慢、缓存会有很多刷新和不命中
13、mysql schema设计中的陷阱
- 太多的列:极限是几千个字段;
- 太多关联:极限是12个表以内做关联,EAV(实体-属性-值)在mysql下不能靠谱的工作;
- 全能的枚举:避免频繁增加枚举值,每增加一次,就需要alter table一次,对与大表来说是高消耗且不安全;
- 尽量不使用NULL:虽然是视情况而定,但如果没有把握,就使用其他默认空值,避免让事情变得更糟糕;
14、范式与反范式
第一范式:不重复列、不重复行、列不可再分
第二范式:有主键,数据不冗余
第三范式:非主键与主键直接关系,不应存在传递依赖
反范式:适当冗余,适当唯一关联
15、缓存与汇总
原则:更快的读,更慢的写,高频操作快,低频操作慢
16、alter table操作速度
- 大部分alter table将会导致mysql中断
- 大部分修改表的结构,是用新的结构创建一个表,从旧表中查出所有数据,并插入新表
- 数据大时,alter table操作有可能花费数个小时甚至数天
- 如果是修改字段属性,建议使用alter column,这个速度将会很快
17、schema总结
- 尽可能的保持任何东西小而简单总是好的。
- 尽量避免过度设计,导致复杂查询的scheam设计。
- 使用小而简单的合适数据类型
- 除非实际情况需要,否则应该尽可能地避免使用null值。
- 尽量使用相同的数据类型存储相似或相关的值。
- 注意可变长字符串,在排序或临时表时可能导致悲观的按最大长度分配内存。
- 尽量使用整型定义标识列
- 避免使用已废弃特性,如浮点数的精度,整数的显示宽度
- alter table是危险且让人痛苦的操作,大部分情况下会锁表并且重建整张表。
18、索引类型
B-Tree B+Tree
Hash index
全文索引
19、索引的特点
- a、索引大大减少了服务器需要扫描的数据量
- b、索引可以帮助服务器避免排序和临时表
- c、索引可以将随机I/O变为顺序I/O
20、B-Tree
常见B-Tree索引按照顺序存储数据,所以mysql可以用来做order by 和group by操作。
因为有序,且B-Tree也会将相关的列值都存储在一起,因为索引中存储了实际的列值,所以部分查询只使用索引就可完成查询
21、索引是最好的解决方案吗
- 非常小的表,简单全表扫描更高效
- 中到大型表,索引非常之有效
- 特大型表,建议采用分区。建立索引和使用索引的代价将随之增长
22、创建高性能的索引
关键词:
-
独立的列:索引列不能是表达式的一部分,也不能是函数的参数。
-
前缀索引:
-
索引选择性:不重复的索引值和数据表记录总数(#T)的比值(1/#T ~ 1之间),索引的选择性越高查询效率越高。比如唯一索引(比值为1)的选择性是最高的,性能也是最好的。
-
多列的索引:一个常见的错误就是为每个列创建独立的索引,或者按照错误的顺序创建多列索引
多列索引在高版本mysql中会自动采用索引合并方案,and时索引相交,or时索引相并,大概率下都不是很好的方案,尽量只使用一个索引。
-
选择合适的索引列顺序:在一个B-Tree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列。在5.6版本之后mysql将不限制于最左列排序。
-
如何选择索引列的顺序有一个经验法则:将选择性最高的列放到所以最前列。但又通常没有我们想象的那么重要。这个法则只是用于优化where条件的查询。
-
如何定义选择性最高?
通过该条件查询可以快速排除更多的记录或快速定位到部分记录的条件,它的选择性更高。 比如customer_id=2时,总共只有30条记录满足条件,而staff_id=584的时候,共有7992条记录满足条件,则custome_id的选择性比staff_id的选择性更高,所以再创建索引的时候,按经验法则,创建联合索引时,customer_id会在staff_id之前。
23、聚簇索引
mysql内建存储引擎暂时还不支持自定义聚簇索引,在mysql内建引擎上,主键一般都是聚簇索引
24、覆盖索引
覆盖索引,顾名思义,如果一个索引包含了所有需要查询的字段的值,我们就称作为覆盖索引。
如果查询只需要扫描索引而无须扫描表,将极大的减少数据的访问量。
对于IO密集型范围查询会比随机从磁盘读取每一行数据的IO要少的多。
如何判断是否使用了覆盖索引:
同explain的extra列,我们可以看到"using index"的信息,表示查询直接从索引中获取数据,而不用去表数据中查询。
explain解析结果中的extra列和type列两者是完全不同的含义:
extra列的using index表示:使用覆盖索引查询取值。
type列的index表示:使用索引扫描排序。
mysql不能在索引中执行like操作。
25、使用索引扫描来做排序
关于最左前缀,在排序中的使用,假设索引idx_abc(a,b,c),在sql语句中:
select * from table where a=1 order by b,c
由于索引中的第一列a是常量1,是可以满足最左前缀,且order by字句中,都是正向排序,满足索引扫描来做排序。
如果第一列a是范围查询,就需要在order by子句中包含a或ab或abc的正向排序,才能满足索引扫描做排序。
26、重复索引、冗余索引
重复索引没有意义,且加重mysql优化器的检查
冗余索引,经常犯的错是:主键作为联合索引中的二级索引,联合索引的最左索引又单独创建索引
27、索引和锁
InnoDB只有在访问行的时候才会对其加锁,而索引能都减少InnoDB访问的行数,从而减少锁的数量。但只有当InnoDB在存储引擎层能够过滤掉所有不需要的行时才有效。
explain中extra
using index表示,在存储引擎时,直接从索引中查询取值
using where表示,从存储引擎返回行数据回到服务器层后,再应用where过滤条件
五、查询性能优化
28、为什么查询速度慢
查询时一个任务,它由多个子任务组成,有可能存在不必要的子任务,每个子任务会消耗一定的时间,部分子任务可能执行次数太多
29、慢查询基础,优化数据访问
- a、是否向数据库请求了不需要的数据:多余行或多余列(特别是连表查询)
- b、是否需要取出全部列:难以使用覆盖索引,也浪费io及cpu资源
- c、重复查询相同的数据:部分开发经常会犯这样的错误,应该缓存或内存重复查询的数据
- d、是否扫描额外的记录:在确定查询只返回需要的数据以后,接下来要确认查询是否扫描了过多的数据,根据响应时间、扫描行数、返回行数三个指标衡量查询的开销。
- e、响应时间=服务时间+排队时间
- f、扫描行数与返回行数比例一般在1:1和10:1之间,有时这个值也可能非常大
- g、explain中的type反应了访问类型:全表扫描、索引扫描index、范围扫描range、唯一索引查询、常数引用ref等
- h、如果查询没有找到合适的访问类型,那么通常的方法就是增加一个合适的索引
30、mysql三种方式应用where条件(从好到坏)
- a、在索引中使用where条件过滤不匹配的记录。这是在存储引擎层完成的。
- b、使用索引覆盖扫描(using index),直接从索引中过滤不需要的记录并返回命中的结果。在mysql服务器层完成。
- c、从数据表中返回数据,然后过滤不满足条件的记录(using where)。在mysql的服务器层完成。
31、重构复杂的查询
- a、一个复杂的查询还是多个简单的查询。看实际情况。前提是目前mysql链接与断开开销越来越小,网络带宽越来越大,mysql每秒内存扫描百万级数据,运行多个小查询不是大问题。但仍然看实际情况。
- b、切分查询。大查询分而治之,比如一次性删除大量数据,可能会锁住很多数据、占满日志、耗尽资源、阻塞小而重要的查询。可以考虑定期定量删除。(一次性删除10000条数据一般来说是比较高效且影响也最小的做法),定期定量可以分散服务器压力。
- c、分解关联查询。比如常见的文章-标签 关系三张表的join关联查询,可以分解为先查询出标签id,通过标签id查询出所有文章id,通过in查询所有文章id对应文章信息。一方面可以利用应用程序中的缓存机制缓存简单查询数据,提高缓存效率及命中率;查询分解后,可以减少锁的竞争;在应用层做关联,更容易对数据库进行拆分达到高性能及可扩展;in按照id顺序查询;应用层做关联,减少冗余记录的查询;哈希关联在应用层中实现,而不是使用mysql的嵌套循环关联。
32、查询执行基础
- a、查询缓存
- b、sql解析、预处理、优化器优化
- c、执行计划
- d、调用存储引擎api执行查询
- e、返回mysql服务器
- f、返回客户端
33、mysql客户端与服务器通信协议
半双工,即只有客户端或服务器一方才可以发送或推送消息给对方
34、查询状态
通过show all processlist命令可以查看每个mysql连接或说线程,任何时刻都有一个状态。
35、mysql引擎能处理的优化类型
-
a、重新定义关联表的顺序
-
b、将外连接转换为内连接
-
c、使用等价变换规则
-
d、优化count min max等聚合函数
-
e、预估并转化为常数表达
-
f、覆盖索引扫描
-
g、子查询优化
-
h、提前终止查询 limit 或不成立的条件
-
i、in不等同于or的时间复杂度,in在mysql中,会先排序后进行二分查找法,是一个O(logn)的复杂度,而or是一个O(n)的复杂度
-
j、关联查询优化器 ,mysql优化器会智能判断join表的顺序
-
k、排序优化,排序是一个高成本的操作,从性能角度,应尽可能避免排序或避免大数据量的排序
当不能使用索引进行排序时,mysql会在内存或硬盘进行排序(统一称为filesort)using filesort
-
l、limit会在排序之后应用
-
m、关联查询的排序,如果order by的列都来自第一个表,则会using filesort对第一个表进行排序,再关联其他表数据,最后应用limit;
如果order by的列来自多张表,则会得到所有关联数据后,再通过using temporary; using filesort排序,最后应用limit。
36、查询执行引擎
mysql的存储引擎接口只有几十个,但功能却非常丰富;像搭积木一样。接口叠加使用可以达到某一个功能实现。
37、mysql关联子查询的局限性
select * from xxx where xxx in(select id from xxx where xxx=1);
这样的关联子查询不建议的,mysql的执行不是我们想象的先执行子句,再执行外层。
mysql优化器会将相关的外层表压到子查询中,它认为这样可以更高效地查找到数据行,但经过explain我们会发现mysql对外层表进行全表查询后,然后再执行改写后的子查询。
所以如果是in的关联子查询,建议in后不加子查询,而是直接加一个由逗号分隔的列表,比如(1,2,3),in的列表查询本身是很快的;
或者建议使用inner join
38、explain extended 可以查看查询语句被改写成什么样子
show warnings
39、union结果集的排序及limit
union结果集的排序及limit,最好在子句中体现,并最后加上一个全局的排序及limit
如:
(select * from xxx order by name desc limit 20) union all (select * from xxx order by name desc limit 20) order by name limit 20;
效果:生成的临时表最多40条数据,提高查询及排序性能。但数据不一定准确。
40、mysql不支持并行执行
41、优化count()查询
count可以统计行数及统计某个列值的数量(没有给列指定值,将统计不含null值的行数,如果指定值,将统计该列等于该值的行数)
例如:count(type) 与 count(type=1) 第一个统计所有type不为NULL的行数,第二个统计type=1的行数
count(*)并不会像我们猜想的那样扩展成所有的列,它会忽略所有的列而直接统计所有行数。
如果希望知道的是结果集的行数,最好使用count(*),这样写更有意义,性能也会更好。
对于不需要非常精确的统计,建议使用近似值,避免各种条件及distinct等约束条件,避免文件排序,返回结果几乎相同,但查询效率快很多。
42、快速/简单/精确
快速、精确、简单,三者难以完全满足,只能永远满足其二 统计数据,意味着大量存储大量扫描,在mysql层,除了覆盖索引,还不够的话,就要考虑修改应用架构,可以增加汇总表或增加外部缓存系统。
可能很快你就会发现陷入一个熟悉的困境:快速、精确、简单,三者永远只能满足其二,必须舍掉其中一个。
43、优化关联查询
确保on和using字句中的列上使用索引,一般来说,除非有其他理由,否则需要在关联顺序中的第二个表的相应列上创建索引。
如表A和表B用C列关联时,如果优化器的关联顺序是B、A,那么只要A上有索引,表B不需要索引。
确保group by与order by表达式中只涉及到一个表中的列。
44、优化group by与distinct
很多场景下mysql都使用同样的办法优化这两种查询,事实上,mysql优化器会在内部处理的时候相互转化这两类查询,人为最为有效的优化办法仍然是索引优化。
而如果是关联查询group by,确保表达式中只设计到一个表的列。
45、优化limit分页
分页功能通常会使用到limit加上偏移量,同时加上合适的order by字句,如果有对应的索引,通常效率会不错,否则mysql需要大量的文件排序操作。
limit常见的问题是在偏移量非常大的时候,需要查询的记录数很大,但只需要其中10或20条记录,也就是抛弃了前面大部分记录,如limit 1000,20,意味着要查询1020条记录,然后保留最后20条记录。
如果每一页访问频率一样,则查询平均需要访问半个表的数据,要优化这种查询,要么是页面中限制分页数量,要么是优化大偏移量的性能。
建议:优化此类分页查询的一个最简单的办法就是尽可能地使用索引覆盖扫描,而不是查询所有的列表,然后根据需要做一次关联操作再返回所有需要的列。对于大偏移量很大的时候,这样做的效率会提升非常大。
范例:(术语:延迟关联)
select * from film inner join(select id from film order by title limit 500000,5) as film_id using(id)
注:using 等价于 on
另外建议:分页还可以通过指定id等唯一列记录范围,减少不必要的扫描记录
范例: 假设上一次分页查询得到的最后一条记录id为1000,则下一个本次分页查询可以:
select * from film where id>1000 order by id limit 0,20
管理后台的列表分页可以参考借鉴并放大该方案,这个方案可以保证每个页面的查询效率基本一致,不会出现越后面查询越慢的问题。
46、延迟关联
延迟关联主要是借用索引覆盖从索引直接获得id等索引列的值,并关联查询对应的其他列的数据;
更多范例参考索引覆盖
47、关于SQL_CALC_FOUND_ROWS
经常会看到limit中使用SQL_CALC_FOUND_ROWS的关键字来获取所有满足条件的总行数。
mysql在使用该关键字的时候,其实是对所有满足条件的行数进行了扫描,所以该关键字提示的代价是很高的。
建议:更好的设计是将具体的页数换成下一页按钮。另一种是是尝试给出近似值。
需要精确结果时,单独使用count(*) 来满足需求,这时如果能使用索引覆盖扫描则通常也会比SQL_CLAC_FOUND_ROWS快得多。
48、优化union查询
建议:将where limit order by等子句冗余地写一份到各个子查询中,减少临时表数据及扫描。
尽量使用union all,否则mysql会给临时表加上dinstinct关键字,会导致对整个临时表进行唯一查询,代价非常高。
注意:mysql总是将结果放入临时表,然后再读出,再返回给客户端。
49、redis是一个很好的队列容器
redis是一个很好的队列容器,可以结合mysql使用
50、高级特性 - 分区
引入分区给查询优化带来了写新思路,同时也带了新的bug。
另外分区存在很多和索引及列不能优化的问题
建议:分区表是一种粗粒度的、简易的索引策略,适用于大数据量的过滤场景
51、高级特性 - 视图
mysql暂不支持物化视图(将视图结果数据存放在一个可以查看表中,并定期从原始表中刷新数据到这个表中)。
mysql也不支持在视图中创建及使用索引。
如果打算通过视图来提升性能,需要做比较详细的测试。 mysql的视图还不是那么的成熟。
建议:对好几个表的复杂查询,使用视图有时候会大大简化问题,但无法将where压到各个具体的表,也不能使用任何索引,需要特别注意这类查询的性能。
52、外键的约束
InnoDB是mysql唯一支持外键的内置存储引擎。
使用外键是有成本的。比如每次修改数据时,都要在另一张表多执行一次查询操作。
InnoDB外键强制使用索引,如果外键的选择性很低,则会导致一个非常大且选择性低的索引。
外键的主要目的是保证两个相关表的数据一致,不过外键维护操作时逐行进行的,所以这样的更新会比批量删除和更新要慢。
如果只是使用外键做约束,那通常在应用逻辑里实现该约束会更好。
外键会带来很大的额外消耗。
建议:外键是一个确保系统完整性的额外特性,但如果设计的是一个高性能的系统,那么外键就显示臃肿。
53、mysql内部存储代码 优缺点
优点:
- 离数据最近,节省带宽,减少网络延迟
- 代码重用,保证行为一致
- 简化代码的维护与更新
- 提升安全
缺点:
- mysql本身没有好的开发和调试工具
- 存储代码效率不比应用程序高,使用函数有限
- 存储过程会服务器带来额外压力
- mysql本身没有什么选项可以控制存储程序的资源消耗
- 调试是一件很困难的事情
建议:mysql的存储过程并没有别的数据库系统那么的成熟和全面,在某些特定经典场景下,数据库专家可以去实现
54、触发器
- mysql触发器实现非常简单,所以功能也有限;
- mysql只支持基于行的触发,而不是基于sql语句的触发;
- 触发器掩盖了服务器背后的复杂工作;可能一个简单sql语句触发更新另一张表,这个触发可能让这条sql影响的记录数翻倍;
- 触发器出现问题很难排查;
- 触发器可能导致死锁和锁等待
- 使用触发器有可能不能保证数据一致,因为失败时难以回滚
55、字符集
字符集:是一种从二进制编码到某类字符符号的映射
字符校对:是指一组用于某个字符集的排序规则
mysql有很多选项可以控制字符集;
只有基于字符的值才有字符集的概念;
mysql的设置可以分为两类:创建对象时的默认值、在服务器和客户端通信时的设置。
建议:真正存储数据的是列,所以最高阶的设置是列指定默认值,如果列没有设置字符集,表的默认字符集才有用。
建议:字符集是一种字符到字符之间的映射,而校对规则是指一个字符集的排序方法。
56、服务器和客户端通信时 设置:
客户端(sql语句与数据)查询时从client字符集转换为connection字符集
输出结果集时从connection字符集转换为result字符集返回给客户端(查询结果)
SET NAMES 'x'
语句与这三个语句等价:
mysql>SETcharacter_set_client =x;
mysql>SETcharacter_set_results =x;
mysql>SETcharacter_set_connection =x;
57、校对规则
对于校对规则通常需要考虑的一个问题:
是否以大小写敏感的方式比较字符串,或者以字符串二进制值来比较大小。
他们对应的规则前缀是_cs 、 _ci 、 _bin。
58、全文索引
在标准的mysql数据库中,只有myisam支持全文索引,InnoDB在mysql5.6中也只是实验性质地支持全文索引。
即使是myisam支持了全文索引,也仍然存在很多性能、数据崩溃等问题。
建议:使用第三方解决方案,如sphinx,lucene,solr等,或者等mysql5.6正式发布后,直接使用InnoDB的全文索引。
59、执行计划缓存与查询缓存
大部分数据库都可以缓存查询的执行计划,相同类型的sql就可以跳过sql的解析与执行计划生成阶段。
mysql查询缓存,保存查询返回的完整结果。当查询命中该缓存时,mysql立即返回结果,跳过了解析、优化、执行阶段。
当查询中有存在一些不确定的数据时,则不会被缓存。如now count等临时聚集计算。
建议:但大部分情况下,我们还是认为应该默认关闭查询缓存。如果可以的话,可以尝试使用应用级别的缓存。
第八章
60、优化服务器配置
通常只需要把基本的项配置正确,大部分情况下只有很少一些参数是真正重要的,应该将更多的时间花在schema 优化、索引优化以及查询优化上。
61、mysql配置工作原理
实际上mysql服务器启动时,会读取配置文件的内容,删除所有注释和换行,然后和命令选项一起处理。
62、explain扩展
有两种:
explain extended 可以查看优化器优化后的sql语句 并结合show warnings
explain partition 可以查看使用哪个分区
mysql5.6将支持explain非select语句。
-
id列
-
select_type列
mysql将select查询分为简单和复杂查询,复杂可分成:简单子查询、派生表、union查询。
simple:简单
primary:复杂
subquery:不在from子句中的子查询
derived:包含在from子句中的子查询(mysql会递归执行并将结果放到一个临时表中,内部成为派生表)
union:union的第二个和随后的select被标记为union。还有其他更复杂的子句内嵌类型。
union result:用来从union的临时表中检索结果的select标记为union result
dependent:意味select依赖于外层查询中发现的数据意味select依赖于外层查询中发现的数据
-
table列
对应的表名或别名
-
type列
手册说是显示了关联类型,更准确的说法是访问类型,决定mysql如何查找表的行。
ALL:全表扫描。使用limit的话,满足limit条件后将结束扫描。
index:这个和全表扫描一样,只是扫描表时,按索引次序进行而不是行,优点是避免排序,缺点是承担按索引次序读取整个表的开销。
如果在extra 列中看到using index,说明mysql在使用覆盖索引,只是扫描索引的数据,比按索引次序全表扫描的开销要少很多。
range:范围扫描就是一个限制的索引扫描,开始于索引的某个点,返回匹配这个值域的行。
比全索引扫描好一些,用不着遍历全部索引。显而易见的范围扫描常带有between或where子句里带有>的查询。当然in和or也会显示范围查询,但他们其实是不同的访问类型,性能上也有重要差异。
ref:一种索引访问/索引查找,返回所有匹配某个单个值的行。此类索引访问只有当使用非唯一索引才会发生。索引要跟某个参考值相比较,一般这个参考值是一个常数或者是上个前一个表里的结果值。
eq_ref:mysql知道最多返回一条符合条件的记录。在使用主键或唯一索引的查找时看到。mysql对此类访问类型的优化做的非常好,因为它知道无须估计匹配行的范围或在找到匹配行后再继续查找。
const,system:常量。mysql能对某部分查询进行优化将其转换成一个常量时,就会使用该访问类型。如:通过将某一行的主键放入where子句里来选取此行的主键,mysql就把这个查询转换成为一个常量。
NULL:这种访问方式意味着mysql能在优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引。如从一个索引列中选取最小值,可以通过单独查找索引来完成,不需要在执行时访问表。
-
possible_keys列
显示了查询可以使用哪些索引。
-
key列
显示了,mysql决定采用哪个索引来优化对该表的访问。
possible_keys:揭示了哪个索引有助于高效地查找行。
key:显示的是优化采用哪个索引可以最小化查询成本。
-
key_len列
显示mysql在索引里使用的字节数。
mysql如果正在使用的只是索引里的某些列,那么就可以用这个值来算出具体的哪些列。
注意:mysql5.5和之前版本只能使用最左前缀。char在utf8下一个字符占用3个字节,int默认4个字节。
在计算使用哪些列的时候,根据索引中的列占用的字节判断。
-
ref列
显示了之前的表在key列记录的索引中查找值所用的列或常量。(const、null、列名)
-
rows列
显示mysql估计为了找到匹配行要读取的行数。
通过把所有rows列的值相乘,可以粗略估算出整个查询需要检查的行数。
5.0老版本mysql中limit子句需要注意,并不会真的执行rows行。
-
filterred列
在使用explain extended时出现,显示的是针对表里符合某个条件的记录数的百分比所做的一个悲观估算。
-
Extra列
额外信息
Using index:表示mysql使用覆盖索引,避免访问表。注意不要和type index访问类型混淆。
Using where:mysql服务器将在存储引擎检索行后,再进行where过滤。当where中涉及索引中的列,就能被存储引擎检验,不会显示using where,所以不是所有where子句都会显示using where。
Using Temporary:mysql在对结果排序时,会使用一个临时表。
Using Filesort:mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。一般在内存或硬盘完成排序,但不会具体告诉是内存还是硬盘,内存和硬盘的效果是有很大差别的。
十、复制
63、复制是如何工作的
- a、在主库上把数据更改记录到二进制日志中(binary log,二进制日志事件)
- b、备库将主库上的日志复制到自己的中继日志中(replay log)
- c、备库读取中继日志中的事件,将其重放到备库数据之上
复制原理:
-
a、基于语句的复制:实现简单,紧凑不占太多带宽,基本是执行sql语句。但数据更新可能会依赖于其他因素,比如当前时间等一些sql函数时,备库重放时,并不会与主库保持一致。
-
b、基于行的复制:可以高效正确的复制行数据,但有时一条语句更新涉及到多行数据时,开销明显大增,增加记录日志与复制的负载。对于执行库表的修改的schema这样的复制会导致失败。
十四、应用层优化
64、应用层优化
不要使用apache来做静态内容服务
静态内容可以使用nginx或第三方云存储服务实现
使用缓存服务,避免压力渗透到数据库层
65、web服务器寻找最优并发度
对于大流量的网站,web服务器同一时刻处理上千个连接是常见的。然而只有小部分连接需要实时处理。
66、缓存
采用缓存通常可以获得数量级的性能提升。诀窍是找到正确的粒度和缓存过期策略组合。另外也需要决定哪些内容适合缓存,缓存在哪里?
典型的高负载应用会有很多层的缓存,在每个环节都可能产生缓存。越接近于客户端越节省资源并且效率更高。
缓存分成两种方式:
被动缓存:存储和返回缓存结果(有结果或无结果)
主动缓存:比如访问缓存未命中,通用会到数据库等存储媒介获取相应资源数据后生成请求结果,并缓存结果并返回数据给应用。
操作开销:
没有缓存时的开销就是为每个请求生成数据的开销;有缓存时的开销是检查缓存的开销加上缓存不命中的概率乘以生成数据的开销,再加上缓存命中的概率乘以缓存提供的数据的开销。
67、多层缓存
应用层以下的缓存:数据库缓存
@todo 2017