平时经常学习使用MySQL数据库,时不时需要优化查询,面试也有遇到数据库优化的问题,今天抽空来总结一翻,在此标记。
数据库规范:
一、基本规范
1.不在数据库做计算,cpu计算务必移至业务层
2.控制单表数据量,单表记录控制在千万级
3.控制列数量,字段数控制在20以内
4.平衡范式与冗余,为提高效率可以牺牲范式设计,冗余数据
5.拒绝数据库的五大陷阱:大sql,大事务,大批量,大类型,大表
二,字段类军规
tinyint(1Byte),smallint(2Byte),mediumint(3Byte),int(4Byte),bigint(8Byte)
用好数值类型,有些字符可转化为数字
例如:用int而不是char(15)存储ip
优先使用enum或set
例如:`sex` enum (‘F’, ‘M’)
避免使用NULL字段,NULL字段很难查询优化,NULL字段的索引需要额外空间,NULL字段的复合索引无效
bad case:
`name` char(32) default null;
good case:
`age` int not null default 0;
不在数据库里存图片,文件等
三,索引类军规
谨慎合理使用索引,改善查询、减慢更新,索引一定不是越多越好(能不加就不加,要加的一定得加)
重复记录条数过多不适合建索引,例如“性别”
字符字段必须建前缀索引,不在索引做列运算
bad case:
select id where age +1 = 10; --可以优化为 select id where age = 10-1;
innodb主键合理使用自增列
主键建立聚簇索引,主键不应该被修改,字符串不应该做主键
如果不指定主键,innodb会使用唯一且非空值索引代替
由程序保证字段约束而不采用外键
四,sql类军规
sql语句尽可能简单:
一条sql只能在一个cpu运算
大语句拆成小语句,减少锁时间,一条大sql可以堵死整个库
简单的事务:
事务时间尽可能短,以提高系统并发能力
bad case:上传图片事务
避免使用触发器和用户自定义函数,可由程序取而代之
不用select * from 消耗cpu,io,内存,带宽,且不具有扩展性
OR改写为IN() 或者OR改写为UNION
limit高效分页
limit越大,效率越低
select id from t limit 10000, 10;
应该改为 :
select id from t where id > 10000 limit 10;
使用union all 替代 union,union有去重开销
尽量不用连接join
大量数据插入可以拆分多次插入小量数据
五,使用性能分析工具:
show profile;
mysqlsla;
mysqldumpslow;
explain;
show slow log;
show processlist;
show query_response_time(percona)
程序员必备的MySQL优化技巧:
1、禁止使用SELECT * ,只获取必要的字段,需要显示说明列属性,缺点:
a)消耗cpu,io,内存,带宽
b)不能有效的利用覆盖索引
c)使用SELECT * 容易在增加或者删除字段后出现程序BUG, 不具有扩展性
2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
3、使用 insert into tablename (t_colname) VALUES (xxx) ,必须显示指定插入的列属性,否则容易在增加或者删除字段后出现程序BUG
4、务必请使用“同类型”进行比较,否则可能全表扫面
SELECT name FROM t_user WHERE phone=13888888888 会导致全表扫描,phone是bigint类型,传入的是long类型
5、禁止在 where 条件的上使用函数或者计算
解读:SELECT name FROM tuser WHERE date(createdatatime)='2017-12-15' 会导致全表扫描
推荐的写法是:SELECT name FROM tuser WHERE create_datatime>= '2017-02-15 ' and create_datatime < '2017-02-16 '
6、禁止负向查询,以及%开头的模糊查询
a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
b)%开头的模糊查询,会导致全表扫描
7、不要大表使用JOIN查询,禁止大表使用子查询,会产生临时表,消耗较多内存与CPU,极大影响数据库性能
8、OR改写为IN()或者UNION,原因很简单or不会走索引
9、事务尽量的简单。事务就像程序中的锁一样粒度尽可能要小
10、不要一次更新大量数据。数据更新会对行或者表加锁,应该分为多次更新
11.explain优化SQL语句:标注(type,key,key_len,rows,extra)
type列,连接类型。一个好的sql语句至少要达到range级别。杜绝出现all级别
key列,使用到的索引名。如果没有选择索引,值是NULL。可以采取强制索引方式
key_len列,索引长度
rows列,扫描行数。该值是个预估值
extra列,详细说明。注意常见的不太友好的值有:Using filesort, Using temporary
12.SQL语句中IN包含的值不应过多
MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:select id from t where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了;再或者使用连接来替换。
13.当只需要一条数据的时候,使用limit 1,使EXPLAIN中type列达到const类型。
14.如果排序字段没有用到索引,就尽量少排序
15.不使用ORDER BY RAND()
select id from `dynamic` order by rand() limit 1000; sql语句,可优化为:
select id from `dynamic` t1 join (select rand() * (select max(id) from `dynamic`) as nid) t2 on t1.id > t2.nid limit 1000;
16.使用合理的分页方式以提高分页的效率。
select id,name from product limit 525657, 20;
使用sql语句做分页的时候,可能有人会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。
优化的方法如下:可以取前一页的最大行数的id,然后根据这个最大的id来限制下一页的起点。
select id,name from product where id> 525245 limit 20
17.避免在 where 子句中对字段进行 null 值判断,对于null的判断会导致引擎放弃使用索引而进行全表扫描。
18.避免在where子句中对字段进行表达式操作,比如:
select user_id,user_project from user_base where age*2=36;
对字段age行做了算术运算,这会造成引擎放弃使用索引,建议改成:
select user_id,user_project from user_base where age=36/2;
19.避免隐式类型转换,where 子句中出现 column 字段的类型和传入的参数类型不一致的时候发生的类型转换,建议先确定where中的参数类型
20.对于联合索引来说,要遵守最左前缀法则,比如:
索引含有字段id,name,school,可以直接用id字段,也可以id,name这样的顺序,但是name;school都无法使用这个索引。所以在创建联合索引的时候一定要注意索引字段顺序,常用的查询字段放在最前面
21.JOIN优化,
LEFT JOIN A表为驱动表
INNER JOIN MySQL会自动找出那个数据少的表作用驱动表
RIGHT JOIN B表为驱动表
MySQL中没有full join,可以用union解决
select * from A left join B on B.name = A.name where B.name is null union allselect * from B;
尽量使用inner join,避免left join
参与联合查询的表至少为2张表,一般都存在大小之分。如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表,但是left join在驱动表的选择上遵循的是左边驱动右边的原则,即left join左边的表名为驱动表。
利用小表去驱动大表能减少嵌套循环中的循环次数,以减少 IO总量及CPU运算的次数。
22.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致
23.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
24.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
25.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先可变字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
26.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。