只要涉及到MySQL数据库,那么其调优一定是必不可少的。往往大家都给具体化为MySQL查询语句及索引
的调优,这样描述是不太准确的,因为MySQL的调优不仅仅是SQL语句,还有硬件升级
、系统配置优化
、表结构涉及优化
、开发规范约定
等 。从这些维度上来说,只要能提高MySQL的性能,我们都可以称之为MySQL调优。
特点:
优化成本:硬件升级>系统配置>表结构设计>SQL语句及索引。
优化效果:硬件升级<系统配置<表结构设计<SQL语句及索引。
硬件升级
从研发之前数据库选型之后,就应该需要对数据库的用途、性能需要、数据量级等指标有个大概的评估,在满足当前需要的基础上尽量选择性能更好的硬件,找好硬件成本与性能需求之间的平衡点。
- 可以使用更好的cpu,更大的内存。
- 可以使用SSD,最大化磁盘IO。
系统配置(innodb)
给MySQL配置足够的内存
大家知道,MySQL是有缓存的,无论是查询缓存,还是pool_buffer里的缓存,都是使用内存,如果内存不够的情况下,就会采取LRU算法来刷盘到磁盘中,读取时就会变慢。
加大 innodb_buffer_pool_size
,使其尽量降低磁盘IO。
mysql> show global status like 'innodb_buffer_pool_pages_%';
+----------------------------------+-------+
| Variable_name | Value |
+----------------------------------+-------+
| Innodb_buffer_pool_pages_data |8190 |
| Innodb_buffer_pool_pages_dirty |0 |
| Innodb_buffer_pool_pages_flushed |12646 |
| Innodb_buffer_pool_pages_free | 0 | 0 表示已经被用光
| Innodb_buffer_pool_pages_misc | 1 |
| Innodb_buffer_pool_pages_total | 8191 |
+----------------------------------+-------+
innodb_buffer_pool_size
默认为128M,理论上可以扩大到内存的3/4或4/5。
修改 my.cnf,然后重启
innodb_buffer_pool_size = 256M
降低磁盘写入次数
- 增大redolog,减少落盘次数。
可以将innodb_log_file_size的大小设置为整个pool_buffer的1/4,即 0.25 * innodb_buffer_pool_size。
通用查询日志可以关闭(慢查询和bin-log必须打开)。
redolog刷盘策略
innodb_flush_log_at_trx_commit
可以适度降低为0或者2(非金融类极度需要安全性的业务场景)。0:默认值。事务提交后,将二进制日志从缓冲写入磁盘,但是不进行刷新操作(fsync()),此时只是写入了操作系统缓冲,若操作系统宕机则会丢失部分二进制日志。
1:事务提交后,将二进制文件写入磁盘并立即执行刷新操作,相当于是同步写入磁盘,不经过操作系统的缓存。
N:每写N次操作系统缓冲就执行一次刷新操作。
将这个参数设为1以上的数值会提高数据库的性能,但同时会伴随数据丢失的风险。
数据库初启动时预热
数据库启动完成后,只有当查询到某条数据时才会进行读取到缓存中,那么我们可以提前预热来让业务请求均使用到缓存。
InnoDB数据库,进行数据预热的脚本是:
SELECT
DISTINCT CONCAT('SELECT ',ndxcollist,' FROM ',db,'.',tb, ' ORDER BY ',ndxcollist,';') SelectQueryToLoadCache
FROM
(
SELECT
engine, table_schema db, table_name tb, index_name,
GROUP_CONCAT(column_name ORDER BY seq_in_index) ndxcollist
FROM
(
SELECT
B.engine, A.table_schema, A.table_name,
A.index_name, A.column_name, A.seq_in_index
FROM
information_schema.statistics A
INNER JOIN (
SELECT engine, table_schema, table_name
FROM information_schema.tables
WHERE engine='InnoDB'
) B USING (table_schema,table_name)
WHERE B.table_schema NOT IN ('information_schema','mysql')
ORDER BY table_schema,table_name,index_name,seq_in_index
) A
GROUP BY table_schema,table_name,index_name
) AA
ORDER BY db,tb;
- 将脚本保存为 preloadmem.sql
- 执行命令:
mysql -uroot -proot -AN < /root/preloadmem.sql > /root/preloadmem.sql
表结构设计优化
非实时数据,设计中间表
对于实时性要求不高的查询数据,比如统计报表,可以每隔一段时间进行统计,写入此表,能节省频繁统计查询导致数据库压力过大。
冗余字段,减少连表查询
需要注意数据一致性问题,多系统之间可以通过MQ等方式来同步。
合理设计字段类型
比如性别可以用tinyint,boolean值可以用bit,尽量设置NOT NULL,大字段单独拆表存储。
主键优化
每张表必须有主键,类型最好为数值型而不是字符型。
SQL语句及索引优化
MySQL的语句优化,一般有以下几个步骤
1.使用explain命令查看索引情况
需要着重关注下type
和extra
字段
type
表示存储引擎查询数据时采用的方式。比较重要的一个属性,通过它可以判断出查询是全表扫描还是基于索引的部分扫描。常用属性值如下,从上至下效率依次增强。**
至少要达到range级别。杜绝出现all级别。
**- ALL:表示全表扫描,性能最差。
- index:表示基于索引的全表扫描,先扫描索引再扫描全表数据。
- range:表示使用索引范围查询。使用>、>=、<、<=、in等等。
- ref:表示使用非唯一索引进行单值查询。
- eq_ref:一般情况下出现在多表join查询,表示前面表的每一个记录,都只能匹配后面表的一行结果。
- const:表示使用主键或唯一索引做等值查询,常量查询。
- NULL:表示不用访问表,速度最快。
Extra
Extra表示很多额外的信息,各种操作会在Extra提示相关信息,常见几种如下:
- Using where 表示查询需要通过索引回表查询数据。(回表查询:通过索引查询主键值,然后再去聚簇索引查询记录信息)
- Using index 表示查询需要通过索引,索引就可以满足所需数据。(索引覆盖:仅在索引树上就可以查询到所需的值,无需回表查询)
- Using fifilesort 表示查询出来的结果需要额外排序,数据量小在内存,大的话在磁盘,因此有Using fifilesort ,建议优化。
- Using temprorary 查询使用到了临时表,一般出现于去重、分组等操作。
2.根据explain结果进行调优
调优手段如下:
select 查询字段尽量少,且指名。最大可能减少回表查询。
查询字段创建索引或组合索引,避免全表扫描。
组合索引是否满足最左匹配原则。
索引过滤性问题,即创建索引的列的数据重复度越低越好。
IN 子句数据值不应太多,该数据会存放到数组中,且需要排序。
巧用limit来停止全表扫描, 比如 limit 1。
大表分页查询优化
分页使用 limit m,n 尽量让m 小,举例如下:limit 1000000, 10;可以找到1000000那条数据的id,然后where id > xxx limit 10;
排序字段增加索引,利用索引天然的有序性,减少内存排序 filesort。
少用
OR
,否则左右两边任一条件字段无索引,均会导致索引失效。尽量用 union all 代替 union
union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,
增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。
不要使用随机函数ORDER BY RAND() ,它不会走索引。
in 和 exists,in适合小表驱动大表,而exists相反。
where子句避免对字段进行函数操作,会导致索引时效。 WHERE age+1 = 30 可以修改为 WHERE age = 30 -1 ;
条件查询字段类型要一致,避免隐式转换。比如 WHERE age = ‘30’ 修改 为 WHERE age = 30;
如果inner join能满足查询业务需求,则尽量使用inner join而不是left join,这种情况下MySQL会自动选择小表作为驱动表,减少嵌套循环次数。join的字段需要创建索引。