MySQL:设计索引的时候,我们一般需要考虑哪些因素

设计过程

(1)首先,我们针对业务需求建立好一张表的结构后,就知道这个表里有哪些字段,每个字段是什么类型的,会包含哪些数据。

(2)接着设计好表的结构后,接下来要做的,就是要设计表的索引。设计索引的时候,我们要考虑的第一点,就是未来我们对表进行查询的时候,大致会如何来查询

可能一开始我们设计完表的结构后,并不能知道未来会怎么查询表。这时我们完全可以在表结构设计完毕之后,先别急着设计索引,因为此时你根本不知道要怎么查询表。

(3)接着我们可以进入系统开发的环节,也就是根据需求文档把业务代码写好。

等系统差不多开发完毕之后,功能都跑通了,这时就可以考虑来如何建立索引了,因为此时系统所有的SQL都已经写完了,你完全知道对每一张表会发起什么样的查询。

在互联网公司里,我们比较崇尚的是尽量写简单的SQL,复杂的逻辑用业务系统来实现就可以了,SQL能用单表查询就不要多表查询,能多表关联就尽量别写子查询,能写几十行SQL就别写几百行的SQL,多考虑用业务代码在内存里面实现一些数据的复杂计算逻辑,而不是放在SQL里做

其实一般的系统,只要你SQL语句尽量简单,然后建好必要的索引,每条SQL都可以走索引,数据库性能往往不是什么大问题。复杂SQL调优主要是针对那些是在没办法必须写上百行的复杂SQL来说的

设计原则

那么这个时候,第一个索引设计原则就有了:针对你的SQL语句里的where条件、order by条件以及group by条件去设计索引

  • 也就是说,

  • 比如你有一个联合索引是INDEX(a,b,c),此时你一看发现有三个SQL,包含了where a=? and b=?,order by a,b,group by a这些部分,那么此时where、order by、group by后续跟的字段都是联合索引的最左侧开始的部分字段,这就可以了,说明你的每个SQL语句都会用上你的索引了。

  • 所以在设计索引的时候,首先第一条,就是要按照这个原则,去保证你的每个SQL语句的where、 order by和group by都可以用上索引。

第一步:思考,你的where条件里是要根据哪些字段来筛选数据?order by要根据哪些字段来排序?group by要根据哪些字段来分组聚合

问题是:比如说我们要写类似:select xx from user_info where xx=xx order by xx limit xx, xx

  • 第一个难题是,往往在类似这种SQL里,你的where条件和order by排序实际上大部分情况下是没法用到索引的
  • 那在where和order by出现索引设计冲突时,到底是针对where去设计索引还是针对order去设计索引呢?
    • 其实这个问题的本质是,你是要让where语句先基于联合索引去进行一个筛选,筛选出来一部分用户指定的数据,接着再把数据加载到内存或者是基于临时磁盘文件去进行指定条件的排序,最后用limit语句拿一页数据;还是说先让order by语句按照你的索引的顺序去找,找的过程中基于where里的条件筛选出来指定的数据,然后再根据limit语句拿出来一页数据?
    • 答案是一般都是让where条件去使用索引来快速筛选出一部分指定的数据,接着再进行排序,最后针对排序后的数据拿出来一页数据
    • 因为基于索引进行where筛选往往可以最大速度筛选出你要的少部分数据,如果筛选出的数据量不是太大的话,后面后继排序和分页的成本往往不是太大。

可能会有多个字段需要在where、group、order by中使用,我们应该选择哪些字段来建立索引呢?考虑如下问题:

