mysql索引以及优化

今天看到别人写的一些关于mysql索引的文章,有一些小收获,就以此开启我的随笔记录简单摘了一些重点

转载文章:http://www.cnblogs.com/tgycoder/p/5410057.html

mysql索引实现原理

1. MyISAM引擎使用B+Tree作为索引结构,叶结点的data域存放的是数据记录的地址,MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。

  

2. InnoDB也使用B+Tree作为索引结构,第一个重大区别是InnoDB的数据文件本身就是索引文件,第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶结点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引

InnoDB主索引(同时也是数据文件)的示意图,可以看到叶结点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

3. 最左前缀与相关优化

之前我理解的最左前缀以为索引的顺序是跟where条件查询的一致不一致就使用不到索引,这是错误的

  Ps:最左前缀原则中where字句有or出现还是会遍历全表

(1) 其实where条件的顺序不影响使用索引,比如三个字段添加联合索引t_user表联合索引(name, mobile, create_date)

   select * from t_user where mobile = ‘13256767876‘ and create_date= ‘2017-07-31‘ and name = ‘corner‘;

  理论上索引对顺序是敏感的,但是由于MySQL的查询优化器会自动调整where子句的条件顺序以使用适合的索引,所以这样也是可以用到索引的

(2)查询条件没有指定索引第一列

   如果where条件中没有name条件,只有另外两个无论顺序是什么都是无法用到索引的,如果where条件只有name,status而没有mobile这时候只能用到一列索引,status这一列的索引是用不到的

(3)范围查询

范围列可以用到索引(必须是最左前缀),但是范围列后面的列无法用到索引。同时,索引最多用于一个范围列,因此如果查询条件中有两个范围列则无法全用到索引

表t_title联合索引(emp_no,title,from_date)

EXPLAIN SELECT * FROM employees.titles
WHERE emp_no < ‘10010‘
AND title=‘Senior Engineer‘
AND from_date BETWEEN ‘1986-01-01‘ AND ‘1986-12-31‘;
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | titles | range | PRIMARY       | PRIMARY | 4       | NULL |   16 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
  只能用到第一列索引,这里特别要说明MySQL一个有意思的地方,那就是仅用explain可能无法区分范围索引和多值匹配,因为在type中这两者都显示为range。同时,用了“between”并不意味着就是范围查询,例如下面的查询:全部索引都用到了
EXPLAIN SELECT * FROM employees.titles
WHERE emp_no BETWEEN ‘10001‘ AND ‘10010‘
AND title=‘Senior Engineer‘
AND from_date BETWEEN ‘1986-01-01‘ AND ‘1986-12-31‘;
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | titles | range | PRIMARY       | PRIMARY | 59      | NULL |   16 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
(4)查询条件中含有函数或表达式如果查询条件中含有函数或表达式,则MySQL不会为这列使用索引like如果通配符%不出现在开头,则可以用到索引,但根据具体情况不同可能只会用其中一个前缀
EXPLAIN SELECT * FROM employees.titles WHERE emp_no=‘10001‘ AND title LIKE ‘Senior%‘;
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | titles | range | PRIMARY       | PRIMARY | 56      | NULL |    1 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+

