mysql里创建‘联合索引’的意义?

因什么需求,要创建‘联合索引’?最实际好处在于什么?如果是为了更快查询到数据,有单列索引不是ok了,为什么有‘联合索引’的存在?求经验丰富老手谈谈。

  • 现在大家普遍的说法是因为,查询条件出现类似这类情况时‘where xx=xx && xx=xx && xx>xx’使用联合索引会比单列索引高效,所以要使用多列索引,但是经过测试,事情并非如此,反而是单列索引处理时间比多列索引还快....详见测试代码及结果http://sdrv.ms/19S68M9(大家一起来测试下,看看是不是单一偶然情况......)。

最后的答案我截取了Filix Suo第二个观点里一段话为答案,创建多列索引的意义就是为了‘减少io操作’。可能使用这个会相应失去什么,这个就有待大量测试考证了。

2013/11/25 23:37更新:
今天刚入手mariadb,结果在手册里翻到了一份多列索引结构的解释(嘿嘿,真开源果然不同),贴出来供大家参考:
https://mariadb.com/kb/en/index-condition-pushdown/
创建多列索引(列1,列2,列3)后的结构类似这样(index结构,附属列,附属列)
附属列可能直接跟在叶节点上,或单独存放(这个没有看到说明)...
附带可以说明单列索引就是多列索引一种形式,只不过没有附属列罢了。。。

阅读 28.9k
评论 更新于 2013-11-26
    5 个回答

    简单的说有两个主要原因:

    1. "一个顶三个"。建了一个(a,b,c)的复合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,因为每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,这可是不小的开销!
    2. 覆盖索引。同样的有复合索引(a,b,c),如果有如下的sql: select a,b,c from table where a=1 and b = 1。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一
    3. 索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select * from table where a = 1 and b =2 and c = 3,假设假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W*10%=100w 条数据,然后再回表从100w条数据中找到符合b=2 and c= 3的数据,然后再排序,再分页;如果是复合索引,通过索引筛选出1000w *10% *10% *10%=1w,然后再排序、分页,哪个更高效,一眼便知
    评论 赞赏 2013-11-25

      如下的有a,b,c 三个key的table

      create table test(
      a int,
      b int,
      c int,
      );
      

      如果我们
      需要执行很多的类似于 select * from test where a=10, b>50, c>20
      这类的组合查询 那么,我们可能需要创建 包含[a,b,c] 的联合索引,而单独的[a][b] [c]上的索引是不够的。
      (可以把一个索引想象成 sorted list).创建了 (a,b,c)的索引相当于 按照a,b,c 排序(排序规则是

      if(X.a>Y.a)
        return '>';
      else if(X.a<Y.a)
        return '<';
       else if(X.b>Y.b)
        return '>';
       else if (X.b<Y.b)
        return '<';
       else if (X.c>Y.c)
        return  '>'
       else if (X.c<Y.c)
        return  '<'
       esle
        return '=='
      


      和分别 按a 排序 分别按b排序 分别按照c排序是不一样的。

      其中 a b c 的顺序也很重要,有时可以是a c b,或者b c a等等。
      如果创建 (a,b,c)的联合索引,查询效率如下:

      优: select * from test where a=10 and b>50 差: select * from test where
      a50

      优: select * from test order by a 差: select * from test order by b 差:
      select * from test order by c

      优: select * from test where a=10 order by a 优: select * from test
      where a=10 order by b 差: select * from test where a=10 order by c

      优: select * from test where a>10 order by a 差: select * from test
      where a>10 order by b 差: select * from test where a>10 order by c

      优: select * from test where a=10 and b=10 order by a 优: select * from
      test where a=10 and b=10 order by b 优: select * from test where a=10
      and b=10 order by c

      优: select * from test where a=10 and b=10 order by a 优: select * from
      test where a=10 and b>10 order by b 差: select * from test where a=10
      and b>10 order by c

      图片

      参考 http://blog.csdn.net/lmh12506/article/details/8879916

      评论 赞赏 2013-11-23
        1. 在一次查询中,MySQL只能使用一个索引。
        2. 在真实项目中,SQL语句中的WHERE子句里通常会包含多个查询条件还会有排序、分组等。
        3. 若表中索引过多,会影响INSERT及UPDATE性能,简单说就是会影响数据写入性能。因为更新数据的同时,也要同时更新索引。
        4. 最实际的好处当然是查询速度快,性能好。

        举例:
        创建一张数据表

        CREATE TABLE `student` (
          `studentId` int(11) NOT NULL,
          `studentName` varchar(255) DEFAULT NULL,
          `gradeId` int(11) DEFAULT NULL,
          `schoolId` int(11) DEFAULT NULL,
          PRIMARY KEY (`studentId`),
          KEY `s_g_s` (`schoolId`,`gradeId`,`studentId`)
        ) 
        

        假设每个班有50名学生,一个年级有10个班,一所学校有4个年级,共有3所学校,那么总共会有6000名学生

        若要查询出第2所学校3年级学生的姓名,SQL如下
        SELECT studentName FROM student WHERE schoolId=2 AND gradeId=3
        如果使用名为s_g_s的联合索引,那通过索引,MySQL可筛选掉大部分不满足查询条件的学生信息,在这个例子中,可筛掉5500条记录,这样MySQL只需回表查询剩余的500条记录即可得到结果。
        如果只使用schoolId上的单列索引,只能筛掉4000条记录,需回表扫描过滤剩余的2000条记录才能得到结果,从数量上看差了一个数量级。性能自然不佳。

        若要查询出第2所学校3年级学生的姓名并按照studentId倒排,SQL如下
        SELECT studentName FROM student WHERE schoolId=2 AND gradeId=3 ORDER BY studentId DESC
        这条SQL若没有多列索引,在较大数据量下性能会很差。但有了s_g_s索引,排序可以在索引上直接完成,不用MySQL取回记录后,再在内存或者磁盘上进行一次排序。性能提升很大。

        若要查询出第2所学校3年级学生的studentId,SQL如下
        SELECT studentId FROM student WHERE schoolId=2 AND gradeId=3
        对于这条查询,s_g_s索引包含所有需要查询的字段的值,MySQL根本不需要再去读取表中的记录,直接全部在索引上完成,这是性能最高的一种索引,通常称为“覆盖索引”。

        这个例子有点“简陋”,但还是可以说明一些问题。
        综上,在实际项目中,
        - 联合索引的使用要远多于单列索引。
        - 联合索引使用正确对性能提升很有帮助。
        - 怎么加索引,把哪些列加索引,加几个索引都是需要根据项目中使用到的SQL及数据表中数据的分布、数据的区分度去衡量的。
        - 使用MySQL explain工具来解析SQL查询执行计划。
        - 推荐看下这本书:《高性能MySQL》

        评论 赞赏 2013-11-23

          我认为是为了更快的区分出相同数据,有很多数据要区分多个属性是否相同,所以用到联合索引

          评论 赞赏 2013-11-23
            gmase
            • 499

            创建多列索引的意义就是为了‘减少io操作

            在mysql5.6版本之前大部分情况是不行的,即使你创建的多列索引,但是你的sql写的不对的话,用到的往往是第一列索引。

            建议你去看看mysql的5.6新特性ICP。

            再给你推荐一篇博文,能够较好的理解为什么会这样
            http://hedengcheng.com/?p=577

            评论 赞赏 2013-11-29
              撰写回答

              登录后参与交流、获取后续更新提醒