(1)首先一个是字段基数问题。

  • 举个例子,有一个字段一共在十万行数据里有1十万个值。这十万个值要么是0要么是1,那么它的基数就是2。因为这个字段的值就两选择:0和1
  • 假如你要是针对上面说的这种字段建立索引的话,那就还不如全表扫描了,因为你的索引树里就仅仅包含0和1两种值,根本没法进行快速的二分查找,也根本就没有太大的意义了。所以这种时候,选用这种基数很低的字段放索引里意义就不大了
  • 一般建立索引的时候,尽量使用那些基数比较大的字段,就是值比较多的字段,这样才能快速发挥出B+树快速二分查找的优势来
  • 怎么知道这个字段的基数大不大呢?
    • select count(distinct(column_name))/count(*) from table_name; 超过了0.5就适合做索引,越接近1越适合
      • 关于列的离散度:公式: count(distinct(column_name)) : count(*),列的全部不同值和所有数据行的比例。
      • 也就说:如果列的重复值越多,离散度就越低,重复值越少,离散度就越高(索引的基数与表总行数越接近,列的离散度就越高。),它衡量的是*变量各个取值之间的差异程度
      • 等于1的表示这个列的数据没有重复的,选择率十分高,是最好的选择
      • 如果B+Tree里面重复值太多,MySQL的优化器发现走索引和使用全表扫描查不了多少的时候,就是建了索引,也不一样会走索引

(2)其次是尽量选择字段的类型比较小的列来设计索引

  • 比如说tinyint之类的。因为它的字段类型比较小,说明这个字段自己本身的值占用的磁盘空间小,此时你在搜索的时候性能也会比较好一点
  • 不过这个所谓的字段类型比较小一点的列,也不是绝对的。很多时候你就是要针对varchar(255)这样的字段建立索引,哪怕多占一些磁盘空间,那也得需要去设计这样的索引。主要是尽量别把基数太低的字段包含在索引里,因为意义不是太大。
  • 当然,如果真的要对=有那种varchar(255)的字段,可能里面的值太大了,你觉得都放在索引树里太占空间了。此时优化方法是仅仅针对这个varchar(255)字段的前20个字符建立索引。也就是说,对这个字段的每个值的前20个字符放在索引树里而已,这就是前缀索引

(3)更新非常频繁的频繁的字段要不要作为主键。

  • 准确的来说,主键一定是自增的而且业务无关,别用UUID之类的,
  • 因为主键自增,那么起码其聚簇索引不会频繁的分裂,主键值都是有序的,就会自然的新增一个页**,但是如果你用的是UUID,那么也会导致聚簇索引频繁的页分裂

(4)尽量不要让你的查询语句里的字段搞什么函数,或者搞个计算(就是where = function(a) = xx),否则就会走全表扫描了

综上所述:

  • 对于那种字段基数很低的列尽量别包含到索引里去,没多大用
  • 另外就是对于那种比较长的字符类型的列,可以设计前缀索引,仅仅包含部分字符到索引树里去,where查询还是可以用的,但是order by和group by就用不上了
  • 尽量不要让你的查询语句里的字段搞什么函数,或者搞个计算
  • 主键一定是自增的而且业务无关

在最终所有的索引建立完成之后,大部分查询都应该能走索引。

第二步:我们选定了哪些字段需要建立索引之后,就要根据这些字段设计一个或者两个联合索引

  • 思考:哪些字段要放到联合索引中去?在联合索引里,字段的顺序要怎么排列呢?
  • 根据什么思考呢?
    • 尽量让每一个联合索引都尽量去包含上你的where、order by、group by里的字段
    • 字段顺序要符合最左匹配原则:仔细审查每个SQL语句,是不是每个where、order by、group by后面跟的字段顺序,都是某个联合索引的最左侧字段开始的部分字段
    • 如果你的where子句中有等值匹配,还有范围匹配。那么必须先让联合索引最左侧开始的多个字段使用等值匹配,最最后一个字段使用范围匹配
      • (in不是范围匹配,范围匹配是>=,range之类的)
      • 因为在SQL里,一旦你的一个字段做范围查询时用到了索引,那么这个字段接下来的条件都不能用索引了
  • 联合索引最多可以建立几个?两三个就足以
    • 业务系统跑起来之后肯定有数据插入也有查询的情况。此时查询应该都能走索引一般问题不会太大的,但是插入就有点讲究了。因为插入数据的时候,可能会更新索引树:
      • 系统插入数据肯定会有主键吧,那有主键就一定会更新聚簇索引树。
      • 系统插入一条数据时肯定会包含索引里的各个字段的值,因此其联合索引的B+树就也必须要更新
      • 另外,系统不停的增删改数据,就会不停的更新其索引树
    • 因为系统插入的数据值可能根本不是按顺序来的,很可能会导致索引树里的某个页自动分裂,这个页分裂的过程就很耗时间,因此一般大家设计索引别太多,建议两三个联合索引就应该覆盖调这个表的全部查询了。否则索引太多必然导致你增删改数据的时候性能很差,因为要更新多个索引树。