4.索引选择性与前缀索引   (1)什么情况下判断字段是否应该建立索引,今天刚看到这个"选择性"的概念,除了表数据很少的情况不用建索引因为索引文件本身要消耗存储空间会加重数据库操作的负担,另外一种情况就是索引的选择性比较低:  所谓索引的选择性(Selectivity),是指不重复的索引值(也叫基数,Cardinality)与表记录数(#T)的比值:Index Selectivity = Cardinality / #T  显然选择性的取值范围为(0, 1],选择性越高的索引价值越大,这是由B+Tree的性质决定的。

           这个问题就像是面试时提问我的一个问题:性别列适不适合建立索引?(答案是否定的)


例如,上文用到的employees.titles表,如果title字段经常被单独查询,是否需要建索引,我们看一下它的选择性:



SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles;
+-------------+
| Selectivity |
+-------------+
|      0.0000 |
+-------------+

title的选择性不足0.0001(精确值为0.00001579),所以实在没有什么必要为其单独建索引。


(2)有一种与索引选择性有关的索引优化策略叫做前缀索引,就是用列的前缀代替整个列作为索引key,当前缀长度合适时,可以做到既使得前缀索引的选择性接近全列索引,同时因为索引key变短而减少了索引文件的大小和维护开销。下面以employees.employees表为例介绍前缀索引的选择和使用。

从图12可以看到employees表只有一个索引<emp_no>,那么如果我们想按名字搜索一个人,就只能全表扫描了:

EXPLAIN SELECT * FROM employees.employees WHERE first_name=‘Eric‘ AND last_name=‘Anido‘;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 300024 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+

如果频繁按名字搜索员工,这样显然效率很低,因此我们可以考虑建索引。有两种选择,建<first_name>或<first_name, last_name>,看下两个索引的选择性:

SELECT count(DISTINCT(first_name))/count(*) AS Selectivity FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.0042 |
+-------------+
SELECT count(DISTINCT(concat(first_name, last_name)))/count(*) AS Selectivity FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.9313 |
+-------------+

<first_name>显然选择性太低,<first_name, last_name>选择性很好,但是first_name和last_name加起来长度为30,有没有兼顾长度和选择性的办法?可以考虑用first_name和last_name的前几个字符建立索引,例如<first_name, left(last_name, 3)>,看看其选择性:

SELECT count(DISTINCT(concat(first_name, left(last_name, 3))))/count(*) AS Selectivity FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.7879 |
+-------------+

选择性还不错,但离0.9313还是有点距离,那么把last_name前缀加到4:

SELECT count(DISTINCT(concat(first_name, left(last_name, 4))))/count(*) AS Selectivity FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.9007 |
+-------------+

这时选择性已经很理想了,而这个索引的长度只有18,比<first_name, last_name>短了接近一半,我们把这个前缀索引 建上:

ALTER TABLE employees.employees
ADD INDEX first_name_last_name4 (first_name, last_name(4));

此时再执行一遍按名字查询,比较分析一下与建索引前的结果:

SHOW PROFILES;
+----------+------------+---------------------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                                           |
+----------+------------+---------------------------------------------------------------------------------+
|       87 | 0.11941700 | SELECT * FROM employees.employees WHERE first_name=‘Eric‘ AND last_name=‘Anido‘ |
|       90 | 0.00092400 | SELECT * FROM employees.employees WHERE first_name=‘Eric‘ AND last_name=‘Anido‘ |
+----------+------------+---------------------------------------------------------------------------------+


 
时间: 2024-12-15 01:52:37

mysql索引以及优化的相关文章

理解MySQL——索引与优化

转自:理解MySQL——索引与优化 写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多).如果对之建立B-Tr

理解MySQL——索引与优化(转)

理解MySQL--索引与优化 写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多).如果对之建立B-Tree索

(转)理解MySQL——索引与优化

参考资料:http://www.cnblogs.com/hustcat/archive/2009/10/28/1591648.html ———————————— 全文: 写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行1

mysql 索引与优化

写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多).如果对之建立B-Tree索引,则只需要进行log100(

sql学习笔记(15)-----------MySQL 索引与优化总结

索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点. 考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多).如果对之建立B-Tree索引,则只需要进行log100(10^6

理解MySQL——索引与优化(很强大)

写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记 录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4 个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要 100s(但实际上要好很多很多).如果对之建立B-Tree索引,则只需要进行log1

MySQL索引和优化查询

来自:http://blog.chinaunix.net/uid-29532375-id-4144615.html 索引和优化查询 恰当的索引可以加快查询速度,可以分为四种类型:主键.唯一索引.全文索引.普通索引.主键:唯一且没有null值.create table pk_test(f1 int not null,primary key(f1));alter table customer modify id int not null, add primary key(id);普通索引:允许重复的

关于mysql 索引自动优化机制: 索引选择性(Cardinality:索引基数)

1.两个同样结构的语句一个没有用到索引的问题: 查1到20号的就不用索引,查1到5号的就用索引,为什么呢?不稳定? mysql> explain select * from test where f_submit_time between '2009-09-01' and '2009-09-20' \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table:

MySQL索引与优化

1. 性能下降,sql执行时间长原因:查询语句没写好,索引失效,关联太多join,服务器参数设置不合理(JoinBuffer大小,SortBuffer大小,最大连接数)2. 使用join时应该小表驱动大表,小数据集驱动大数据集3. 索引:索引是帮助MySQL高效获取数据的一种数据结构,即索引的本质是数据结构.除了数据本身之外,MySQL数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的而基础上实现高效的数据查找算法,这种数据结构就是索引. 4