MySQL唯一索引和普通索引选哪个?

想象这样一个场景,在设计一张用户表时,每人的身份证号是唯一的,需要搜索。但由于身份证号字段较大,不好将其作为主键。在业务代码已经保证插入身份证唯一的情况下,可以选择建立唯一索引和普通索引,这时该如何选择呢?接下来,将从查询和更新的执行过程进行分析。

查询过程

假设 k 是表 t 上的索引,在搜索 select id from t where k=5 时,会先从 k 这棵 B+ 的树根开始,按层搜索叶子节点,找到 k=5 的数据页,然后在数据页内容进行二分法定位。

对于普通索引,找到 k=5 的记录后,会继续向下查找一个,直到碰到第一个不是 5 的记录结束。

对于唯一索引,由于取值唯一,找到后直接停止。

由于 InnoDB 是按照数据页为单位(数据页默认 16 KB)进行读写的,在读取一条数据时,会将整个数据页整体读到内存。 在读入内存的数据页中,如果包含 k=5 的记录,在查询的情况下,唯一索引比普通索引多了一次查找和判断的过程,可以忽略。

如果 k=5 是当前数据页的最后一条,就需要在读取下一个数据页。但这发生的概率较低,也可以忽略。

所以总得来说,普通索引和唯一索引在查询的过程中差异不大。

change buffer

在分析唯一索引和普通索引的影响前,先来认识一下 change buffer 这个结构。

什么是 change buffer ?

在执行更新操作时,如果要更新的数据页在内存中就直接更新,否则的话,在不影响数据一致性的前提下,InnoDB 会将更新操作缓存在 change buffer 中,从而省去了从磁盘读取数据页的过程。在下次查询操作读取到恰好需要更新的数据页时,会将 change buffer 的更新语句执行,写入数据页。将操作应用到硬盘的过程叫 merge. 后台线程会定期 merge 或 数据库正常关闭时,也会进行 merge 操作。

merge 的执行流程:

  1. 从磁盘读入老版本数据页。
  2. 从 change buffer中找出和该数据页关联的记录,依次应用,得到新版数据页。
  3. 写 redo log,记录数据的变更和 change buffer 的变更。

change buffer 实际上是可以持久化到硬盘中的数据,也就是说在内存和硬盘上都 change buffer 的存在。change buffer 之前叫 insert buffer,开始只对 insert buffer 有优化,后来加上了对 delete 和 update 的支持,进而改名叫 change buffer。

可以看到,先将更新操作记录在 change buffer,减少了将磁盘数据页读取到内存的过程,语句的执行速度会有很明显的提升。同时,将数据读入内存,会占用 buffer pool 内存,所以减少读操作,还提高了内存使用率。

Buffer Pool 是内存中的一个区域,InnoDB 在访问表和索引数据时会在其中进行缓存。允许在内存中直接更新经常使用的数据,来加快处理速度。在一些专用的服务器上,会将 80% 的物理内存分为 buffer pool.

可以通过 innodb_change_buffer_max_size 来设置 change buffer 占用 buffer pool 的大小。

change buffer 应用场景?

如上面提到,change buffer 预先保存了更新记录,减少了读取数据页的过程,从而提高性能。也就是说如果 change buffer 中针对不同的数据页如果包含的更新记录越多,其实收益也就越大。

因此对于写多读少的业务(更新完立即查询)change buffer 发挥的作用也就越大。如常见的账单类,日志类等系统。

如果业务是更新完立即查询,虽然可以将更新记录放在 change buffer 中,但由于之后要马上查询数据页,所以会立即触发 merge 过程。这样随机访问 IO 次数并不会减少,反而增加了 change buffer 的维护代价,起到反效果。

更新过程

对于唯一索引来说,所有的更新操作都需要判断是否违反唯一性约束。所以必须把所需要的数据页读入内存,然后直接更新就可以,不需要使用 change buffer. 所以 change buffer 只对普通索引有用。

具体分析下,对于一张表插入一个新记录:

如果新记录要更新的数据页在内存中:

对于唯一索引,找到合适的位置,判断有没有冲突,插入值,语句结束。

对于普通索引:找到位置,插入值,语句结束。

所以数据页在内存时,唯一和普通索引就差一个判断的过程。可以忽略。

如果新记录要更新的数据页不在内存中:

对于唯一索引,将数据页读入内存,判断冲突,插入,语句结束。

对于普通索引,将语句记录在 change buffer 中,语句结束。