前缀索引应该截取多少字段作为前缀

什么是前缀索引?

  • 当字段值比较长的时候,建立索引会消耗很多的空间,搜索起来也会很慢。我们可以通过截取字段的前面一部分内容建立索引,这个就叫前缀索引
  • 建立前缀索引之后,其where查询还是可以用的,但是order by和group by就用不上了
  • 为什么要使用前缀索引呢?数据库的数据存储以页为单位,一页能存储的数据越多,一次IO操作获取的数据越大效率越高

前缀索引应该截取多少字段作为前缀

创建一张商户表,因为地址字段比较长,在地址字段上建立前缀索引:

create table shop(address varchar(120) not null);
alter table shop add key (address(12));

问题是,截取多少呢?截取得多了,达不到节省索引存储空间的目的,截取得少了,重复内容太多,字段的散列度(选择性)会降低。怎么计算不同的长度的选择性呢?

  • 先看一下字段在全部数据中的选择度:
select count(distinct address) / count(*) from shop;
  • 通过不同长度去计算,与全表的选择性对比:
select count(distinct left(address,10))/count(*) as sub10, count(distinct left(address,11))/count(*) as sub11, count(distinct left(address,12))/count(*) as sub12, count(distinct left(address,13))/count(*) as sub13
from shop

只要截取前 13 个字段,就已经有比较高的选择性了

建立前缀查询后,where查询还是可以用的,但是order by和group by就用不上了

比如,我们需要建立出了类似KEY my_index(name(20),age,course)这样的索引。假设name是varchar(255)类型的,但是在索引树里你对name的值仅仅提取前20个字符而已。

  • 此时你在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会
    • 先到索引树里根据name字段的前20个字段值去搜索
    • 定位到之后前20个字符的前缀匹配的部分数据之后
    • 再回到聚簇索引提取出来完整的name字段值进行比对就可以了
  • 但是假如你要是order by name,那么此时因为name在索引树中仅仅包含了前20个字符你,所以这个排序就没法用上索引了。group by也是同理。

哪些情况下数据库索引会失效

列和列对比

某个表中,有两列(id和c_id)都建了单独索引,下面这种查询条件不会走索引

select * from test where id=c_id;

这种情况会被认为还不如走全表扫描。

存在NULL值条件(is null/ is not null)

  • 索引中无法存储NULL值,所以where条件判断如果对字段进行了NULL值判断(is NULL/ is not null),则数据库放弃索引而进行全表查询
  • 我们在设计数据库表时,应该尽力避免NULL值出现,如果非要不可避免的要出现NULL值,也要给一个DEFAULT值,数值型可以给0、-1之类的, 字符串有时候给空串有问题,就给一个空格或其他
select * from test where id is not null;

  • 为什么索引列无法存储Null值?
    • 索引是有序的,NULL值进入索引时,无法确定其应该放在哪里(将索引值进行建树,其中必然涉及到诸多的比较操作,null值是不确定值无法比较,无法确定NULL出现在索引树的叶子节点位置)
  • 如果需要把空值存入索引,方法有二
    • 其一,把NULL值转为一个特定的值,在WHERE中检索时,用该特定值查找。(推荐)
    • 其二,建立一个复合索引。例如create index ind_a on table(col1,1); 通过在复合索引中指定一个非空常量值,而使构成索引的列的组合中,不可能出现全空值。

