1. 1. Table 结构优化
-
表优化
控制单表的长度, 使用 3 大范式拆分表. -
字段优化
- 选用合适的字段类型, 如整数类型的处理比字符串处理开销更小
- 选用何时的字段长度, 固定长度的表会更快
- 尽可能使用not null定义字段
-
索引优化
对于索引而言, 字段长度越小越好, 因为数据库的存储单位是页, 一页中能存下的数据越多越好; 字段离散度越大越好.
离散度可以通过select count(distinct column_name) from table_name查询. count 值越大, 离散度也越大.- 对于频繁查询的列建立索引, 频繁查询的列是指出现在
where,group by,order by,on中的列
- 对于频繁查询的列建立索引, 频繁查询的列是指出现在
-
其他
- 对于会有删除操作的大表, 要定期清理存储空间
2. 2. CRUD SQL 优化
- 使用连接 (join) 代替子查询
- 大的查询可以拆分成多个小的查询, 以便利用查询缓存
- 通过延迟关联优化分页查询
如:1
2
3
4select film_id, description from sakila.film order by title limit 50, 5;
-- 可优化成:
select film.film_id, film.description from sakila.film
inner join (select film_id from sakila.film order by title limit 50, 5) as lim using(film_id);
3. 3. 命令分析
- 查看数据库状态
通过类似于 show [session|global] status like @status_name; 命令查询各种状态.
其中 session 是查询当前会话的状态, 默认值; global 是从 MySQL 启动到现在的状态.
status_name 有:
- com_select: 查询次数
- com_insert: 插入次数
- com_update: 更新次数
- com_delete: 删除次数
- connections: 登录次数
- uptime: 运行时长
- slow_queries: 慢查询次数
- handler_read%: 索引使用情况. handler_read_key: 这个值越高越好, 越高表示使用索引查询到的次数. handler_read_rnd_next: 这个值越高, 说明查询低效.
- 查看数据库参数
通过类似于 show variables like @variables_name; 命令查询各种状态.
- 查看 SQL 执行时间
1 | show variables like 'PROFILING'; |
- 表结构优化建议
以下 SQL 语句会给出每一列的优化建议.
1 | SELECT column_name FROM table_name procedure analyse(); |
- 定期回收闲置的数据库空间
1 | optimize table table_name; |
optimize table的支持表类型: innodb,myisam, archive, ndb; 会重组表数据和索引的物理页, 对于减少所占空间和在访问表时优化io有效果.
对于MyISAM表, 当表上的数据行被删除时, 所占据的磁盘空间并没有立即被回收, 使用命令后这些空间将被回收, 并且对磁盘上的数据行进行重排(注意: 是磁盘上, 而非数据库).
在InnoDB表中等价 ALTER TABLE … FORCE, ALTER TABLE ENGINE= INNODB,会重新整理在聚簇索引上的数据和索引;
只需在批量删除数据行之后, 或定期(每周一次或每月一次)进行一次数据表优化操作即可, 只对那些特定的表运行.
需要有足够的空间才能进行OPTIMIZE TABLE.如果没有磁盘空间, MySQL将不能进行优化, 表也无法使用.
- 查看慢查询
1 | -- 查看慢查询是否开始, 存储文件位置 |
也可以使用 pt-query-digest 工具分析慢查询日志.
pt-query-digest是用于分析mysql慢查询的一个工具, 它可以分析binlog, General log, slowlog, 也可以通过SHOWPROCESSLIST或者通过tcpdump抓取的MySQL协议数据来进行分析.
可以把分析结果输出到文件中, 分析过程是先对查询语句的条件进行参数化, 然后对参数化以后的查询进行分组统计, 统计出各查询的执行时间, 次数, 占比等, 可以借助分析结果找出问题进行优化.
- EXPLAIN 分析查询 SQL
- table: 表名
- type: 连接的类型
- all: 全表扫描
- index: 索引的扫描
如果在 Extra 列看到Using index, 表示正在使用覆盖索引, 只宿醉索引中的数据, 并不按索引扫描整个表. - range: 索引的范围查找
- ref: 索引查找
ref_or_null 是 ref 的变体, 表示需要在初次筛选结果中再次查找出 NULL 条目. - eq_ref: 主键或唯一索引的等值查找, 只会返回一条记录
- const, system: 主键, 索引
- possible_keys: 可能用到的索引
- key: 实际使用的索引
- key_len: 索引的长度, 越短越好
- ref: 索引的哪一列被使用了, 常数较好
- rows: mysql认为必须检查的用来返回请求数据的行数
- extra:
- Using index: 使用覆盖索引
- Using where: MySQL 服务器将在存储引擎检索后再进行过滤
不是所有 where 条件都会显示该值.
有时候该值表示查询将受益于不同的索引 - Using filesort: 需要额外排序
无法按索引次序从表里读出行, 需要在内存或磁盘上进行排序 - Using temporary: 需要使用临时表
会使用内存或磁盘上幅度临时表, 通常用于排序 - Range checked for each record (index map: N): 没有好的索引
- 查看实时 sql 记录
这个文件会随着访问的增加而不断变大, 所以生产环境建议临时开启, 用完及时关闭.
1 | SET GLOBAL general_log = 'ON'; |