Mysql数据库提供两种类型的索引,若是没正确设置,索引的利用效率会大打折扣却彻底不知问题出在这。php
代码以下:mysql
以上建立的实际上是一个多列索引,建立列索引的代码以下:sql
代码以下:数据库
一个多列索引能够认为是包含经过合并(concatenate)索引列值建立的值的一个排序数组。 当查询语句的条件中包含last_name 和 first_name时,例如:数组
代码以下:性能优化
sql会先过滤出last_name符合条件的记录,在其基础上在过滤first_name符合条件的记录。那若是咱们分别在last_name和first_name上建立两个列索引,mysql的处理方式就不同了,它会选择一个最严格的索引来进行检索,能够理解为检索能力最强的那个索引来检索,另一个利用不上了,这样效果就不如多列索引了。
可是多列索引的利用也是须要条件的,如下形式的查询语句可以利用上多列索引:服务器
代码以下:网络
如下形式的查询语句利用不上多列索引:mysql优化
代码以下:并发
多列建索引比对每一个列分别建索引更有优点,由于索引创建得越多就越占磁盘空间,在更新数据的时候速度会更慢。
当咱们执行查询的时候,MySQL只能使用一个索引。若是你有三个单列的索引,MySQL会试图选择一个限制最严格的索引。可是,即便是限制最严格的单列索引,它的限制能力也确定远远低于firstname、lastname、age这三个列上的多列索引。
另外创建多列索引时,顺序也是须要注意的,应该将严格的索引放在前面,这样筛选的力度会更大,效率更高。
对于要常常查询的含量大量数据的数据库,创建索引是很是重要的,创建索引通常都是在where语句用得较多的列上。如今有个问题,若是一个表有多个列须要创建索引,是把全部列建成一个索引,仍是对每个列建一个索引,上篇文章作了一个介绍,这是做者得出的结论,Conclusion: For benchmarked queries we can see Multiple Column index beats Index Merge in all cases when such index can be used. It is also worth to watchout a MySQL may decide not to do Index merge (either intersection or union) but instead do full table scan or access table picking only one index on the pair.意思应该是说对多个列建索引比对每一个列分别建索引更有优点,并且要知道索引创建得越多就越占磁盘空间,在更新数据的时候速度会更慢。
tb表有1700条记录,foo字段有750个不一样的记录,那么就能够说We have a cardinality of 750 for foo。总规则能够说是cardinality越大的字段应该排在索引的第一位就是说索引的位置是(foo,bar),由于cardinality越大那么第一次取出来的记录集就越小,再进行第二次查询的次数就越少了。
考虑数据列的基数(cardinality)。基数是数据列所包含的不一样值的数量。例如,某个数据列包含值一、三、七、四、七、3,那么它的基数就是4。索引的基数相对于数据表行数较高(也就是说,列中包含不少不一样的值,重复的值不多)的时候,它的工做效果最好。若是某数据列含有不少不一样的年龄,索引会很快地分辨数据行。若是某个数据列用于记录性别(只有"M"和"F"两种值),那么索引的用处就不大。若是值出现的概率几乎相等,那么不管搜索哪一个值均可能获得一半的数据行。在这些状况下,最好根本不要使用索引,由于查询优化器发现某个值出如今表的数据行中的百分比很高的时候,它通常会忽略索引,进行全表扫描。惯用的百分比界线是"30%"。如今查询优化器更加复杂,把其它一些因素也考虑进去了,所以这个百分比并非MySQL决定选择使用扫描仍是索引的惟一因素。
最左前缀
多列索引还有另一个优势,它经过称为最左前缀(Leftmost Prefixing)的概念体现出来。继续考虑前面的例子,如今咱们有一个firstname、lastname、age列上的多列索引,咱们称这个索引为fname_lname_age。当搜索条件是如下各类列的组合时,MySQL将使用fname_lname_age索引:
再多说几句组合索引的最左优先原则:
组合索引的第一个字段必须出如今查询组句中,这个索引才会被用到。
若是有一个组合索引(col_a,col_b,col_c)
下面的状况都会用到这个索引:
col_a = "some value";
col_a = "some value" and col_b = "some value";
col_a = "some value" and col_b = "some value" and col_c = "some value";
col_b = "some value" and col_a = "some value" and col_c = "some value";
对于最后一条语句,mysql会自动优化成第三条的样子~~。
从另外一方面理解,它至关于咱们建立了(firstname,lastname,age)、(firstname,lastname)以及(firstname)这些列组合上的索引。下面这些查询都可以使用这个fname_lname_age索引:
SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age='17'; SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan'; SELECT peopleid FROM people WHERE firstname='Mike'; The following queries cannot use the index at all: SELECT peopleid FROM people WHERE lastname='Sullivan'; SELECT peopleid FROM people WHERE age='17'; SELECT peopleid FROM people WHERE lastname='Sullivan' AND age='17';
选择索引列
在性能优化过程当中,选择在哪些列上建立索引是最重要的步骤之一。
a.考虑使用索引的主要有两种类型的列:在WHERE子句中出现的列,在join子句中出现的列。
b.考虑列中值的分布,索引的列的基数越大,索引的效果越好。
c.使用短索引,若是对字符串列进行索引,应该指定一个前缀长度,可节省大量索引空间,提高查询速度。
d.利用最左前缀
e.不要过分索引,只保持所需的索引。每一个额外的索引都要占用额外的磁盘空间,并下降写操做的性能。
在修改表的内容时,索引必须进行更新,有时可能须要重构,所以,索引越多,所花的时间越长。
请看下面这个查询:
SELECT age ## 不使用索引
FROM people WHERE firstname='Mike' ## 考虑使用索引
AND lastname='Sullivan' ## 考虑使用索引
这个查询与前面的查询略有不一样,但仍属于简单查询。因为age是在SELECT部分被引用,MySQL不会用它来限制列选择操做。所以,对于这个查询来讲,建立age列的索引没有什么必要。下面是一个更复杂的例子:
SELECT people.age, ##不使用索引
town.name ##不使用索引
FROM people LEFT JOIN town ON
people.townid=town.townid ##考虑使用索引
WHERE firstname='Mike' ##考虑使用索引
AND lastname='Sullivan' ##考虑使用索引
与前面的例子同样,因为firstname和lastname出如今WHERE子句中,所以这两个列仍旧有建立索引的必要。除此以外,因为town表的townid列出如今join子句中,所以咱们须要考虑建立该列的索引。
那么,咱们是否能够简单地认为应该索引WHERE子句和join子句中出现的每个列呢?差很少如此,但并不彻底。咱们还必须考虑到对列进行比较的操做符类型。MySQL只有对如下操做符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE。能够在LIKE操做中使用索引的情形是指另外一个操做数不是以通配符(%或者_)开头的情形。例如,“SELECT peopleid FROM people WHERE firstname LIKE 'Mich%';”这个查询将使用索引,但“SELECT peopleid FROM people WHERE firstname LIKE '%ike';”这个查询不会使用索引。
用or分隔开的条件,若是or前的条件中的列有索引,然后面的列没有索引,那么涉及到的索引都不会被用到,例如:select * from table_name where key1='a' or key2='b';若是在key1上有索引而在key2上没有索引,则该查询也不会走索引
总结:多列索引只有在where条件中含有索引中的首列字段时才有效
分析索引效率
如今咱们已经知道了一些如何选择索引列的知识,但还没法判断哪个最有效。MySQL提供了一个内建的SQL命令帮助咱们完成这个任务,这就是EXPLAIN命令。EXPLAIN命令的通常语法是:EXPLAIN <SQL命令>。你能够在MySQL文档找到有关该命令的更多说明。下面是一个例子:
EXPLAIN SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age='17';
这个命令将返回下面这种分析结果:
下面咱们就来看看这个EXPLAIN分析结果的含义。
索引的缺点
到目前为止,咱们讨论的都是索引的优势。事实上,索引也是有缺点的。
首先,索引要占用磁盘空间。一般状况下,这个问题不是很突出。可是,若是你建立每一种可能列组合的索引,索引文件体积的增加速度将远远超过数据文件。若是你有一个很大的表,索引文件的大小可能达到操做系统容许的最大文件限制。
第二,对于须要写入数据的操做,好比DELETE、UPDATE以及INSERT操做,索引会下降它们的速度。这是由于MySQL不只要把改动数据写入数据文件,并且它还要把这些改动写入索引文件。
在大型数据库中,索引是提升速度的一个关键因素。无论表的结构是多么简单,一次500000行的表扫描操做不管如何不会快。若是你的网站上也有这种大规模的表,那么你确实应该花些时间去分析能够采用哪些索引,并考虑是否能够改写查询以优化应用。
转载自:http://www.jb51.net/article/39221.htm
1.对查询进行优化,应尽可能避免全表扫描,首先应考虑在 where 及 order by 涉及的列上创建索引。
2.应尽可能避免在 where 子句中使用!=或<>操做符,不然将引擎放弃使用索引而进行全表扫描。
3.应尽可能避免在 where 子句中对字段进行 null 值判断,不然将致使引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
能够在num上设置默认值0,确保表中num列没有null值,而后这样查询:
select id from t where num=0
4.应尽可能避免在 where 子句中使用 or 来链接条件,不然将致使引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
能够这样查询:
select id from t where num=10
union all
select id from t where num=20
5.下面的查询也将致使全表扫描:
select id from t where name like '%abc%'
若要提升效率,能够考虑全文检索。
6.in 和 not in 也要慎用,不然会致使全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
7.若是在 where 子句中使用参数,也会致使全表扫描。由于SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,若是在编译时创建访问计划,变量的值仍是未知的,于是没法做为索引选择的输入项。以下面语句将进行全表扫描:
select id from t where num=@num
能够改成强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8.应尽可能避免在 where 子句中对字段进行表达式操做,这将致使引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改成:
select id from t where num=100*2
9.应尽可能避免在where子句中对字段进行函数操做,这将致使引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name以abc开头的id
select id from t where datediff(day,createdate,'2005-11-30')=0--'2005-11-30'生成的id
应改成:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10.不要在 where 子句中的“=”左边进行函数、算术运算或其余表达式运算,不然系统将可能没法正确使用索引。
11.在使用索引字段做为条件时,若是该索引是复合索引,那么必须使用到该索引中的第一个字段做为条件时才能保证系统使用该索引,不然该索引将不会被使用,而且应尽量的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如须要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,可是会消耗系统资源的,应改为这样:
create table #t(...)
13.不少时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
14.并非全部索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即便在sex上建了索引也对查询效率起不了做用。
15.索引并非越多越好,索引当然能够提升相应的 select 的效率,但同时也下降了 insert 及 update 的效率,由于 insert 或 update 时有可能会重建索引,因此怎样建索引须要慎重考虑,视具体状况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽量的避免更新 clustered 索引数据列,由于 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将致使整个表记录的顺序的调整,会耗费至关大的资源。若应用系统须要频繁更新 clustered 索引数据列,那么须要考虑是否应将该索引建为 clustered 索引。
17.尽可能使用数字型字段,若只含数值信息的字段尽可能不要设计为字符型,这会下降查询和链接的性能,并会增长存储开销。这是由于引擎在处理查询和链接时会逐个比较字符串中每个字符,而对于数字型而言只须要比较一次就够了。
18.尽量的使用 varchar/nvarchar 代替 char/nchar ,由于首先变长字段存储空间小,能够节省存储空间,其次对于查询来讲,在一个相对较小的字段内搜索效率显然要高些。
19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20.尽可能使用表变量来代替临时表。若是表变量包含大量数据,请注意索引很是有限(只有主键索引)。
21.避免频繁建立和删除临时表,以减小系统表资源的消耗。
22.临时表并非不可以使用,适当地使用它们可使某些例程更有效,例如,当须要重复引用大型表或经常使用表中的某个数据集时。可是,对于一次性事件,最好使用导出表。
23.在新建临时表时,若是一次性插入数据量很大,那么可使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是数据量不大,为了缓和系统表的资源,应先create table,而后insert。
24.若是使用到了临时表,在存储过程的最后务必将全部的临时表显式删除,先 truncate table ,而后 drop table ,这样能够避免系统表的较长时间锁定。
25.尽可能避免使用游标,由于游标的效率较差,若是游标操做的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法以前,应先寻找基于集的解决方案来解决问题,基于集的方法一般更有效。
27.与临时表同样,游标并非不可以使用。对小型数据集使用 FAST_FORWARD 游标一般要优于其余逐行处理方法,尤为是在必须引用几个表才能得到所需的数据时。在结果集中包括“合计”的例程一般要比使用游标执行的速度快。若是开发时间容许,基于游标的方法和基于集的方法均可以尝试一下,看哪种方法的效果更好。
28.在全部的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每一个语句后向客户端发送 DONE_IN_PROC 消息。
29.尽可能避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
30.尽可能避免大事务操做,提升系统并发能力。
《高性能mysql》
若是应用程序使用了MySQL,其中包含一些查询速度慢的sql,咱们要去优化它们,优化的思路须要如何进行呢?主要是如下两点:
一、应用程序是否在检索大量超过须要的数据(行、列);
二、mysql服务器层是否在分析大量超过须要的数据行。
前者比较好进行,主要看开发者的细心以及缜密逻辑、流程分析;后者就须要一些数据库方面的知识、优化以及实践技巧。对于后一点,首先须要分清一个概念,就是扫描行数与返回行数的区别,后者是咱们实际取得的数据,而前者是mysql得出后者所须要扫描的数据量。
若是发现查询大量的数据但只返回少数的行,那么一般能够尝试下面的技巧:一、使用索引覆盖索引;二、改变库表结构,如使用单独的汇总表;三、重写这个复杂的查询。
下面针对查询优化提供一些技巧:
一、分解关联查询。
这个主要针对这种查询:关联查询了多个表,这种状况下可能出现本来能够经过索引实现的order by失效,数据须要在到达mysql服务器后再进行排序;而且多表关联,mysql实现的方式是一次扫描取一个表的数据,最后再处理合并,这些都须要消耗mysql的资源。固然,关联查询也有一些好处,好比只须要访问一次mysql,减小网络请求。当弊大于利时,咱们能够采起这样的优化措施,将主表的数据先查询出来,其余一些信息,在代码里拼凑好条件,一次性查询出来,再进行属性合并等操做。
二、当表a和表b用列c关联,若是优化器的关联顺序是b、a,那么就不须要在b的对应列上创建索引
三、确保任何group by和order by中的表达式只涉及一个表中的列(最好是优化器扫描的第一个表中的),这样mysql才能使用索引来优化过程
四、group by表达式,若是没有显式的order by表达式,默认会对后面的字段进行排序,若是排序字段没有用上索引,将是一个很大的性能消耗,尤为当有联表时,须要经过临时表(using temporary)实现。一个优化的技巧,是加上order by null。
四、mysql老是经过建立并填充临时表的方式来执行union查询。所以不少优化策略在union查询中都无法很好地使用,常常须要手工地将where limit order by 等子句"下推"到union的各子查询中,以便优化器能够充分利用这些条件进行优化。
除非确实须要服务器消除重复的行,不然就必定要使用union all。缘由是union操做须要取出两个表的数据,经过排序排除重复的行,会消耗mysql资源,若是数据量大的还要用到磁盘排序。
五、尽可能使用update(经过条件过滤来保证数据一致性等)代替先select for update再update的写法,由于事务提交的速度越快,持有的锁时间就越短,能够大大减小竞争和加速串行执行效率。
六、有些查询是没法优化的,能够考虑使用别的查询或者策略来实现相同的目的。
七、经过近似计算等方法,先过滤缩小范围(使用索引),而后再精确过滤。(这种是精确过滤用不上索引时的处理策略)
八、须要的时候,尽量让程序完成一些计算。(好比结果集中字符串的拼拼凑凑)
在优化查询的过程当中,索引的创建、使用扮演着很是重要的角色。创建索引时须要全局考虑全部的查询,而不只仅是当前要处理、优化的查询,不能由于要优化当前的查询而严重影响其余查询的执行效率。创建索引时须要考虑两点:一、出现频率高的查询条件及其顺序,二、索引列的选择性,要讲选择性高的列放到索引的最前列。索引列的选择性是指:不重复的索引值和数据表的记录总数的比值,选择性越高则查询效率越高。一般主要考虑第一点,由于它对查询的效率影响较大。
冗余索引,是指这种状况,index1(a),index2(a,b),index1是index2的最左前缀,它的做用也就能够被index2来代替(在使用b-tree索引的时候)。大多数状况下都不须要冗余索引,应该尽可能尽可能扩展已有的索引而不是建立新索引。但也有时候处于性能方面的考虑须要冗余索引,由于扩展已有的索引会致使其变得太大,从而影响其余使用该索引的查询性能。
索引使用中须要注意:
一、只有当索引的列顺序和order by子句的顺序彻底一致,而且全部列的排序方向(倒序或正序)都同样时,mysql才可以使用索引对结果作排序。若是查询须要关联多张表,则只有当order by子句引用的字段所有为第一个表(mysql优化器优化后实际执行时的第一个表)时,才能使用索引作排序。
order by子句和查找型查询的限制是同样的,须要知足索引的最左前缀的要求;不然mysql都须要执行排序操做,没法利用索引排序。有一种状况,order by子句能够不知足索引的最左前缀的要求;就是前导列为常量的时候。若是where子句或者join子句对这些列指定了常量,就能够弥补索引的不足。
二、对于一个表的一次扫描中最多只能用到它的一个索引
三、尽可能将须要作范围查询的列放到索引的后面,以便优化器可以使用尽量多的索引列
松散索引扫描与紧凑索引扫描:
二者的区别:在松散索引扫描方式下,分组操做和范围预测(若是有的话)一块儿执行完成。在紧凑索引扫描下,先对索引执行范围扫描(range search),再对结果元组进行分组。
松散索引扫描的条件:
1)、查询在单一表上
2)、group by指定的列是索引的一个最左前缀,而且没有其余的列
3)、若是在选择列表select list中存在聚合函数,只能使用min()和max()两个聚合函数,而且指定的是同一列
4)、若是查询中存在除了group by指定的列以外的索引其余部分,那么必须以常量的形式出现
5)、索引中的列必须索引整个数据列的值,而不是一个前缀索引(注意不是索引前缀,前缀索引是指索引中的某些列不是某个字段,而是某个字段的前缀部分)
从5.5开始,松散索引的扫描条件放宽了:
1)、select中的聚合函数除了min()和max()以外,还支持avg(distinct)、sum(distinct)、count(distinct)
2)、查询中没有group by和distinct条件
判断一个查询是否使用松散索引扫描的方法:执行计划中有using index for group-by
紧凑索引扫描起做用的条件:
在查询中存在常量相等等where条件字段(索引中的字段),且该字段在group by指定的字段的前面或者中间。来自相等条件的常量可以填充搜索keys中的gaps,于是可以构成一个索引的完整前缀。索引前缀可以用户索引查找。若是要求对group by的结果进行排序,而且查找字段组成一个索引前缀,那么mysql一样能够避免额外的排序操做。
查询使用了紧凑索引扫描的判断方法:执行计划中有using index
你有90w数据了,这并非由于数据量少而不使用索引,而是你的索引建错了,没有任何意义,因此MySQL不会去用你的索引。
当你source字段惟一性不高,例如你90w数据,里面source字段来来去去就那么十几个值,这种状况下影响结果集巨大,就会全表扫描。这种状况全表扫描还要快于利用索引,只要理解索引的本质不难明白MySQL为什么不使用索引。
极端点的状况,90万的数据,source只有0和1两个值,利用索引要先读索引文件,而后二分查找,找到对应数据的数据磁盘指针,再根据读到的指针再读磁盘上对应的数据数据,影响结果集45万。这种状况,和直接全表扫描那个快显而易见。
若是你source字段是一个unique,就会用到索引。
若是你必定要用索引,能够用force index,不过效率不会有改善通常还会更慢就是了。
合理使用索引,Cardinality是一个重要指标,过小的话跟没建没区别,还浪费空间。