like查询以%开头

当使用模糊搜索时,尽量采用后置的通配符,

例如:name||’%’,因为走索引时,其会从前去匹配索引列,这时候是可以找到的,如果采用前匹配,那么查索引就会很麻烦,比如查询所有姓张的人,就可以去搜索’张%’。相反如果你查询所有叫‘明’的人,那么只能是%明。这时候索引如何定位呢?

前匹配的情况下,执行计划会更倾向于选择全表扫描。后匹配可以走INDEX RANGE SCAN。

所以业务设计的时候,尽量考虑到模糊搜索的问题,要更多的使用后置通配符。
在这里插入图片描述

条件上包括函数、表达式、 计算(+ - * /):

查询条件上尽量不要对索引列使用函数,比如下面这个SQL

select * from test where upper(name)='SUNYANG';

这样是不会走索引的,因为索引在建立时会和计算后可能不同,无法定位到索引。但如果查询条件不是对索引列进行计算,那么依然可以走索引。比如

select * from test where name=upper('sunyang');
--INDEX RANGE SCAN

这样的函数还有:to_char、to_date、to_number、trunc等

也不能有表达式、 计算(+ - * /):

explain SELECT * FROM `t2` where id+1 = 4;

数据类型出现隐式转换

  • 如果类型是字符串,那一定要在条件中将数据使用引号括起来,否则不使用索引
ALTER TABLE user_innodb DROP INDEX comidx_name_phone;
ALTER TABLE user_innodb add INDEX comidx_name_phone (name,phone);
explain SELECT * FROM `user_innodb` where name = 136;
explain SELECT * FROM `user_innodb` where name = '136';

在这里插入图片描述

如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)

在这里插入图片描述
要想使用or,又想让索引失效,只能将or条件中的每个列都加上索引

负向查询

Not Like不能

explain select *from employees where last_name not like 'wang';

!= (<>)和 NOT IN 在某些情况下可以:

explain select *from employees where emp_no not in (1)
explain select *from employees where emp_no <> 1

组合索引,不是使用第一列索引,索引失效(最左匹配原则)

如果mysql估计使用全表扫描比使用索引块,就不会使用索引

注意一个SQL语句是否使用索引,跟数据库版本、数据量、数据选择读都有关系。

其实,用不用索引,最终都是优化器说了算

优化器是基于什么的优化器?基于cost开销,它不是基于规则、也不是基于语义。怎么开销小就怎么来

查看索引的使用情况:

show status like ‘Handler_read%’;

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

在这里插入图片描述

实例:陌生人社交App索引创建

陌生人社交App有一个功能是通过一定的条件去搜索和选择,查找App上哪些用户可能比较符号你的期望,然后去和对方进行交友

那你筛选的时候,是针对社交App的哪个表进行查找呢?很明显是用户信息user_info表。这个表大致会包含哪些用户的个人信息呢?大致会包含你的地区(你在哪个省份、哪个城市,这个很关键,否则不在一个城市,可能线上聊的好,线下见面的机会都没有),性别,年龄,身高,体重,兴趣爱好,性格特点,还有照片,当然肯定还有最近一次在线时间(否则半年都不上线APP了,你把他搜出来干什么呢?)另外如果支持交友过程中让其他人对他进行评价,那么可能还需要包含这个人的一个综合评分。

