考虑优化的层面
- 服务器层(优化器,缓存)
- 存储引擎
- schema设计(索引/表)
- 查询优化
服务器层
- 待续…
存储引擎 (详见第二章笔记)
- 尽量使用innodb,以便使用它的行级锁;Myisam是表级锁,锁竞争更频繁。
- 根据场景选择:
场景:
全文索引: InnoDB + Sphinx
在线热备份: InnoDB
记录日志: MyISAM,Archive
10TB以下: InnoDB
10TB以上: InfoBright.
大部分情况优先考虑: Innodb - 待续…
schema设计
表设计(详见第四章笔记)
- 数据类型:
1
2
3
4(1) 更小的通常更好,
int 好于 char (如可以用int存储ip);
date/time/datetime 好于 char.
(2) 尽量Not Null; // 因为NULL导致索引需要额外字节.// 不过这个影响不大 - 考虑使用缓存表,汇总表,计数器表;
- 表设计原则:
1
2
31. 避免太多的列; 服务器层需要缓冲存储引擎层的行,变长的行会引入转换开销.
2. 避免太多关联;
3. 恰当使用NULL. 大多数时候避免NULL列,有时候可以使用.(当异常值不好定义时) - 适当程度使用范式,不迷信三范式.
特定需求可以单独定制相应的缓存表和汇总表,而不改变原有的设计结构. - 待续…
索引设计(详见第五章笔记)
- 不同类型索引适用场景:
1
2
3
41. 哈希索引:(全键值查询,Memory引擎)
数据仓库的查找表。(精确匹配O(1)复杂度,星形,多表连接)
2. B-Tree索引:
大部分场景。
三星系统
索引与查询匹配程度的评价:三星系统:
- 一星: 将查询相关记录放在了一起;(顺序IO,范围查询能利用到索引)
- 二星: 数据顺序与查找顺序一致;(避免排序,让排序也能利用到索引)
- 三星: 索引列包含查询的所有列。(避免二次访问磁盘)
上述评价的对象是(索引,某种查询). 因此并没有一个索引能够完美适应所有查询.只能说尽量适应当前场景下的高频查询.
索引适用场景:
- 小表: 直接扫全表即可;
- 中表: 索引;
- 大表: 分区。索引开销太大。
作为过滤条件的列需要考察的3个属性,按重要性如下:
- 查询频率;
- 相关查询是否是范围查询,或者列的取值是否可枚举。
- 选择性。
设计索引的原则:
- 查询频率高,则应该加入索引;
- 如果是范围查询,则如果作为联合索引的一列,应该放在后面;(因为最左前缀无法利用范围查询后的列)
如果是可枚举列,则查询语句中可以使用In (‘male’,’female’)绕过列顺序限制,让Mysql始终能够应用相关索引; - 满足前两个条件后,应该把选择性高的列放在联合索引的前面。
查询优化(详见第六章)
优化思路
- 尽量减少数据量(提前使用limit);
- 检查响应时间组成;
- 索引相关优化;
- 重写分解复杂查询;
- 如果是日志系统,开启异步写入数据的参数;
- 检查关联表的顺序;
- 相关命令
1
2
3explain extended <sql>;
show warnings;
-- 可以得到重构出的查询。
检查是内排还是外排(
max_length_for_sort_data
控制);排序优化:
关联查询排序时候,尽量把Order by的列提到第一张表中。
( 如果Order by的所有列都来自关联的第1个表,Mysql在关联处理第一个表的时候就会进行排序。)网络传输;
(1)缓冲区大小(SQL_BUFFER_RESULT
);
(2)先释放表锁,再发送结果:SQL_BUFFER_RESULT
。待续…
索引相关
范围查询转化成枚举,充分利用索引;
如索引为(dt,city),查询为:1
2
3
4
5
6
7select dt,city from t
where dt>='2018-01-01' and dt<='2018-01-02'
and city='beijing'
-- 转化为=>
select dt,city from t
where dt in ('2018-01-01','2018-01-02')
and city='beijing'延迟关联+覆盖索引;
检查explain的type:
1
2
3
4
5
6
7
8
9
10
11
12
13const(system): 根据PRI或Unique key,只取出确定的(一行)数据,常量优化.
eq_ref: JOIN条件包括了所有索引,并且索引是Unique key.
ref: JOIN条件包括部分索引或不是Unique key.
ref_or_null: WHERE col=exp or col is null
index_merge: Where id=xx or userid=xxx. 索引合并优化.
unique_subquery: where col in( select id from xxx). 这里id唯一.
index_subquery: 同上,只是id不唯一.
range: 索引在某个范围内.
all: 扫全表检查索引字段是否正确转换了类型:
1
dt = date_format(date_sub(current_date, interval 1 day), '%Y-%m-%d')
待续…
查询中锁的使用
- 尽量使用读锁,避免使用写锁;
- 降低写锁使用时间(先抢注后处理+定时清理占用标志),消除
select ... for update
的使用;(避免显式加锁,优先考虑隐式加锁) - Innodb不同索引对应的锁:
(1) 主键索引: 排他锁(写锁);
(2) 二级索引: 共享锁(读锁)。 - 待续…