由于从磁盘到内存涉及随机 IO 访问,是数据库成本最高的操作之一。普通索引比唯一索引减少的读入操作,可以有很好的性能提升。

唯一或普通索引的选择

通过在查询和更新方面,两者的比较。我们知道,在查询过程中,除了极特殊情况,其实两者的差异并不大。

主要的差异是在更新过程中,要更新的数据页并不在内容中的情况。这时唯一索引,由于需要唯一性检查,不能利用 change buffer. 多了从磁盘到内容读取数据的过程,其中涉及随机 IO 的访问,相对来说效率就低了。

所以如果业务需要更新不错的性能,这时可以选用普通索引。当然一切都是建立在能保证数据准确性的前提下。

当如果更新后来紧接着查询操作,可以考虑关掉 change buffer. 其他的情况,change buffer 都能有很好的提升。

特别针对机械硬盘,change buffer 效果很显著。

redo log 和 change buffer 的比较

InnoDB 中 redo log 的出现使其具有了 crash-safe 的能力,同时还提高了效率,通过 WAL 先写日志,再写磁盘。

而 change buffer 是节省了从磁盘读入数据页到内存的随机IO过程。

下面通过一条插入语句来分析下两者间的关系:

mysql> insert into t(id,k) values(id1,k1),(id2,k2);

假设 k 为普通索引,k1 所插入的数据页在内存中, k2 不在。

执行插入操作时,主要涉及了图中这四部分的内容:

InnoDB buffer pool:内存区域

redo log:日志

system table space(ibdata1):系统表空间

data(t.idb): 数据表空间

innodb_file_per_table 开启时,表被创建在独立的表空间下,否则的话被创建在系统的表空间下。

执行过程如下:

  1. k1 所在的 page1 在内存中,直接更新内存
  2. k2 所在的 page2 不在内存中,记录在 change buffer.
  3. 将 k1 和 k2 的操作记录在 redo log.
  4. 提交事务。

可以看到这条更新语句(包括插入,删除,更新操作)执行成本很低,两次写入内存,1次顺序写入磁盘。虚线的操作,是后台操作,不影响响应时间。

再来看一条查询语句:

select * from t where k in (k1, k2)

假设读语句发生在更新语句不久,内存数据还在,此时读操作就和系统表空间和 redo log 无关。

执行过程:

  1. 读取 k1 所在的 page1,在内存中,直接返回。注意,并没有读磁盘上的数据,而且磁盘上的数据还有可能是之前的版本的。
  2. 读取 k2 所在的 page2,这时需要将 page2 从磁盘加载到内存,并应用 change buffer 的内容,然后返回正确的结果。从这里也能看出,change buffer 不适用于更新完立马去读的情况。

总结下 redo log 和 change buffer 的关系:

存储位置:change buffer 也会持久化在硬盘里,但保存在系统表空间 ibdata1 里。而 redo log 是单独的文件。

记录内容:change buffer 记录的是更新操作的内容,而 redo log 记录的是普通数据页的修改和 change buffer 的改动。

同步磁盘过程:同步内存中数据页的修改时通过 merge 操作进行的,而不是根据 redo log.

从更新的过程来看: redo log 将随机写磁盘的 IO 转换成了顺序写,而 change buffer 则是节省了随机读磁盘的 IO 消耗。

如果服务器异常掉电,会不会导致 change buffer 丢失?

并不会,因为 change buffer 中的数据已经被记录到 redo log 中,所以不会丢失。

由于 change buffer 一部分数据在磁盘,一部分在内存。对于在磁盘的数据已经 merge 所以不会丢失。
对于在内存中的数据:

  1. 如果 change buffer 写入,但 redo log 未提交,binlog 未提交,事务会回滚,这部分数据不存在。
  2. 如果 change buffer 写入,redo log 写入,binlog 写入,并已提交,不会丢失。从 redo log 直接恢复。
  3. 如果 change buffer 写入,redo log 写入但未 commit,binlog 写入,从 binlog 恢复 redo log 再恢复 change buffer.

参考资料

Buffer Pool

以上就是MySQL唯一索引和普通索引选哪个?的详细内容,更多关于MySQL唯一索引和普通索引的资料请关注我们其它相关文章!

(0)