针对这个用户表进行搜索,可不仅仅是筛选那么简单的,可能还需要分页显示,另外,搜索的时候一般还需要根据一定的规则对筛选出来的结果进行一个排序。

  • 我们在设计社交App的时候,怎么建立索引呢?
    • 这个时候我们接下来的问题是,用户在搜索潜在好友的时候,一般会用上哪些条件呢?我们到底要把哪些字段包含到索引里去?到底在联合索引里,字段的顺序要如何排列呢?
    • 因为用户在搜索潜在好友的时候,一般会根据省份、城市、性别这三个因素,这几个字段几乎是必选的,所以我们可以把省份、城市和性别三个字段,放在联合索引的最左侧,这样跟其他字段组合联合索引后,让大部分查找都可以直接通过索引树就可以把where条件指定的数据筛选出来了
    • 提问:但是不是说基数太低的最好别放在索引里去吗,省份、城市和性别,都是基数非常小的几个字段,可选的值就那么几个,为什么要放到索引里去?
    • 规则是死的,人是活的。假设你就因为省份、城市和性别几个字段的基数太小了,此时就不把它们几个包含到联合索引里去,那么你实际查询的时候都要基于这几个字段去搜索,此时你就只能把这几个字段放在where条件的最后,那么最后每次查询都必须要先用联合索引查询出来一部分数据,接着数据加载到内存里去,再根据where条件最好的省份、城市和性别几个字段进行过滤筛选,每次查询都得多这么一个步骤。所以我们根据省份、城市和性别三个字段建立联合索引
    • 也就是说,可以把基数较低但是频繁查询(几乎每次查询都会指定)的省份、城市和性别几个字段放到联合索引的最左侧去,此时就可以让每次查询时指定的省份、城市和性别,都直接从索引树里进行筛选。

现在已经把省份、城市和性别三个几乎每次查询都会加的条件放入了联合索引的最左侧去,那么这个联合索引里还要放哪些字段呢?

  • 分析这个问题之前,我们先来分析一个问题,那就是假设查询的时候,不指定性别,就指定了省份,城市,以及年龄,也就是说where provice=xx and city=xx and age between xx and xx,那么此时怎么办呢?因为age不在索引里,所以就根本没法通过age去索引里进行筛选了

  • 那如果把索引设计成(province,city,sex,age),此时你的语句写成where province=xx and city=xx and age >= xx and age <= xx,也是没法让age用上索引去筛选的,因为city和age之间隔了一个sex,所以此时就不符合最左侧连续多个字段的原则了。

  • 其实针对这个问题,大家完全没必要担心,因为假设有上诉场景,那么我们完全是可以把age放入联合索引的,设计成(province,city,sex,age)这样的索引,那么在搜索的时候就根据省份、城市和年龄来筛选,性别是不限的,此时where语句可以写成:where province=xxx and city = xx and sex in (‘female’, ‘male’) and age >= xx and age <= xx。

  • 另外,假设我们在查询语句里还有一些频繁使用的条件,通常都是兴趣爱好和性格特点,这个兴趣爱好和性格特点,往往都是有固定的一些枚举值的。比如兴趣爱好可以有下述的值可选:运动、电影、旅游、烹饪,性格特点可能包含下面的值:温柔、霸气、御姐、体贴、善良,等等。

  • 那么针对这样的一些频繁使用的包含枚举值范围的一些字段,也完全可以加入到联合索引里去,可以设计成(province, city, sex, hobby, character, age)这样的一个联合索引,此时假设出现了这样一个查询,按照省份、城市、性格和年龄进行搜索,此时SQL怎么写?还是用之前的那个策略和思路,就是写成where province=xx and city=xx and sex in(xx, xx) and hobby in (xx, xx, xx, xx) and character=xx and age>=xx and age<=xx

  • 也就是说,即使你不按性别和爱好进行筛选,但是在SQL里你可以对这两个字段用in语句,把它们所有的枚举值都放进去。这样的话,就可以顺利的让province,city,character和age四个真正要筛选的字段用上索引,直接在索引里进行筛选是没有问题的

  • 那为什么age字段一定要放在联合索引的最后一个呢?因为如果你的where语句里有等值匹配,还有范围匹配。那么必须先让联合索引最左侧开始的多个字段使用等值匹配,最最后一个字段使用范围匹配

  • 就比如上面的语句where province=xx and city=xx and sex in(xx, xx) and hobby in (xx, xx, xx, xx) and character=xx and age>=xx and age<=xx,他们完全是按照联合索引最左侧开始的,province、city、sex、hobby、character都是联合索引最左侧开始的多个字段,他们都是等值匹配,然后最后一个age字段使用的是范围匹配,这种就是可以完全用上索引的。

  • 但是假设你要是在联合索引里把age放在中间的位置,设计一个类似(province, city, sex, age, hobby, character)的联合索引,接着SQL写成where province=xx and city=xx and sex in(xx, xx) and age>=xx and age<=xx and hobby in (xx, xx, xx, xx) and character=xx的话,那么不好意思,只有 province, city, sex, age几个字段可以用上索引。

  • 因为在SQL里,一旦你的一个字段做范围查询时用到了索引,那么这个字段接下来的条件都不能用索引了。这是规则。所以说实际设计索引的时候,必须把经常用作范围查询的字段放在联合索引的最后一个,才能保证你的SQL里每个字段都能基于索引去查询。

