数据库索引


索引

创建并使用正确的索引【减少数据访问】

优点:加快检索速度、唯一索引保证每条数据唯一性等等,对生产系统的性能有质的提升;

缺点:索引会大大增加表记录的DML开销;

拓展:索引对DML(INSERT,UPDATE,DELETE)附加的开销有多少?

这个没有固定的比例,与每个表记录的大小及索引字段大小密切相关,以下是一个普通表测试数据,仅供参考:

索引对于Insert性能降低56%

索引对于Update性能降低47%

索引对于Delete性能降低29%

因此对于写IO压力比较大的系统,表的索引需要仔细评估必要性,另外索引也会占用一定的存储空间。

正确的索引可以让性能提升100,1000倍以上,不合理的索引也可能会让性能下降100倍,因此在一个表中创建什么样的索引需要平衡各种业务需求。

类别

主键索引(聚集索引)、唯一索引、普通索引、组合索引、全文索引

主键索引(聚集索引):primary key

例子:

ALTER TABLE `test_a` ADD INDEX `primary_key` (`id` ASC);

唯一索引:unique index

例子:

create unique index name_season_unique on test_a(name, season);

或者:

ALTER TABLE `test_a` ADD UNIQUE INDEX `name_season_unique` (`name` ASC, `season` ASC);

建议:尽量不用UNIQUE,由程序保证约束

普通索引:index

常用于

  • 搜索时经常使用到的字段
  • 用于连接其它表的字段
  • 用于外键字段
  • 高选中性的字段
  • order by子句中使用到的字段
  • xml类型

不适用于:
唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件,例如性别
更新非常频繁的字段不适合创建索引,例如登录次数,最后登录时间
不会出现在 where 子句中的字段不该创建索引
例子:

create index name_index on test_a(name);

或者:

ALTER TABLE `test_a` ADD INDEX `name_index` (`name` ASC);

组合索引(也叫复合索引、多列索引)

类似于普通索引,只是索引是放在多个列名上,例如字典表中部首+笔画的组合索引

create index name_no_index on test_a(name, season);

全文索引:fulltext index

create fulltext index name_fulltext_index on test_a(name);
-- 创建用于展示效果的数据
INSERT INTO `test_a` (`id`, `name`, `season`) VALUES ('6', '不知水果', '不知季度');
-- 展示效果
select match(name) against('不知水果') from test_a where id in(1, 6);
drop index name_fulltext_index on test_a;

0表示没有匹配到,或者你的词是停止词,是不会建立索引的.
使用全文索引,不能使用like语句,如果使用like这样就不会使用到全文索引了

在建表的时候建立唯一键、普通索引、唯一索引的方式如下:

PRIMARY KEY (`id`),
UNIQUE KEY `name_season_unique` (`name`,`season`),
KEY `name_index` (`name`)

拓展:
删除索引

DROP INDEX index_name ON table_name;

或者:

ALTER TABLE `table_name` DROP INDEX `index_name`;

查看索引:

show index from table_name;

修改索引
我们一般是先删除再重新创建,也可以直接进行修改

索引注意点

  • 索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
  • 应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描
  • 值分布很稀少的字段不适合建索引,例如”性别”这种只有两三个值的字段
  • 字符字段只建前缀索引
  • 字符字段最好不要做主键
  • 不用外键,由程序保证约束
  • 使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引
  • 查询要建立索引最重要的条件是查询条件中需要使用索引
  • 如果MySQL估计使用全表扫描要比使用索引快,则不使用索引

SQL什么条件会使用索引?

当字段上建有索引时,通常以下情况会使用索引:

  • INDEX_COLUMN = ?
  • INDEX_COLUMN > ?
  • INDEX_COLUMN >= ?
  • INDEX_COLUMN < ?
  • INDEX_COLUMN <= ?
  • INDEX_COLUMN between ? and ?
  • INDEX_COLUMN in (?,?,...,?)
  • INDEX_COLUMN like ?||'%'(后导模糊查询)
  • T1. INDEX_COLUMN=T2. COLUMN1(两个表通过索引字段关联)

SQL什么条件建了索引但不会使用索引?

查询条件

不能使用索引原因

示例

INDEX_COLUMN <> ?

INDEX_COLUMN not in (?,?,...,?)

不等于操作不能使用索引

 

function(INDEX_COLUMN) = ?

INDEX_COLUMN + 1 = ?

INDEX_COLUMN || 'a' = ?

经过普通运算或函数运算后的索引字段不能使用索引

 

INDEX_COLUMN like '%'||?

INDEX_COLUMN like '%'||?||'%'

含前导模糊查询的Like语法不能使用索引

对于使用like的查询,'aaa%' 会使用到索引,查询如果是'%aaa'不会使用到索引。 explain select * from dept where name like '研发部%'; -- 不会使用索引 explain select * from dept where name like '%研发部';

INDEX_COLUMN is null

B-TREE索引里不保存字段为NULL值记录,因此IS NULL不能使用索引

 

NUMBER_INDEX_COLUMN='12345'

CHAR_INDEX_COLUMN=12345

