mysql 索引创建和使用的经验原则

mysql:索引创建和使用的经验原则


本人并不是一个专业的dba,对于索引这么宏大的话题只能做一个泛泛而谈.由于不同引擎索引机制的差别,mysql版本的变化都会对索引产生巨大的影响.因此本文只是本人个人在平时的学习数据库索引的过程了解到知识作为一个记录.记录一些本人认为关于索引简单但是重要的知识点.


索引的基本常识

让我们用官网上面的第一段话开头:indexes are used to find rows with specific column values quickly. Without an index, MySQL must begin with the first row and then read through the entire table to find the relevant rows. 换而言之索引适用于快速查找某些行,而不需要让引擎去做全表扫描.当然这些做是有条件的,索引的创建和更新是需要资源的(CPU IO 内存 磁盘空间).所以索引通过牺牲插入和更新的效率来大幅度提高读的效率.
索引分单列索引和组合索引.单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引.组合索指的是一个索引包含多个列.
绝大多数的索引(PRIMARY KEY, UNIQUE, INDEX, and FULLTEXT)通常都是采用B-trees这一个数据结构,除了b-树之外,还有哈希索引,通过哈希函数的方法创建索引.memory引擎则支持显式的hash索引.innodb也支持hash索引的,但是我们必须启用(也就是说通常情况下是不启用的),开启后,hash索引的创建由InnoDB存储引擎引擎自动优化创建,我们干预不了.不过如果我们在innodb中十分需要hash索引来优化我们的查询,我们可以人工创建一个伪hash索引,(比如URL这样的长字符串创建索引是不合适的)我们可以创建一个短的带有 b-tree索引的字段,存储字段的哈希值,从而完成一个伪哈希索引.

索引的创建四个经验法则

关于索引的创建我给出四个经验法则
- 在经常使用的查询语句中,使用的where语句下,级联(join)查询下的on语句下使用的字段建立索引
- 在多列索引中将选择性最高的行放在第一列
- 确保在使用join 语句下两个连接之间的相同类型和大小的索引
- 对于常用的需要优化的语句,使用explain去查看索引的使用情况,用于优化索引

索引的基本原则与评价索引的好坏

<高性能MySQL>中给出了三个索引的基本原则.
- 单行访问是很慢的,特别是在机械硬盘中,最好的读取块中能包含可能足够多的行(实际上就是io),使用索引可以创建位置引用来提升效率.
- 按顺序访问数据是很快的.第一,顺序io不需要多次磁盘寻道.第二,如果服务器能够按照顺序访问数据,那么就不需要额外的排序操作,并且 GROUP BY查询也需咋做排序和行按组进行聚合计算.(也就是说要谨慎使用 group by 语句,尽量在主键上建立group by,如果不行就在group by 上建立索引)
- 索引的覆盖查询是很快的.如果一个索引包含查询需要的所有列,那么引擎就不需要在回表查找行,这样就避免大量的单行访问.

在这里我们给出一个评价方案出来,从上到下满足条件越多说明索引越好.
- 索引将相关的记录放在一起.这是一星
- 如果索引中的数据顺序和查找中的排序顺序一致将获得两星
- 如果索引中的列包含查询中需要的全部列则获得三星

主键

表的主键表示您在最重要的查询中使用的一列或一组列.它有一个关联索引,用于快速查询性能.查询性能受益于NOT NULL优化,因为它不能包含任何NULL值.借助InnoDB存储引擎主键通常会当做簇集索引,因此使用主键进行排序访问,会非常的快.

如果您的表很重要,但没有明显的列或一组列作为主键,那么您可以创建一个带有自动增量值的单独列作为主键.当您使用外键连接表时,这些唯一ID可用作指向其他表中相应行的
指针.
尤其是对于INNODB而言,更是如此.(这里可以参考<高性能MySQL>中关于簇集索引的章节)
对于主键而言我有以下三个建议:
- 不更新主键列的值
- 不允许重用主键列的值
- 不在主键列中使用可能会更改的值(例如,如果使用一个名字作为主键以标识某个供应商,应该供应商合并和更改其名字时,必须更改这个主键)

最左匹配原则

B+树的数据结构决定了在使用索引的时候必须遵守最左前缀原则,在创建联合索引的时,尽量将经常参与查询的字段放在联合索引的最左边.

一般情况下不建议使用like操作,如果非使用不可的话,需要注意:like ‘%abd%’不会使用索引,而like ‘aaa%’可以使用索引.这也是前面的最左前缀原则的一个使用场景.

组合索引与最左匹配

为了形象地对比单列索引和组合索引,为表添加多个字段:

CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL, age INT NOT NULL );

为了进一步榨取MySQL的效率,就要考虑建立组合索引.就是将 name,city,age建到一个索引里:

ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);

建表时,usernname长度为 16,这里用 10.这是因为一般情况下名字的长度不会超过10,这样会加速索引查询速度,会减少索引文件的大小.提高INSERT的更新速度.

建立这样的组合索引,其实是相当于分别建立了下面三组组合索引:

usernname,city,age

usernname,city

usernname

为什么没有 city,age这样的组合索引呢?这是因为MySQL组合索引“最左前缀”的结果.简单的理解就是只从最左面的开始组合.并不是只要包含这三列的查询都会用到该组合索引.下面的几个SQL就会用到这个组合索引:

SELECT * FROM mytable WHREE username=”admin” AND city=”郑州”

SELECT * FROM mytable WHREE username=”admin”

而下面几个则不会用到:

SELECT * FROM mytable WHREE age=20 AND city=”郑州”

SELECT * FROM mytable WHREE city=”郑州”

如果分别在 usernname,city,age上建立单列索引,让该表有3个单列索引,查询时和上述的组合索引效率也会大不一样,远远低于我们的组合索引.因为虽然此时有了三个索引,但MySQL只能用到其中的那个它认为似乎是最有效率的单列索引.

索引的使用原则

  • 索引字段尽量使用数字型(简单的数据类型)
    若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销.这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了

  • 尽量不要让字段的默认值为NULL
    在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂.

索引不会包含有NULL值的列,只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的.

所以我们在数据库设计时尽量不要让字段的默认值为NULL,应该指定列为NOT NULL,除非你想存储NULL.你应该用0、一个特殊的值或者一个空串代替空值.

  • 前缀索引和索引选择性
    对串列进行索引,如果可能应该指定一个前缀长度.

对于BLOB、TEXT或者很长的VARCHAR类型的列,必须使用前缀索引,因为MYSQL不允许索引这些列的完整长度.

前缀索引是一种能使索引更小、更快的有效办法,但另一方面也有其缺点:MySQL无法使用前缀索引做order by和group by,也无法使用前缀索引做覆盖扫描.

一般情况下某个前缀的选择性也是足够高的,足以满足查询性能.例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引.

不过这里我们也可以采用上面的伪哈希列索引的办法对于字符串列进行索引,当然这样的代价就在于业务层代码的逻辑会变得复杂化.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值