下一个问题是,假设在查询的时候还有一个条件,是要根据用户最近登录时间在7天之内来进行筛选,筛选最近7天登录过APP的用户,那么实际上可能你的用户表里有这么一个字段,latest_login_time

  • 你要是在where条件里加入这么一个latest_login_time <= 7天内语句,肯定这个是没法用上索引了。因为你这里必然会用一些计算或者是函数,才能进行一些时间的比对。而且假设你的查询里还有age进行范围查询,那么我们之前说过,范围查询的时候,也就只有第一个范围查询是可以用上索引的,第一个范围查询之后的其他范围查询是用不上索引的。
  • 也就是说,即使你索引设计成这样:(province, city, sex, hobby, character, age, latest_login_time),然后你的where语句写成这样:where xx xxx and age>=xx and age<=xxx and latest_login_time>=xx,虽然age和latest_login_time都在联合索引里,但是按照规则,只有age范围查询可以用到索引,latest_login_time始终是用不到索引的。
  • 因此你在设计表的时候,就必须考虑到这个问题,此时你完全可以设计一个字段为:does_login_in_latest_7_days,也就是说,这个人是否在最近7天内登录过APP。假设在7天内登录了这个APP,那么这个字段就是1,否则超过7天没登录,这个字段就是0!这样就把一个时间字段转换为了一个枚举值的字段。
  • 接下来的解决方案就简单化了,可以设计一个联合索引为:(province, city, sex, hobby, character, does_login_in_latest_7_days, age),然后搜索的时候,一定会在where条件里带上一个does_login_in_latest_7_days=1,最后再跟上age范围查询,这样就可以让你的where条件里的字段都用索引来筛选。

实际上一般来说,假设你要是where语句里通过上述联合索引就可以过滤掉大部分的数据,就保留小部分数据下来基于磁盘文件进行order by语句的排序,最后基于limit进行分页,那么一般性能还是比较高的。

但有时候又怕一个问题,就是说万一你要是就仅仅使用联合索引里一些基数特别小的字段来筛选呢?

比如就基于性别来筛选,比如一下子筛选出所有的女性,可能有上百万用户数据,接着还要磁盘文件进行排序再分页?那这个性能可能就会极为的差劲了!

  • 所以针对上述问题,可以针对那种基数很低的字段再加上排序字段单独额外设计一个辅助索引,专门用于解决where条件里都是基数低的字段,然后还要排序后分页的问题,比如说就可以设计一个联合索引为:(sex, score)。
  • 此时万一你要是写出如下SQL:select xx from user_info where sex=‘female’ order by score limit xx,xx,此时假设用之前设计的那个联合索引,那绝对是完蛋了,因为根本没法用索引
  • 但是用我们设计的那个辅助的(sex, score)索引呢?此时因为where条件里的字段是等值匹配,而且还是等于某个常量值,所以虽然order by后跟的score字段是(sex, score)索引里的第二个字段,order by没有从索引最左侧字段开始排列,但是他也可以使用到索引来排序。
  • 因为具体到使用索引的层面,它会先对where条件里的sex=‘female’在索引树里筛选到这部分数据,接着在sex=‘female’的数据里,这些数据实际上都是排列在一起的,因为在索引里,会按照sex和score两个字段去排序,所以sex='female’的数据都是在一块的。然后找到这部分数据之后,接着就可以确定,这部分数据肯定是按照score字段进行排序的,此时就可以按照score字段值的顺序,去读取limit语句指定的数据分页了

