数据库优化总结
Posted 软件技术共享平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库优化总结相关的知识,希望对你有一定的参考价值。
第一部分:SQL语句优化
1、尽量避免使用select *,使用具体的字段代替*,只返回使用到的字段。
2、尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描。
SELECT * FROM t WHERE id IN (2,3)
SELECT * FROM t1 WHERE username IN (SELECT username FROM t2)
优化方式:如果是连续数值,可以用between代替。如下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3
如果是子查询,可以用exists代替。如下:
SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t1.username = t2.username)
3、尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE username LIKE '%li%'
优化方式:尽量在字段后面使用模糊查询。如下:
SELECT * FROM t WHERE username LIKE 'li%'
4、尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE score IS NULL
优化方式:可以给字段添加默认值0,对0值进行判断。如下:
SELECT * FROM t WHERE score = 0
5、尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t2 WHERE score/10 = 9
SELECT * FROM t2 WHERE SUBSTR(username,1,2) = 'li'
优化方式:可以将表达式、函数操作移动到等号右侧。如下:
SELECT * FROM t2 WHERE score = 10*9
SELECT * FROM t2 WHERE username LIKE 'li%'
6.当只要一行数据时使用LIMIT 1
加上LIMIT 1可以增加性能。mysql数据库引擎会在查找到一条数据后停止搜索,而不是继续往后查询下一条符合条件的数据记录
7、使用连接(JOIN)来代替子查询(Sub-Queries)
8、在建有索引的字段上尽量不要使用函数进行操作
例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。
9、排序的索引问题
mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
10、应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
11、应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or Name = 'admin'
可以这样查询:
select id from t where num = 10
union all
select id from t where Name = 'admin'
12、索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。
比如性别可能就只有两个值,建索引不仅没什么优势,还会影响到更新速度,这被称为过度索引。
第二部分:数据库索引
1、索引的创建、查看、删除
//创建索引有两种方式:1.CREATE TABLE语句时可以创建索引 2.单独用CREATE INDEX或ALTER TABLE来为表增加索引
// 1、ALTER TABLE 用来创建普通索引、唯一索引、主键索引和全文索引
ALTER TABLE table_name ADD INDEX index_name (column_list);
ALTER TABLE table_name ADD UNIQUE (column_list);
ALTER TABLE table_name ADD PRIMARY KEY (column_list);
ALTER TABLE table_name ADD FULLTEXT (column_list);
// 2、CREATE INDEX可对表增加普通索引或UNIQUE索引以及全文索引,但是不可以对表增加主键索引
CREATE INDEX index_name ON table_name (column_list);
CREATE UNIQUE index_name ON table_name (column_list);
CREATE FULLTEXT index_name ON table_name (column_list);
查看索引
mysql> show index from tblname;
mysql> show keys from tblname;
删除索引
删除索引的mysql格式 :DORP INDEX IndexName ON tab_name;
2、使用索引的优缺点
创建索引可以大大提高系统的性能, 可以大大加快数据的检索速度。
索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。
第一, 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二, 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三, 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
一般来说,应该在这些列上创建索引。
第一, 在经常需要搜索的列上,可以加快搜索的速度;
第二, 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
第三, 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
第四, 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
第五, 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
第六, 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
一般来说,不应该创建索引的的这些列具有下列特点:
第一,在查询中很少使用列不应该创建索引。
第二,对于只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
第四,当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。
3、组合索引
组合索引的生效原则是 从前往后依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用;
造成断点的原因:
前边的任意一个索引没有参与查询,后边的全部不生效。
前边的任意一个索引字段参与的是范围查询,后面的不会生效。
断点跟索引字字段在SQL语句中的位置前后无关,只与是否存在有关。
where a=3 and b=45 and c=5 .... #这种三个索引顺序使用中间没有断点,全部发挥作用;
where a=3 and c=5... #这种情况下b就是断点,a发挥了效果,c没有效果
where b=3 and c=4... #这种情况下a就是断点,在a后面的索引都没有发挥作用,这种写法联合索引没有发挥任何效果;
where b=45 and a=3 and c=5 .... #这个跟第一个一样,全部发挥作用,abc只要用上了就行,跟写的顺序无关
(a,b,c) 三个列上加了联合索引(是联合索引 不是在每个列上单独加索引)而是建立了a,(a,b),(a,b,c)三个索引,另外(a,b,c)多列索引和 (a,c,b)是不一样的。
(0) select * from mytable where a=3 and b=5 and c=4;
#abc三个索引都在where条件里面用到了,而且都发挥了作用
(1) select * from mytable where c=4 and b=6 and a=3;
#这条语句为了说明 组合索引与在SQL中的位置先后无关,where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样
(2) select * from mytable where a=3 and c=7;
#a用到索引,b没有用,所以c是没有用到索引效果的
(3) select * from mytable where a=3 and b>7 and c=3;
#a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引
(4) select * from mytable where b=3 and c=4;
#因为a索引没有使用,所以这里 bc都没有用上索引效果
(5) select * from mytable where a>4 and b=7 and c=9;
#a用到了 b没有使用,c没有使用(a使用的是范围查询)
(6) select * from mytable where a=3 order by b;
#a用到了索引,b在结果排序中也用到了索引的效果,前面说了,a下面任意一段的b是排好序的
(7) select * from mytable where a=3 order by c;
#a用到了索引,但是这个地方c没有发挥排序效果,因为中间断点了,使用 explain 可以看到 filesort
(8) select * from mytable where b=3 order by a;
#b没有用到索引,排序中a也没有发挥索引效果
在查询时,MYSQL只能使用一个索引,如果建立的是多个单列的普通索引,在查询时会根据查询的索引字段,从中选择一个限制最严格的单例索引进行查询。别的索引都不会生效。
第三部分:MySQL优化:explain分析
Explain是Mysql的自带查询优化器,负责select语句的优化器模块,可以模拟优化器执行SQL查询语句,从而知道Mysql是如何处理SQL的,语法也很简单:Explain + SQL
1、id:反映的是表的读取的顺序,或查询中执行select子句的顺序。
小表永远驱动大表,三种情况:
(1)id相同,执行顺序是由上至下的
(2)id不同,如果是子查询,id序号会递增,id值越大优先级越高,越先被执行
(3)id存在相同的,也存在不同的,所有组中,id越大越先执行,如果id相同的,从上往下顺序执行
derived是衍生虚表的意思,derived2中的2对应id2
2、select_type:反映的是Mysql理解的查询类型
(1)simple:简单的select查询,查询中不包含子查询或union。
(2)primary:查询中若包含任何复杂的字部分,最外层查询标记为primary。
(3)subquery:select或where列表中的子查询。
(4)derived(衍生):在from列表中包含的子查询,Mysql会递归执行这些子查询,把结果放在临时表里。
(5)union:若第二个select出现在union后,则被标记为union,若union包含在from字句的子查询中,外层select将被标记为derived
(6)union result:union后的结果集
3、table:反映这一行数据是关于哪张表的
4、type:访问类型排序
反映sql优化的状态,至少达到range级别,最好能达到ref
查询效率:system > const > eq_ref > ref > range > index > all
(完整的排序:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index >all)
(1)system:从单表只查出一行记录(等于系统表),这是const类型的特例,一般不会出现
(2)const:查询条件用到了常量,通过索引一次就找到,常在使用primary key或unique索引中出现。
where id=1写死,所以类型是const
(3)eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描。
(4)ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它可能会找到多个符合条件的行,与eq_ref的差别是eq_ref只匹配了一条记录。
(5)range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般是在where语句中出现了between、<、>、in等的查询。
这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。与eq_ref和ref的区别在于筛选条件不是固定值,是范围。
(6)index:full Index scan,index和all的区别为index类型只遍历索引树。这通常比all快,因为索引文件通常比数据文件小。
要获得的id信息,刚好id在索引上,从索引中读取
(all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
(7)all:全表扫描,如果查询数据量很大时,全表扫描效率是很低的。
5、possible_keys、key、key_len:反映实际用到了哪个索引,索引是否失效
(1)possible_keys:Mysql推测可能用到的索引有哪些,但不一定被查询实际使用
(2)key:实际使用的索引,若为null,则可能没建索引或索引失效。
(查询中若使用了覆盖索引,则该索引仅出现在key列表中。
覆盖索引:select后面的字段和所建索引的个数、顺序一致)
(3)key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。同样的查询结果下,长度越短越好。
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。
6、ref:反映哪些列或常量被用于查找索引列上的值
7、rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
仅通过主键索引查找是641行
建完相关的复合索引再查,需要查询的行数就变少了
8、Extra
(1)using filesort:mysql中无法利用索引完成的排序,这时会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
创建索引时就会对数据先进行排序,出现using filesort一般是因为order by后的条件导致索引失效,最好进行优化。
order by的排序最好和所建索引的顺序和个数一致
(2)using temporary:使用了临时表保存中间结果,mysql在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。
影响更大,所以要么不建索引,要么group by的顺序要和索引一致
(3)using index:表示相应的select操作中使用了覆盖索引,避免访问了表的数据行,效率好
覆盖索引:select后的数据列只从索引就能取得,不必读取数据行,且与所建索引的个数(查询列小于等于索引个数)、顺序一致。
所以如果要用覆盖索引,就要注意select的列只取需要用到的列,不用select *,同时如果将所有字段一起做索引会导致索引文件过大,性能会下降。
出现using where,表明索引被用来执行索引键值的查找
如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
(4)using where:表明使用了where过滤
(5)using join buffer:使用了连接缓存
(6)impossible where:where子句的值是false
(7)select tables optimized away
(8)distinct:优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作
以上是关于数据库优化总结的主要内容,如果未能解决你的问题,请参考以下文章