一:索引的类型mysql
二:索引的优势sql
三:高性能索引策略bash
四:索引案例服务器
1.1类型介绍数据结构
索引有不少类型,能够为不一样场景提供更好的性能。在MySQL中,索引是在存储引擎层而不是服务器层实现的。因此,并无统一的索引标准:不一样存储引擎的索引的工做方式并不同,也不是全部的存储引擎都支持全部类型的索引,即便多个存储引擎支持同一种类型的索引,其底层的实现也可能不一样ide
1.2 B+Tree索引函数
存储引擎以不一样的方式使用B+Tree索引,性能也各有不一样,各有优劣。性能
例如MyISAM使用前缀压缩技术使得索引更小,但InnoDB则按照原数据格式进行存储。优化
再如MyISAM索引经过数据的物理位置引用被索引行,而InooDB则根据主键引用被索引的行spa
1.2.1 B+Tree索引数据结构
1.2.2理解数据结构
假若有以下数据表:
CREATE TABLE people( last_name varchar(50) not null, first_name varchar(50) not null, dob date not null, gender eum('m','f') not null, key(last_name, first_name, dob) )
对于每一行的数据,索引中包含了,last_name,first_name和dob列的值
注意:索引对多个值进行排序的依据是CREATE TABLE语句中定义索引时列的顺序。看最后两个条目,两我的的姓和名都同样,则根据他们的出生日期来排列顺序
1.2.3 B+Tree索引对以下类型的查询有效
全值匹配:和索引中的全部列进行匹配,例如前面建立的索引可用于查找姓名为Allen Cuba,出生于1960-01-01的人
匹配最左前缀:前面建立的索引可用于查找全部姓为Allen的人,即只使用索引的第一列
匹配列前缀:前面建立的索引也能够只匹配某一列的值的开头部分,例如能够用于查找全部以J开头的姓的人
匹配范围值:前面建立的索引可用于查找姓在Allen和Barrymore之间的人。(这里只使用了索引的第一列)
精确匹配某一列并范围匹配另一列:前面建立的索引可用于查找全部姓为Allen,而且名字是字母K开头(好比Kim)的人。即第一列last_name全匹配,第二列first_name范围匹配
只访问索引的查询:B+Tree一般能够支持“只访问索引的查询”,即查询只须要访问索引,而无须访问数据行。
由于索引树中的节点是有序的,因此除了按值查找以外,索引还能够用于查询中的ORDER BY操做。通常来讲,若是B+Tree能够按照某种方式查找到值,那么也能够按照这种方式用于排序。因此,若是ORDER BY 子句知足前面列出的几种查询类型,则这个索引也能够知足对应的排序需求
1.2.4 B+Tree索引的限制
1.2.4.1若是不是按照索引的最左列开始查找,则没法使用索引。例如上面例子中的索引没法用于查找名字为Kim的人,也没法查找某个特定生日的人,由于这两列都不是最左数据列。相似的,也没法查找姓氏以某个字母结尾的人
1.2.4.2不能跳过索引中的列。也就是说,前面所述的索引没法用于查找姓为Allen而且在某个特定日期出生的人。若是不指定名(first_name),则MySQL只能使用索引的第一列
1.2.4.3若是查询中有某个列的范围查询,则其右边全部列都没法使用索引优化查找。例若有查询WHERE last_name='Allen' ANDfirst_name LIKE 'K%' AND dob='1930-07-12',这个查询只能使用索引的前两列,由于这里LIKE是一个范围条件(可是服务器能够把其他列用于其它目的)。若是范围查询列值数量有限,那么能够经过使用多个等于条件来代替范围条件。尽量将范围查询放到最后,由于范围查询后的列不能使用索引
2.1索引的优势
索引可让服务器快速的定位到表的指定位置。可是这并非索引的惟一做用,到目前为止
能够看到,根据建立索引的数据结构不一样,索引也有一些其它的附加做用
最多见的B+Tree索引,按照顺序存储数据,因此MySQL能够用来作ORDER BY 和GROUPBY操做。由于数据是有序的,因此B+Tree也就会将相关的列值都存储在一块儿。最后,由于索引中存储了实际的列值,因此某些查询只使用索引就可以完成所有查询。据此特性,总结下来索引有以下三个优势:
1.索引大大减小了服务器须要扫描的数据量
2.索引能够帮助服务器避免排序和临时表
3.索引能够将随机I/O变为顺序I/O
Lahdenmaki和Leach提出,如何评价一个索引是否适合某个查询的“三星系统”:
A.索引将相关的记录放到一块儿则得到一星;
B.若是索引中的数据顺序和查找中的排序顺序一致则得到两星;
C.若是索引中的列包含了查询中须要的所有列则得到三星;
3.高性能的索引策略
正确的建立索引和使用索引是实现高性能查询的基础。如今讨论一下如何真正的发挥这些索引的优点
3.1独立的索引
一般看到一些查询不当的使用索引,或者使MySQL没法使用已有的索引。若是查询中的列不是独立的,则MySQL就不会使用索引。“独立的列”是指索引列不能是表达式的一部分,也不能是函数的参数
例1:下面这个查询就没法使用user_id列的索引
mysql> SELECT user_id FROM tb1 WHERE user_id+1 = 5;
很容易看出WHERE中的表达式起始等价于user_id=4,可是MySQL没法自动解析这个方程式,这彻底是用户行为。咱们应该养成简化WHERE条件的习惯,始终将索引列单独放在比较符号的一侧
例2:下面这个查询就没法使用date_col列的索引
mysql> SELECT ... WHERE TO_DAYS(CUURRENT_DATE) - TO_DAYS(date_col) <= 10
解决方法:在程序中获取当前时间,减去10天,加入最后的值存储在last_date_val中
mysql> SELECT ... WHERE date_col <= last_date_val
3.2前缀索引和索引选择性
有时候须要索引很长的字符列,这会让索引变得很慢。一般能够索引开始的部分字符,这样能够大大节约索引空间,从而提升索引效率。但这样也会下降索引的选择性。
索引选择性是指:不重复的索引值(也称为基数)和数据表的记录总数(#T)的比值,范围从1/#T到1之间。索引的选择性越高,则查询效率越高,由于选择性高的索引可让MySQL在查找时过滤掉更多的行。惟一索引的选择性是1,这是最好的索引选择性,性能也最好
理解索引选择:每行记录都有一个索引值,若是前缀过少,那么索引值重复的可能性就越大,这样在查询的时候查询到的数据就越多,过滤掉的数据就越少,因此要找一个合理的前缀。
合理前缀的诀窍在于:选择足够长的前缀以保证较高的选择性,同时又不能太长(以便节约空间)。前缀应该足够长,以使得前缀索引的选择性接近于索引整个列。换句话说,前缀的“基数”应该接近于完整列的“基数”
3.2.1如何选择合理前缀的长度
mysql> CREATE TABLE tb( -> city VARCHAR(50) NOT NULL -> ); Query OK, 0 rows affected (0.26 sec) mysql> SELECT COUNT(*) AS cnt, city_name FROM city GROUP BY city_name ORDER BY cnt DESC LIMIT 10;
能够看到,上面每一个值都出现了5-7次,如今查找到最频繁出现的城市,先从3个前缀字母开始
mysql> SELECT COUNT(*) AS cnt, LEFT(city_name,3) AS pref FROM city GROUP BY pref ORDER BY cnt DESC LIMIT 10;
每一个前缀都比原来的城市出现的次数更多,所以惟一前缀比惟一城市要少的多。而后增长前缀的长度,知道这个前缀的选择性接近完整列的选择性。
mysql> SELECT COUNT(*) AS cnt, LEFT(city_name,7) AS pref FROM city GROUP BY pref ORDER BY cnt DESC LIMIT 10;
计算合适前缀的长度的方法就是计算完整列的选择性,并使前缀的选择性接近于完整列的选择性。下面是如何计算完整列的选择性:
mysql> SELECT COUNT(DISTINCT city_name)/COUNT(*) FROM city;
一般来讲(尽管也有例外状况),这个例子中若是前缀的选择性可以接近于0.4317,基本上就可用了,能够在一个查询中针对不一样前缀长度计算。
显示长度为7的时候,再增长前缀长度,选择性已经没有提高了(或者某些时候提高幅度很小了)
3.2.2建立前缀索引
mysql> ALTER TABLE city ADD [KEY|INDEX] city_name_index(city_name(7));
前缀索引是一种能使索引更小、更快的有效方法,但另外一方面也有其缺点:MySQL没法使用前缀索引作ORDER BY 和 GROUP BY ,也就没法使用前缀索引作覆盖扫描
3.3多列索引
不少人对多列索引的理解还不够。一个常见的错误就是,为每一个列建立独立的索引,或者按照错误的顺序建立多列索引
CREATE TABLE t( c1 INT, c2 INT, c3 INT, KEY(c1), KEY(c2), KEY(c3) );
这种索引策略,通常是因为人们听到一些说,“把WHERE条件里面的列都建上索引”致使的。其实是很是错误的,这样一来最好的状况下也只能是“一星”索引,其性能比起真正最有的索引可能差几个数量级。有时候若是没法设计一个“三星”索引,那么不如忽略掉WHERE子句,集中精力优化索引列的顺序,或者建立一个全覆盖索引
在多个列上创建独立的单列索引大部分状况下并不能提升MySQL的查询性能。MySQL5.0和更新版本引入了一种叫“索引合并”的策略,必定程度上可使用表上的多个单列索引来定位指定的行。
更早版本的MySQL只能使用其中某一个单列索引,而后这种状况下没有哪个独立的单列索引是很是有效的。例如:表film_actor在字段film_id和actor_id上各有一个单列索引,但对于下面这个查询WHERE条件,这两个单列索引都不是好的选择
mysql> SELECT film_id,actor_id FROM film_actor WHERE actor_id=1OR film_id=1;
在老的MySQL版本中,MySQL对这个查询会使用全表扫描。除非改写成以下的两个查询。
mysql> SELECT film_id,actor_id FROM film_actor WHERE actor_id=1 -> UNION ALL -> SELECT film_id,actor_id FROM film_actor WHERE film_id=1 AND actor_id<>1;
3.4选择合适的索引列顺序
3.4.1例子分析
mysql> SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;
是建立一个(staff_id, customer_id)索引仍是应该颠倒顺序呢?能够作一些查询来肯定这个表中值的分布状况,并肯定哪一个列的选择性更高。
mysql> SELECT SUM(staff_id=2),SUM(customer_id=584) FROM payment;
mysql> SELECT COUNT(DISTINCT(staff_id))/COUNT(*) AS staff_id , COUNT(DISTINCT(customer_id))/COUNT(*) AS customer_id , COUNT(*) FROM payment;