所以此时这种针对sex低基数的字段的筛选和基于评分排序的语句,整体运行的效率是非常高的,完全可以基于辅助索引来实现。

以此类推,完全可以通过对查询场景的分析,用(province, city, sex, hobby, character,does_login_in_latest_7_days, age)这样的联合索引去抗下复杂的where条件筛选的查询,此时走索引筛选速度很快,筛选出的数据量较少,接着进行排序和limit分页。

同时针对一些低基数字段筛选+评分排序的查询场景,可以设计类似(sex, score)的辅助索引来应对,让他快速定位到一大片低基数字段对应的数据,然后按照索引顺序去走limit语句获取指定分页的数据,速度同样会很快。

总结:尽量利用一两个复杂的多字段联合索引,抗下你80%以上的查询,然后用一两个辅助索引抗下剩余20%的非典型查询,保证你99%以上的查询都能充分利用索引,就能保证你的查询速度和性能!

小结

索引应该怎么设计

  • 主键索引要必须以业务无关,并且是自增长的
  • 我们建立的索引大多是联合索引,写的SQL语句基本上也是根据联合索引来的,索引数量不宜太多,建议两三个联合索引
    • 哪些字段适合建立联合索引呢?
      • 思考第一原则:
        • where条件、order by条件、group by条件、jion的on字段查询频率高的字段创建索引。
          • 尽量让每一个联合索引都尽量去包含上你的where、order by、group by里的字段
          • 你的where条件和order by排序实际上大部分情况下是没法用到索引的,这个时候一般是让where条件去使用索引来快速选择出一些数据,然后排序,最后针对排序后的数据拿出来一页数据
        • 如果where子句后有范围查询,必须把经常用作范围查询的字段放在联合索引的最后一个。因为在SQL里,一旦你的一个字段做范围查询时用到了索引,那么这个字段接下来的条件都不能用索引了
      • 对所有适合建立联合索引的字段,怎么选择呢?
        • 频繁更新的值,不要作为主键或者索引 。页分裂
        • 若是不能有效区分数据的列不适合做索引列。离散度太低,导致扫描行数过多。
        • 索引字段的长度不能太长,我们要尽量选择字段的类型比较小的列来设计索引,如果一定需要对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间(此时where查询还是可以用的,但是order by和group by就用不上了)
        • 对于定义为text、image和bit的数据类型的列不要建立索引。
      • 在建立联合索引的时候,一定要把最常用的列放在最左边。如果有多个非常常用的字段,那么把散列性高(区分度高)的值放在前面。
      • 创建复合索引,而不是修改单列索引:
        • 尽量的扩展索引,不要新建索引
        • 比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
      • 创建索引时,应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值

哪些情况下数据库索引会失效

  • 不要对两个辅助索引比较,比如(where idx1 = idx2)
  • 不要在where条件中对索引进行NULL(is NULL/ is not null)判断(ps:我们在设计数据库表索引时,应该尽量避免NULL值出现,尽量设置一个DEFAULT值)
  • 在使用模糊搜索时,不要以 %开头。
  • 查询条件上尽量不要对索引列使用函数、表达式、 计算(+ - * /)
  • 如果类型是字符串,查询是一定要加引号,就是不要出现隐式转换
  • 组合索引时,不满足最左前缀匹配原则
  • 如果where条件中带有or,索引将失效
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值