MySQL 索引使用案例

索引使用案例

支持多种过滤条件
假设要设计一个在线约会网站,用户信息表有很多列,包括国家、地区、城市、性别、眼睛颜色,等等。网站必须支持上面这些特征的各种组合来搜索用户,还必须允许根据用户的最后在线时间、其他会员对用户的评分等对用户进行排序并对结果进行限制。如何设计索引满足上面的复杂需求呢?

现在需要看看哪些列选择性更高,哪些列在WHERE 子句中出现得最频繁。country、sex 列的选择性通常不高,但可能很多查询都会用到。所以考虑到使用的频率,建议在创建不同组合索引的时候将(sex,country )列作为前缀。但根据经验不是不应该在选择性低的列上创建索引的吗?这么做有两个理由:

  • 第一点,如前所述几乎所有的查询都会用到sex 列。
  • 更重要的一点是,索引中加上这一列也没有坏处,即使查询没有使用sex列也可以通过下面的“诀窍”绕过。这个“诀窍”就是:如果某个查询不限制性别,那么可以通过在查询条件中新增AND SEX IN('m','f') 来让MySQL选择该索引。
    接下来,需要考虑其他常见WHERE 条件的组合,并需要了解哪些组合在没有合适索引的情况下会很慢。(sex,country,age )上的索引就是一个很明显的选择,另外很有可能还需要(sex,country,region,age )和(sex,country,region,city,age)这样的组合索引。
    将aging放在索引的最后是因为age列多半是范围查询,而查询只能使用索引的最左前缀,直到遇到第一个范围条件列。前面的列在where子句中基本都是等于条件,故放置在age之前。

如何为一些生僻的搜索条件(比如has_pictures、eye_color、hair_color 和education)来设计索引呢?这些列的选择性高、使用也不频繁,可以选择忽略它们,让MySQL多扫描一些额外的行即可。

避免多个范围条件
从EXPLAIN 的输出很难区分MySQL是要查询范围值(>,<),还是查询列表值(in,not in)。EXPLAIN 使用同样的词“range”来描述这两种情况,但可以从值的范围和多个等于条件来得出不同。在我们看来,列表查询就是多个等值条件查询。这两种访问效率是不同的。对于范围条件查询,MySQL无法再使用范围列后面的其他索引列,但是对于“多个等值条件查询”则没有这个限制。

优化排序
使用文件排序对小数据集是很快的,但如果一个查询匹配的结果有上百万行的话会怎样?例如如果WHERE 子句只有sex 列,如何排序?可以增加一些特殊的索引来做排序,例如,可以创建(sex,rating )索引用于下面的查询:

mysql> SELECT<cols> FROM profiles WHERE sex='M' ORDER BY rating LIMIT 10;

而即使有索引,如果用户界面上需要翻页,并且翻页翻到比较靠后时查询也可能非常慢。下面这个查询就通过ORDER BY 和LIMIT 偏移量的组合翻页到很后面的时候:

mysql> SELECT<cols> FROM profiles WHERE sex='M' ORDER BY rating LIMIT 100000;

无论如何创建索引,这种查询都是个严重的问题。因为随着偏移量的增加,MySQL需要花费大量的时间来扫描需要丢弃的数据(需要将行数据加载出来?)。一个更好的办法是限制用户能够翻页的数量,实际上这对用户体验的影响不大,因为用户很少会真正在乎搜索结果的第10000页。

优化这类索引的另一个比较好的策略是使用延迟关联,通过使用覆盖索引查询返回需要的主键,再根据这些主键关联原表获得需要的行。这可以减少MySQL扫描那些需要丢弃的行数。下面这个查询显示了如何高效地使用(sex,rating )索引进行排序和分页:

mysql> SELECT <cols> FROM profiles INNER JOIN (
 -> SELECT <primary key cols> FROM profiles
 -> WHERE x.sex='M' ORDER BY rating LIMIT 100000, 10
 -> ) AS x USING(<primary key cols>);

维护索引

减少索引和数据的碎片
B-Tree索引可能会碎片化,这会降低查询的效率。碎片化的索引可能会以很差或者无序的方式存储在磁盘上。
根据设计,B-Tree需要随机磁盘访问才能定位到叶子页,所以随机访问是不可避免的。然而,如果叶子页在物理分布上是顺序且紧密的,那么查询的性能就会更好。否则,对于范围查询、索引覆盖扫描等操作来说,速度可能会降低很多倍;对于索引覆盖扫描这一点更加明显。
行碎片(Row fragmentation),这种碎片指的是数据行被存储为多个地方的多个片段中。即使查询只从索引中访问一行记录,行碎片也会导致性能下降,InnoDB不存在这种碎片。
行间碎片(Intra-row fragmentation),行间碎片是指逻辑上顺序的页,或者行在磁盘上不是顺序存储的。行间碎片对诸如全表扫描和聚簇索引扫描之类的操作有很大的影响,因为这些操作原本能够从磁盘上顺序存储的数据中获益。
剩余空间碎片(Free space fragmentation),剩余空间碎片是指数据页中有大量的空余空。这会导致服务器读取大量不需要的数据,从而造成浪费。

对于MyISAM表,这三类碎片化都可能发生。但InnoDB不会出现短小的行碎片;InnoDB会移动短小的行并重写到一个片段中。可以通过执行OPTIMIZE TABLE 或者导出再导入的方式来重新整理数据,这对多数存储引擎都是有效的。

posted @   cd_along  阅读(237)  评论(0编辑  收藏  举报
编辑推荐:
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示