在做数值比较时需要将两边的数据转换成同一种数据类型,如果两边数据类型不同时会对字段值隐式转换,相当于加了一层函数处理,所以不能使用索引。

如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须'') -- 会应用索引 explain select * from dept where name = '研发部'; -- 不会使用索引并且报错 explain select * from dept where name = 研发部;

a.INDEX_COLUMN=a.COLUMN_1

给索引查询的值应是已知数据,不能是未知字段值。

 

对于创建的组合索引,只要查询条件使用了最左边的列,索引一般就会被使用,不是使用的第一部分,则不会使用索引

组合索引都是从第一个字段开始找起,看第一个条件字段是否是组合索引的第一个字段,如果是则使用索引,否则不使用

alter table dept add index name_loc (name, loc); explain select * from dept where name = '研发部'; explain select * from dept where name = '研发部' and loc = '大厦10楼1001'; -- 不会使用索引 explain select * from dept where loc = '大厦10楼1001';

如果条件中有or,即使其中有条件带索引也不会使用。

 

-- 我们把组合索引删除,然后只在name上加入索引 alter table dept drop index name_loc; alter table dept add index name_index (name); -- 使用了索引 explain select * from dept where name = '研发部'; -- key为NULL explain select * from dept where name = '研发部' or loc = '大厦10楼1001';

注:

经过函数运算的字段要使用索引可以使用函数索引,这种需求建议与DBA沟通。

我们一般在什么字段上建索引?

这是一个非常复杂的话题,需要对业务及数据充分分析后再能得出结果。主键及外键通常都要有索引,其它需要建索引的字段应满足以下条件:

1、字段出现在查询条件中,并且查询条件可以使用索引;

2、语句执行频率高,一天会有几千次以上;

3、通过字段条件可筛选的记录集很小,那数据筛选比例是多少才适合?

这个没有固定值,需要根据表数据量来评估,以下是经验公式,可用于快速评估:

小表(记录数小于10000行的表):筛选比例<10%

大表:(筛选返回记录数)<(表总记录数*单条记录长度)/10000/16

单条记录长度≈字段平均内容长度之和+字段数*2

例如:emp表中有600万的数据,单条记录长度 ≈ (7+6+6+3+8+4+1+10+7+6) + 11 * 2 = 70

筛选返回记录数就需要小于 6000000 * 70 / 10000 / 16 = 3625 左右即可

在emp的name字段中添加了索引,查看索引筛选比例如下,351小于预期值3625,说明在这个字段上建立索引是非常合理的

如何知道SQL是否使用了正确的索引?

简单SQL可以根据索引使用语法规则判断,复杂的SQL不好办,判断SQL的响应时间是一种策略,但是这会受到数据量、主机负载及缓存等因素的影响,有时数据全在缓存里,可能全表访问的时间比索引访问时间还少。要准确知道索引是否正确使用,需要到数据库中查看SQL真实的执行计划。

只通过索引访问数据

有些时候,我们只是访问表中的几个字段,并且字段内容较少,我们可以为这几个字段单独建立一个组合索引,这样就可以直接只通过访问索引就能得到数据,一般索引占用的磁盘空间比表小很多,所以这种方式可以大大减少磁盘IO开销。

如:select id,name from company where type='2';

如果这个SQL经常使用,我们可以在type,id,name上创建组合索引

create index my_comb_index on company(type,id,name);

有了这个组合索引后,SQL就可以直接通过my_comb_index索引返回数据,不需要访问company表。

切记,性能优化是无止境的,当性能可以满足需求时即可,不要过度优化。在实际数据库中我们不可能把每个SQL请求的字段都建在索引里,所以这种只通过索引访问数据的方法一般只用于核心应用,也就是那种对核心表访问量最高且查询字段数据量很少的查询。

优化SQL执行计划

SQL执行计划是关系型数据库最核心的技术之一,它表示SQL执行时的数据访问算法。由于业务需求越来越复杂,表数据量也越来越大,程序员越来越懒惰,SQL也需要支持非常复杂的业务逻辑,但SQL的性能还需要提高,因此,优秀的关系型数据库除了需要支持复杂的SQL语法及更多函数外,还需要有一套优秀的算法库来提高SQL性能。

目前ORACLE有SQL执行计划的算法约300种,而且一直在增加,所以SQL执行计划是一个非常复杂的课题,一个普通DBA能掌握50种就很不错了,就算是资深DBA也不可能把每个执行计划的算法描述清楚。虽然有这么多种算法,但并不表示我们无法优化执行计划,因为我们常用的SQL执行计划算法也就十几个,如果一个程序员能把这十几个算法搞清楚,那就掌握了80%的SQL执行计划调优知识。

查看索引的使用情况

show status like 'Handler_read%';

大家可以注意:
handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
handler_read_rnd_next:这个值越高,说明查询低效。

这时我们会看到handler_read_rnd_next值很高,这是因为我们前面没有加索引的时候,做过多次查询的原因.

定时清除不需要的数据,定时整理索引锁片

当创建了索引并且索引正常的工作,但性能仍然不好,那就可能是产生了索引碎片,需要进行索引碎片处理。
optimize table table_name;