mysql优化要从以下四方面考虑:
- 数据表设计合理
- 表设计要符合三范式(规范的模式),有时需要适当的逆范式。
- SQL语句的优化
- 数据库的配置
- 适当的硬件配置和操作系统
以上四点顺序也表现了对性能影响的大小。本文主要针对前两点进行总结。
一、数据表设计
首先来介绍下三范式和逆范式。
三范式
- 第一范式(1NF):具有原子性,不可分割。只要使用关系型数据库,默认就符合。
- 第二范式(2NF):在满足1NF的基础上,才可以考虑是否满足2NF。只要表的记录满足唯一性,即同一张表中,不可能出现完全相同的记录。一般来说,在表设计时,设置主键即可。
- 第三范式(3NF):在满足2NF的基础上,才可以考虑是否满足3NF。即数据表中的字段可以通过关联的关系,派生即可。一般来说,在表设计时,通过外键来进行关联。
一般设计数据表时,满足三范式即可,如果为了更加严谨合理的设计,可以考虑更多的范式。
逆范式
何为逆范式?逆范式就是反三范式,在数据表设计时适当的逆范式设计可以提高性能。以下举例说明:
假如对于参加活动次数的记录,需要统计学生参加的次数和班级总参加活动的次数,那么有如下数据表。
班级表class
classId | className |
---|---|
1 | 班级1 |
2 | 班级2 |
学生表student
studentId | studentName | classId | counts |
---|---|---|---|
1 | 学生1 | 1 | 10 |
2 | 学生2 | 1 | 10 |
如果按照三范式设计数据表,想统计班级1所有学生总的活动次数,可以通过select counts from student where classId=1
,然后将取得的数据遍历,叠加counts得到最终想要的结果。但是如果在学生表的记录数量非常多的情况下,查询必然会非常消耗资源。鉴于这种情况,可以适当的逆范式设计,即存储冗余的参加活动次数的数据,可以有如下的表设计:
班级表class
classId | className | counts |
---|---|---|
1 | 班级1 | 20 |
2 | 班级2 | 0 |
在班级表中增加counts字段,存储该班级所有学生参加活动的总次数,在每次更新学生表的counts字段时,同时更新班级表的counts字段,那么再想要统计班级1所有学生总的活动次数的话,只需要通过select counts from class where classId=1
即可达到效果。
关于逆范式的合理使用,需要注意的是,在一对多的情况下,需要将冗余的数据放置在一的这一方。
二、SQL语句优化
SQL语句有哪几类
- DDL(数据定义语句):create,alter,drop
- DML(数据操作语句):insert,update,delete
- SELECT(select语句):select
- DTL(数据事务语句):commit,rollback,savepoint
- DCL(数据控制语句):grant,revoke
SQL语句优化可以从以下几方面着手:
1、show status命令
使用show status命令可以显示mysql数据库的当前状态。从而可以得知数据库运行的相关信息,以定位待优化的功能点。
2、建立适当索引
索引使用的是“空间换时间”的策略,因此当表中数据海量时,数据库会针对指定的数据表维护一个索引文件,文件中记录的就是表中每条记录的位置信息。当执行查询时,数据库会通过索引信息直接定位到记录的位置,从而提高查询效率。但是索引的引入,会降低增删改操作的效率,因为增删改操作时,需要对索引文件进行修改。
在哪些列上适合添加索引
- 作为查询条件,使用很频繁的字段应该创建索引
- 唯一性较差的字段不适合单独创建索引,即存储值内容很少的字段,例如性别,通常只有男女两个值,创建索引后,索引文件维护的男性数据和女性数据也会非常多,效率提升并不大。
- 频繁更新内容的字段,也不适合创建索引,因为该字段内容更新后,还需要去更新维护索引信息。
- 不会出现在where子句中的字段不适合创建索引,因为创建了不使用也没有意义。
索引的种类
- 主键索引(primary):某个字段设为主键,自动创建主键索引。
- 唯一索引(unique):某个字段列具有唯一性,同时又是索引。
- 普通索引(index)
- 全文索引(fulltext):主要用于对字段内容进行分词查询使用。例如想要在文章内容字段中查询包含“hello”单词的记录,那么可以在文章内容字段上添加全文索引以达到快速检索的目的。一般来说,在中文的系统中,不推荐使用全文索引,因为汉字的组合相较于英文单词的组合更复杂,建立索引对于提高效率意义不大。对于全文索引的使用,限制了数据表引擎必须是MyISAM。
索引的查询
- SHOW INDEX FROM 表名;
- SHOW KEYS FROM 表名;
- DESC 表名;
索引的使用
索引的使用非常简单,但是需要注意以下几点:
- 假如a字段和b字段作为复合索引,创建索引时,a字段先于b字段创建索引,那么执行
select * from table where a=xxx
会使用索引,而执行select * from table where b=xxx
是不会使用索引的。 - 对于like查询,如果条件字段添加了索引,但是查询条件是“%xxx”,索引是不生效的。
- 如果有两列(a和b)都建了单独索引,那么执行
select * from table where a=b
,索引是不生效的。 - 对于在索引列上计算的方法,索引是不会生效的,因为通过方法计算后,索引列的值会发生变化。以执行
select * from table where upper(a)='XXXXX'
来说,数据列a添加了索引,但是通过upper方法计算后,值会发生变化,索引不生效。如果想做这样的查询,可以转换思路,将条件值传递给方法,例如select * from table where a=upper('xxxxx')
,这样的话,索引是会起作用的。
关于索引使用的情况分析
使用show status like 'handler_read%'
查看索引的使用情况。
- Handler_read_key表示索引有效使用,值越大,说明索引使用越多,因此分析时,对于该值要越大越好。
- Handler_read_rnd_next表示索引无效使用,值越大,说明无效的索引越多,因此分析时,对于该值要越小越好。
3、分表
当表的数据量非常庞大的时候,即使使用索引,查询效率依然很低,因此就需要对数据表进行分表操作,分表的基本思想就是“化整为零,分而治之”。
分表可以从水平分割和垂直分割来着手。
- 水平分割:当数据表的记录数非常多时,可以采用水平分割。例如一张表中有1亿条数据,我们可以将其分割为100张100万数据的表,这时我们需要针对表中某个字段设计一个分割策略,按照该策略在插入时插入到对应的子表中,查询时按照该策略从对应的子表中查询到相应的数据。
- 垂直分割:当数据表记录数不是很多,但是每条记录中的某些字段的值非常大时,可以采用垂直分割。将内容庞大的字段单独建立数据表存储,在原表中存储对应的关系即可。
4、其他优化技巧
- 使用order by null禁用排序
- 在对精度有要求的应用中,使用decimal类型来存储相应的字段值。
- 对于图片,视频等大文件,采用专用的文件服务器存储,在数据库中只存储文件在文件服务器上的路径。
- 对于主键的选择,尽量使用不含业务逻辑的id做主键。
- 对于表字段数据类型大小的选择,能选择小的字段尽量使用小字段存储,例如能用4个字节存储的字段就不要使用32个字节的空间来存储。一方面是造成空间的浪费,另一方面在查询时也会由于遍历这无效的空间而影响的查询的效率。
- 关于读写分离
- 目的:缓解大型系统的查询压力。
- 实现原理:请求达到服务器前,先经过读写分离中间件,如360的Atlas、新浪的Amoeba,如果是DML数据操作语句如insert,update,delete,则分流给数据库集群的主节点数据库处理,后由mysql proxy完成同步,将数据同步到各个子节点数据库内。如果是查询操作,则由数据库集群的负载均衡来确定去哪个当前压力小的子数据库中查询。
三、题外话
MyISAM和InnoDB的区别
- MyISAM不支持外键,InnoDB支持外键。
- MyISAM不支持事务,InnoDB支持事务。
- MyISAM创建表后,对应生成三个文件FRM文件(存储表结构),MYD文件(存储数据),MYI文件(存储索引);InnoDB创建表后,只生成FRM文件,数据存储在ibdata1文件中。
关于使用MyISAM引擎的数据表磁盘碎片清理
当数据表的引擎是MyISAM时,数据有删除,在MYD文件中其实是不会释放空间的。如果长此以往,那么磁盘空间迟早会消耗殆尽,那么就需要定时对其进行碎片的清理。
使用optimize table 表名
清理数据文件碎片空间。