相关推荐

  • MySQL选错索引的原因以及解决方案

    MySQL 中,可以为某张表指定多个索引,但在语句具体执行时,选用哪个索引是由 MySQL 中执行器确定的.那么执行器选择索引的原则是什么,以及会不会出现选错索引的情况呢? 先看这样一个例子: 创建表 Y,设置两个普通索引, 创建一个存储过程用于插入数据. MySQL: 5.7.27, 隔离级别: RR CREATE TABLE `Y` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, `b` int(11) DE

  • MySQL索引失效的几种情况汇总

    一.索引不存储null值 更准确的说,单列索引不存储null值,复合索引不存储全为null的值.索引不能存储Null,所以对这列采用is null条件时,因为索引上根本 没Null值,不能利用到索引,只能全表扫描. 为什么索引列不能存Null值? 将索引列值进行建树,其中必然涉及到诸多的比较操作.Null值的特殊性就在于参与的运算大多取值为null. 这样的话,null值实际上是不能参与进建索引的过程.也就是说,null值不会像其他取值一样出现在索引树的叶子节点上. 二.不适合键值较少的列(重复

  • mysql下普通索引和唯一索引的效率对比

    今天在我的虚拟机中布置了环境,测试抓图如下: 抓的这几个都是第一次执行的,刷了几次后,取平均值,效率大致相同,而且如果在一个列上同时建唯一索引和普通索引的话,mysql会自动选择唯一索引. 谷歌一下: 唯一索引和普通索引使用的结构都是B-tree,执行时间复杂度都是O(log n). 补充下概念: 1.普通索引 普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度.因此,应该只为那些最经常出现在查询条件(WHEREcolumn=)或排序条件(ORDERBYcolumn

  • MySql索引提高查询速度常用方法代码示例

    使用索引提高查询速度 1.前言 在web开发中,业务模版,业务逻辑(包括缓存.连接池)和数据库这三个部分,数据库在其中负责执行SQL查询并返回查询结果,是影响网站速度最重要的性能瓶颈.本文主要针对Mysql数据库,在淘宝的去IOE(I 代表IBM的缩写,即去IBM的存储设备和小型机:O是代表Oracle的缩写,去Oracle数据库,采用Mysql和Hadoop代替:E是代表EMC2,去EMC2的设备性,用PC server代替EMC2),大量使用Mysql集群!而优化数据的重要一步就是索引的建立

  • MySQL普通索引和唯一索引的深入讲解

    场景 1.维护一个市民系统,有一个字段为身份证号 2.业务代码能保证不会写入两个重复的身份证号(如果业务无法保证,可以依赖数据库的唯一索引来进行约束) 3.常用SQL查询语句:SELECT name FROM CUser WHERE id_card = 'XXX' 4.建立索引 身份证号比较大,不建议设置为主键 从性能角度出发,选择普通索引还是唯一索引? 假设字段k上的值都不重复 查询过程 1.查询语句:SELECT id FROM T WHERE k=5 2.查询过程 通过B+树从树根开始,按

  • MySQL唯一索引和普通索引选哪个?

    想象这样一个场景,在设计一张用户表时,每人的身份证号是唯一的,需要搜索.但由于身份证号字段较大,不好将其作为主键.在业务代码已经保证插入身份证唯一的情况下,可以选择建立唯一索引和普通索引,这时该如何选择呢?接下来,将从查询和更新的执行过程进行分析. 查询过程 假设 k 是表 t 上的索引,在搜索 select id from t where k=5 时,会先从 k 这棵 B+ 的树根开始,按层搜索叶子节点,找到 k=5 的数据页,然后在数据页内容进行二分法定位. 对于普通索引,找到 k=5 的记

  • MongoDB性能篇之创建索引,组合索引,唯一索引,删除索引和explain执行计划

    一.索引 MongoDB 提供了多样性的索引支持,索引信息被保存在system.indexes 中,且默认总是为_id创建索引,它的索引使用基本和MySQL 等关系型数据库一样.其实可以这样说说,索引是凌驾于数据存储系统之上的另一层系统,所以各种结构迥异的存储都有相同或相似的索引实现及使用接口并不足为 奇. 1.基础索引 在字段age 上创建索引,1(升序);-1(降序): db.users.ensureIndex({age:1}) _id 是创建表的时候自动创建的索引,此索引是不能够删除的.当

  • MySQL中主键索引与聚焦索引之概念的学习教程

    主键索引 主键索引,简称主键,原文是PRIMARY KEY,由一个或多个列组成,用于唯一性标识数据表中的某一条记录.一个表可以没有主键,但最多只能有一个主键,并且主键值不能包含NULL. 在MySQL中,InnoDB数据表的主键设计我们通常遵循几个原则: 采用一个没有业务用途的自增属性列作为主键: 主键字段值总是不更新,只有新增或者删除两种操作: 不选择会动态更新的类型,比如当前时间戳等. 这么做的好处有几点: 新增数据时,由于主键值是顺序增长的,innodb page发生分裂的概率降低了:可以

  • MySQL索引之聚集索引介绍

    在MySQL里,聚集索引和非聚集索引分别是什么意思,有什么区别? 在MySQL中,InnoDB引擎表是(聚集)索引组织表(clustered index organize table),而MyISAM引擎表则是堆组织表(heap organize table). 也有人把聚集索引称为聚簇索引. 当然了,聚集索引的概念不是MySQL里特有的,其他数据库系统也同样有. 简言之,聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序,而非聚集索引则就是普通索引了,仅仅只是对数据列创

  • MySQL数据库优化技术之索引使用技巧总结

    本文实例总结了MySQL数据库优化技术的索引用法.分享给大家供大家参考,具体如下: 这里紧接上一篇<MySQL数据库优化技术之配置技巧总结>,进一步分析索引优化的技巧: (七)表的优化 1. 选择合适的数据引擎 MyISAM:适用于大量的读操作的表 InnoDB:适用于大量的写读作的表 2.选择合适的列类型 使用 SELECT * FROM TB_TEST PROCEDURE ANALYSE()可以对这个表的每一个字段进行分析,给出优化列类型建议 3.对于不保存NULL值的列使用NOT NUL

  • Mysql如何适当的添加索引介绍

    这里先简单介绍一下索引: 添加索引是为了提高数据库查询性能,索引是最物美价廉的东西了,不用加内存,不用改程序,不用调sql,只要执行个正确的create index ,查询的速度就可能提高百倍千倍,这可是有诱惑力的,可是天下没有没费的午餐,查询的速度的提高是以牺牲insert update delete的速度为代价的.而且索引大小一般是数据的三分之一  ,再加上索引要加载进内存的,如果全部字段都加索引会以牺牲内存为代价的,所以才要设当的添加索引. 这里简单介绍一下mysql中常用索引: 在添加索

  • MySql 知识点之事务、索引、锁原理与用法解析

    本文实例讲述了MySql 知识点之事务.索引.锁原理与用法.分享给大家供大家参考,具体如下: 事务 事务概念 事务就是一组原子性的SQL查询,或者说一个独立的工作单元.如果数据库引擎执行一组操作语句,那么久执行所有的操作,如果其中有任何一条崩溃或其他原因无法执行,所有语句将不会执行.也就是说事务内的语句,要么全部执行成功,要么全部执行失败. 事务特性ACID 原子性(atomicity) 一个事务被视为最小工作单元,不可拆分,整个事务所有的操作要么全部提交成功,要么全部失败回滚,不可只执行部分.

  • MySQL中冗余和重复索引的区别说明

    MySQL允许在单个列上创建多个索引,无论是有意还是无意,MySQL需要单独维护这些重复索引,优化器在优化查询时也需要逐个考虑这会影响MySQL的性能 概念阐述 重复索引: 在相同的列上按照相同的顺序创建的相同类型的索引.应该避免创建这样的重复索引,发现之后也应该立即移除. 冗余索引: 两个索引按照相同的顺序覆盖了相同的列. 创建的原因 一般来说,我们有时候会在不经意间创建了重复索引,例如下面的例子: CREATE TABLE test( ID INT NOT NULL PRIMARY KEY,

  • 浅谈MySql整型索引和字符串索引失效或隐式转换问题

    目录 问题概述 问题重现 问题引申 结论 问题概述 今天在上班时,DBA突然找出来一段sql,表示该sql存在隐式转换,不走索引.经过我们的查看后,发现是类型varchar的字段, 我们使用条件传入了数值型的值,由于担心违反保密协议,在此就不贴图了,由我重现一下类似情况给大家看一下. 问题重现 首先我们先创建一张用户表test_user,其中USER_ID为了效果我们设置为varchar类型且加上唯一索引. CREATE TABLE test_user ( ID int(11) NOT NULL

  • mysql聚集索引、辅助索引、覆盖索引、联合索引的使用

    目录 聚集索引(Clustered Index) 辅助索引(Secondary Index) 覆盖索引(Covering index) 联合索引 <MySQL技术内幕 InnoDB存储引擎>学习笔记 聚集索引(Clustered Index) 聚集索引就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据. 举个例子,直观感受下聚集索引. 创建表t,并以人为的方式让每个页只能存放两个行记录(不清楚怎么人为控制每页只存放两个行记录): 最后<MySQL技术内幕>

随机推荐