索引:系统根据某种算法,将已有的数据(未来可能新增的数据),单独建立一个文件。文件能够实现快速的匹配数据,并且能够快速的找到对应表中的记录。但是添加索引会导致插入、更新变慢–因为需要去更新相关的索引文件。
索引用于快速找出在某个列中有一特定值的行,不使用索引,MySQL必须从第一条记录开始读完整个表,直到找出相关的行,表越大,查询数据所花费的时间就越多,如果表中查询的列有一个索引,MySQL能够快速到达一个位置去搜索数据文件。
索引是一个单独的、物理的数据库结构,它是某个表中一字段或若干字段值
的集合 。表的存储由两部分组成,一部分用来存放数据 ,另一部分存放索引页面
。通常,索引页面相对于数据页面来说小得多。数据检索花费的大部分开销是磁盘读写,没有索引就需要从磁盘上读表的每一 个数据页,如果有索引,则只需查找索引页面就可以了。所以建立合理的索引,就能加速数据的检索过程。
【1】索引概述
① 索引的意义
- ① 提升查询数据的效率;
- ② 约束数据的有效性(唯一性等);
缺点:
- 创建索引和维护索引要耗费时间,并且随着数据量的增加所耗费的时间也会增加;
- 索引本身会产生索引文件,有时候甚至会比数据文件还大,非常耗费磁盘空间。
- 当对表中的数据进行增加、删除、修改时,索引也需要动态的维护,降低了数据的维护速度。
② 索引增加的前提条件
- 如果某个字段要作为查询条件经常使用,那么可以使用索引;
- 如果某个字段需要进行数据的有效性约束,也可能使用索引(主键,唯一键)。
- 对经常更新的表就避免对其进行过多的索引,对经常用于查询的字段应该创建索引,
- 数据量小的表最好不要使用索引,因为由于数据较少,可能查询全部数据花费的时间比遍历索引的时间还要短,索引就可能不会产生优化效果。
- 在一同值少的列上(字段上)不要建立索引,比如在学生表的"性别"字段上只有男,女两个不同值。相反的,在一个字段上不同值较多可是建立索引。
③ MySQL中索引的分类
索引按是否聚集可以分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。
按照索引类别可以分为以下四种:
- 主键索引:primary key ;
- 唯一索引:unique key ;
- 全文索引:fulltext index ;
- 普通索引:index 。
这里需要注意,全文索引。MySQL从3.23.23版开始支持全文索引和全文检索,FULLTEXT索引仅可用于 MyISAM 表;他们可以从CHAR、VARCHAR或TEXT列中作为CREATE TABLE语句的一部分被创建,或是随后使用ALTER TABLE 或CREATE INDEX被添加。
【2】索引表操作实践
① 添加/删除普通索引-index
- 添加索引,指定名字为index_name;
alter table tb_sys_user
add index index_name(column_name)
USING BTREE;
- 删除索引,如果没有指定名字,默认使用列名
alter table tb_sys_user drop index index_name
- 添加组合索引
alter table tb_sys_user add index index_name(column1,column2...)
- 添加唯一索引
ALTER TABLE `tb_user_add`
ADD UNIQUE INDEX `age_index` (`age`) USING HASH;
这里可以发现,USING BTREE/USING HASH 分别是使用不同的算法进行索引。
② 组合索引(最左前缀)
平时用的SQL查询语句一般都有比较多的限制条件,所以为了进一步榨取MySQL的效率,就要考虑建立组合索引。例如针对表user中字段user_name和user_pass建立一个组合索引:ALTER TABLE userADD INDEX index_name_pass (user_name(50),user_pass(10))
。建立这样的组合索引,其实是相当于分别建立了下面两组组合索引:
- user_name,user_pass
- user_name
为什么没有user_pass这样的组合索引呢?这是因为MySQL组合索引“最左前缀”
的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这两列的查询都会用到该组合索引,如下面的几个SQL所示:
explain select * from `user` where user_name='admin' and user_pass='123456';
explain select * from `user` where user_name='admin' ;
explain select * from `user` where user_pass='123456';
③ 查看某个表的索引
show index from table_name;
【3】索引的优化
过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。
索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。下面是一些总结以及收藏的MySQL索引的注意事项和优化方法。
① 何时使用聚集索引或非聚集索引
动作描述 | 使用聚集索引 | 使用非聚集索引 |
列经常被分组排序 | 使用 | 使用 |
返回某范围内的数据 | 使用 | 不使用 |
一个或极少不同值 | 不使用 | 不使用 |
小数目的不同值 | 使用 | 不使用 |
大数目的不同值 | 不使用 | 使用 |
频繁更新的列 | 不使用 | 使用 |
外键列 | 使用 | 使用 |
主键列 | 使用 | 使用 |
频繁修改索引列 | 不使用 | 使用 |
② 索引列的数据类型
- 越小的数据类型通常更好
越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快。 - 简单的数据类型更好
整型数据比起字符,处理开销更小,因为字符串的比较更复杂。在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;以及用整型数据类型存储IP地址。 - 索引不会包含有NULL值的列。
只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
③ 使用短索引
对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
④ MySQL查询只使用一个索引
如下图所示,user_name和user_email都建立了索引,但是查询的时候只使用到了user_name:
再如下图,主键索引存在时,优先使用主键索引:
⑤ 索引列排序
MySQL查询只使用一个索引
,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
⑥ like语句操作
一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 和%aaa不会使用
索引而like “aaa%”可以使用
索引。
⑦ 不要在列上进行运算
例如:select * from users where YEAR(adddate)<2007
,将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成:select * from users where adddate<’2007-01-01′
。
最后总结一下,MySQL只对一下操作符才使用索引:<,<=,=,>,>=,between,in,以及某些时候的like(不以通配符%或_开头的情形)
。而理论上每张表里面最多可创建16
个索引,不过除非是数据量真的很多,否则过多的使用索引也不是那么好玩的。
【4】BTREE和HASH索引
① B-Tree索引
假设有如下一个表:
CREATE TABLE People (
last_name varchar(50) not null,
first_name varchar(50) not null,
dob date not null,
gender enum('m', 'f') not null,
key(last_name, first_name, dob)
);
其索引包含表中每一行的last_name、first_name和dob列。其结构大致如下:
索引存储的值按索引列中的顺序排列。可以利用B-Tree索引进行全关键字、关键字范围和关键字前缀查询,当然,如果想使用索引,你必须保证按索引的最左边前缀(leftmost prefix of the index)来进行查询。
(1)匹配全值(Match the full value):对索引中的所有列都指定具体的值。例如,上图中索引可以帮助你查找出生于1960-01-01的Cuba Allen。
(2)匹配最左前缀(Match a leftmost prefix):你可以利用索引查找last name为Allen的人,仅仅使用索引中的第1列。
(3)匹配列前缀(Match a column prefix):例如,你可以利用索引查找last name以J开始的人,这仅仅使用索引中的第1列。
(4)匹配值的范围查询(Match a range of values):可以利用索引查找last name在Allen和Barrymore之间的人,仅仅使用索引中第1列。
(5)匹配部分精确而其它部分进行范围匹配(Match one part exactly and match a range on another part):可以利用索引查找last name为Allen,而first name以字母K开始的人。
(6)仅对索引进行查询(Index-only queries):如果查询的列都位于索引中,则不需要读取元组的值。
由于B-树中的节点都是顺序存储的,所以可以利用索引进行查找(找某些值),也可以对查询结果进行ORDER BY。当然,使用B-tree索引有以下一些限制:
- (1) 查询必须从索引的最左边的列开始。关于这点已经提了很多遍了。例如你不能利用索引查找在某一天出生的人。
- (2) 不能跳过某一索引列。例如,你不能利用索引查找last name为Smith且出生于某一天的人。
- (3) 存储引擎不能使用索引中范围条件右边的列。例如,如果你的查询语句为
WHERE last_name="Smith" AND first_name LIKE 'J%' AND dob='1976-12-23',
则该查询只会使用索引中的前两列,因为LIKE是范围查询。
② hash索引
MySQL中,只有MyISAM存储引擎显示支持hash索引,是MyISAM表的默认索引类型,尽管MyISAM表也可以使用B-Tree索引。MyISAM存储引擎支持非唯一hash索引,这在数据库领域是罕见的,如果多个值有相同的hash code,索引把它们的行指针用链表保存到同一个hash表项中。
假设创建如下一个表:
CREATE TABLE testhash (
fname VARCHAR(50) NOT NULL,
lname VARCHAR(50) NOT NULL,
KEY USING HASH(fname)
) ENGINE=MEMORY;
包含的数据如下:
假设索引使用hash函数f( ),如下:
f('Arjen') = 2323
f('Baron') = 7437
f('Peter') = 8784
f('Vadim') = 2458
此时,索引的结构大概如下:
Slots是有序的,但是记录不是有序的。当你执行
mysql> SELECT lname FROM testhash WHERE fname='Peter';
MySQL会计算’Peter’
的hash值,然后通过它来查询索引的行指针。因为f('Peter') = 8784
,MySQL会在索引中查找8784,得到指向记录3的指针。
因为索引自己仅仅存储很短的值,所以,索引非常紧凑。Hash值不取决于列的数据类型,一个TINYINT列的索引与一个长字符串列的索引一样大。
Hash索引有以下一些限制:
- (1)由于索引仅包含hash code和记录指针,所以,MySQL不能通过使用索引避免读取记录。但是访问内存中的记录是非常迅速的,不会对性造成太大的影响。
- (2)不能使用hash索引排序。
- (3)Hash索引不支持键的部分匹配,因为是通过整个索引值来计算hash值的。
- (4)Hash索引只支持等值比较,例如使用
=,IN( )和<=>
。对于WHERE price>100
并不能加速查询。
参考博文:
MySQL索引使用策略及优化实例MySQL索引使用和优化MySQL索引背后的数据结构及算法原理