1. 什么是数据库索引
索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可以快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她,则与在表中搜索所用的行相比,索引有助于更快地获取信息。
索引的一个主要目的就是加快检索表中数据的方法,亦即能协助信息搜索者尽快的找到符合限制条件的记录ID的辅助数据结构。
现在我写一个存储过程,往数据库tb_person表里面插入100000条数据库
| 查询语句 | 查询时间 |
|---|---|
| select * from tb_person | 3.812s |
| select * from tb_person where name = 1 | 0.036 |
| select * from tb_person WHERE name = ‘5000050000’ | 0.032 |
| select * from tb_person WHERE id = ‘100001’ | 0.009 |
我们的表有三个字段 id, age, name 其中id是主键(有索引),其它字段是普通字段(没有索引),当我们根据字段name进行查询时,没有索引,必须遍历整个表,直到被找到为止。我们根据id查询,因为有索引,即可在索引中查找。由于索引是经过某种算法优化过,查找次数要少很多,索引,索引是用来定位的。索引也是一种数据结构。
我们根据上面的实验,可以看到性能大约在4倍左右。
2. 索引的种类
数据库索引好比是一本书前面的目录,能加速数据库的查询速度。索引能分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。
根据数据库的功能,可以在数据库设计器中创建三种索引:唯一索引、主键索引和聚集索引。
2.1唯一索引
唯一索引是不允许其中任何两行具有相同索引值的索引。当现有数据库中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。
2.2 主键索引
数据库经常有一列或多列组合,其值唯一标识表中的每一行,改行成为表的主键。在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。
- UNIQUE 约束唯一标识数据库表中的每条记录。
- UNIQUE 和 PRIMARY KEY 约束均为列或列集合提供了唯一性的保证。
- PRIMARY KEY 拥有自动定义的 UNIQUE 约束。
- 请注意,每个表可以有多个 UNIQUE 约束,但是每个表只能有一个 PRIMARY KEY 约束
2.3 聚集索引
在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。聚集索引和非聚集索引的区别,如字典默认按字母顺序排序,读者如知道某个字的读音可根据字母顺序快速定位。因此聚集索引和表的内容是在一起的。如读者需查询某个生僻字,则需按字典前面的索引,举例按偏旁进行定位,找到该字对应的页数,再打开对应页数找到该字。这种通过两个地方而查询到某个字的方式就如非聚集索引
索引的键值逻辑顺序决定了表数据行的物理存储顺序
在mysql的InnoDB类型数据库是按照主键进行聚集,如果没有定义主键,InnoDB会试着使用唯一的非空索引来代替。如果没有这种索引,InnoDB就会定义隐藏的主键然后在上面进行聚集。
我再写个存储过程就是在10w条数据,每3行删除一条数据
总数据量 主键是否连续 执行语句 消耗时间 10w 是 select * from test1.tb_person 0.080 7w 否 select * from test1.tb_person 0.078
这个很有意思,其实这就和主键聚集索引有关,这样做会增加表碎片,影响性能。
2.4 索引列
可以基于数据库表中的单列或多列创建索引。多列索引可以区分其中一列可能有相同值的行。如果经常同时搜索两列或多列或按两列或多列排序时,索引也很有帮助。例如,如果经常在同一查询中为姓和名两列设置判据,那么在这两列上创建多列索引将很有意义。
3. 基本特点
建立索引的目的是加快对表中记录的查找或排序。为表设置索引要付出代价的:
- 增加了数据库的存储空间
- 插入和修改数据库时要话费较多的时间(因为索引也随之变动)。
数据库索引就是为了提高表的搜索效率而对某些字段中的值建立的目录。
我创建了两个数据库test1、test2创建相同的数据库表,每个表10W条数据,test1数据库里的表使用主键索引,test2数据库里的表没有索引,我们来比较一下两个数据库的大小。我们可以很清楚发现需要维护索引的表和不需要索引的表的大小差
数据库表 主键是否有索引 执行语句 消耗时间 test1 是 select * from test1.tb_person 0.080 test2 否 select * from test2.tb_person 0.060 test1 是 select * from test1.tb_person where id = 2345 0.001 test2 否 select * from test2.tb_person where id = 2345 0.015 我们发现有时候增加索引查询性能反而下降
3.1 创建索引可以大大提高系统的性能(优点)
- 通过创建唯一索引,可以保证数据库表中每一行数据的唯一性。
- 可以大大加快数据库的检索速度
- 可以加速表与表之间的连接,特别是在实现数据的参考完整性方面特别有意义
- 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间
- 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能
3.2 增加索引也有许多不利的方面(缺点)
- 创建索引和维护索引要消耗时间,这种时间随着数据量的增加而增加
- 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
- 当对表中的数据进行增加、删除和修改的时候,索引也要动态地维护,这样就降低了数据的维护速度
4.注意事项
索引是建立在数据库表中的某些列的上面。在创建索引的时候,应该考虑在哪些列上可以创建索引,在哪些列上不能创建索引。
4.1 一般来说,应该在这些列上创建索引:
- 在经常需要搜索的列上,可以加快搜索的速度
- 在作为主键的列上,强调该列的唯一性和组织表中数据的排列结构
- 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
- 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序。加快排序查询时间;
- 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度;
4.2 对于有些不应该创建索引:
- 对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
- 对于那些只有很少数据值的列。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
- 对于那些定义为text、image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少,不利于使用索引。
- 当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是相互矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改操作远远多于检索操作时,不应该创建索引。
参考百度百科
5.优化LIMIT
在实际项目中我发现随着LIMIT[位置偏移量],行数,随着[位置偏移量]的增大性能慢慢下降
通过判断id的范围来分页
sql语句 消耗时间 select * from clz_data where 1=1 ORDER BY id asc LIMIT 99999, 10 0.086s select * from clz_data where 1=1 and id > 99999 ORDER BY id asc LIMIT 10 0.001s 局限性:id必须是自增长,并且是连续的。 order by 只能是id
子查询优化法
sql语句 消耗时间 select * from clz_data where 1=1 ORDER BY market_id asc LIMIT 99999, 10 22.752 select * from (select id from clz_data where 1=1 ORDER BY market_id asc LIMIT 99999, 10 ) a LEFT JOIN clz_data b on a.id = b.id 2.841s
6.使用联合索引
| sql语句 | 索引类型 | extra | 花费时间 |
|---|---|---|---|
| SELECT cd.dq,cd.jcdw,count(*) AS count,sum(CASE cd.sfhg WHEN ‘不合格’ THEN 1 ELSE 0 END) AS unqualified FROM clz_data cd WHERE 1 = AND cd.dq = ‘鄞州区’ GROUP BY cd.jcdw ORDER BY unqualified DESC | 没有索引 | Using where; Using temporary; Using filesort | 2.589s |
| dq | Using index condition; Using temporary; Using filesort | 4.677s | |
| dq,jcdw,sfhg | Using index condition; Using temporary; Using filesort | 4.838s | |
| dq_jcdw_sfhg | Using where; Using index; Using temporary; Using filesort | 0.148s |
- MySQL使用联合索引只能使用左侧的部分,例如INDEX(a,b,c),当条件为a或a,b或a,b,c时都可以使用索引,但是当条件为b,c时将不会使用索引。这好比一本先根据姓,再根据名进行排序的电话簿,当查找的时候有姓的条件,效率会比没有任何条件高;如果在姓的基础上还有名的条件,效率会更高;但若只有名的条件,电话簿将不起作用。
- 离散度更高的索引应该放在联合索引的前面,因为离散度高索引的可选择性高。考虑一种极端的情况,数据表中有100条记录,若INDEX(a,b)中a只有两种情况,而b有100种情况。这样对于查询唯一记录a = …,b = …时,先遍历全部索引看满足a条件的有50个索引节点,接下来还要再一个个遍历这50个索引节点。如果是INDEX(b,a),先遍历全部索引发现满足b条件的索引节点只有一个,再遍历这个节点发现也满足a条件。虽然最后都能找到那个唯一的索引节点,但是第二种索引顺序对引擎遍历索引效率有很大的提高(用电话薄的思想去思考问题)。
来自 http://blog.csdn.net/qq_33290787/article/details/51950256
总结:索引可以优化我们的查询单前提是合理得使用。