MySQL的普通索引和唯一索引到底什么区别?

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 普通索引可重复,唯一索引和主键一样不能重复。唯一索引可作为数据的一个合法验证手段,例如学生表的身份证号码字段,人为规定该字段不得重复,那么就使用唯一索引。(一般设置学号字段为主键)

1 概念区分


普通索引 V.S 唯一索引

普通索引可重复,唯一索引和主键一样不能重复。

唯一索引可作为数据的一个合法验证手段,例如学生表的身份证号码字段,人为规定该字段不得重复,那么就使用唯一索引。(一般设置学号字段为主键)


主键 V.S 唯一索引

主键保证DB的每一行都是唯一、不重复,比如身份证,学号等,不重复。

唯一索引的作用跟主键一样。

但在一张表里面只能有一个主键,不能为空,唯一索引可有多个。唯一索引可有一条记录为null。


比如学生表:


在学校,一般用学号做主键,身份证号作为唯一索引

在教育局,就把身份证号弄成主键,学号作为唯一索引

所以选谁做主键,取决于业务需求。


2 案例


某居民系统,每人有唯一身份证号。若系统要按身份证号查姓名:


select name from CUser where id_card = 'ooxx';


id_card字段较大,不推荐做主键。现有如下选择:


在id_card创建唯一索引

创建一个普通索引

假定业务代码已确保不会写入重复身份证号,这两个选择逻辑上都正确。

但性能角度考虑,选择哪个呢?


假设字段 k 上的值都不重复。


InnoDB索引结构:

3.png


3 查询性能


select id from T where k=4


通过B+树从root开始层序遍历到叶节点,数据页内部通过二分搜索:


普通索引

查找到满足条件的第一个记录(4,400)后,继续查找下个记录,直到碰到第一个不满足k=4的记录

唯一索引

查到第一个满足条件的,就停止搜索

看起来性能差距很小。


InnoDB数据按数据页单位读写。即读一条记录时,并非将该一个记录从磁盘读出,而以页为单位,将其整体读入内存。


所以普通索引,多了一次“查找和判断下一条记录”的操作,即一次指针寻找和一次计算。

若k=4记录恰为该数据页的最后一个记录,则此时要取下个记录,还得读取下个数据页。

对整型字段,一个数据页可存近千个key,因此这种情况概率其实也很低。因此计算平均性能差异时,可认为该操作成本对CPU开销忽略不计。


4 更新性能


往表中插入一个新记录(4,400),InnoDB会有什么反应?


这要看该记录要更新的目标页是否在内存:


在内存

普通索引

找到3和5之间的位置,插入值,结束。


唯一索引

找到3和5之间的位置,判断到没有冲突,插入值,结束。


只是一个判断的差别,耗费微小CPU时间。


不在内存

唯一索引

将数据页读入内存,判断到没有冲突,插入值,结束。


普通索引

将更新记录在change buffer,结束。


将数据从磁盘读入内存涉及随机I/O访问,是DB里成本最高的操作之一。而change buffer可以减少随机磁盘访问,所以更新性能提升明显。


5 索引选择最佳实践


普通索引、唯一索引在查询性能上无差别,主要考虑更新性能。所以,推荐尽量选择普通索引。


若所有更新后面,都紧跟对该记录的查询,就该关闭change buffer。其它情况下,change buffer都能提升更新性能。

普通索引和change buffer的配合使用,对数据量大的表的更新优化还是明显的。


在使用机械硬盘时,change buffer收益也很大。所以,当你有“历史数据”库,且出于成本考虑用机械硬盘,应该关注这些表里的索引,尽量用普通索引,把change buffer开大,确保“历史数据”表的数据写性能。


6 change buffer 和 redo log


WAL 提升性能的核心机制,也是尽量减少随机读写,它们有啥区别?


6.1 插入流程

insert into t(id,k)

values (id1,k1),(id2,k2);


假设当前k索引树的状态,查找到位置后:


k1所在数据页在内存(buffer pool)

k2数据页不在内存

看如下流程:


带change buffer的更新流程

图中箭头都是后台操作,不影响更新请求的响应。

2.png



该更新做了如下操作:


Page1在内存,直接更新内存

Page2不在内存,就往change buffer区,缓存一个“往Page2插一行记录”的信息

将前两个动作记入redo log

至此,事务完成。执行该更新语句成本很低,只是写两处内存,然后写一处磁盘(前两次操作合在一起写了一次磁盘),还是顺序写。


6.2 处理之后的读请求


select * from t

where k

in (k1, k2);


读语句紧随更新语句之后,这时内存中的数据都还在,所以此时这俩读操作就与系统表空间和 redo log 无关。


带change buffer的读过程

1.png


读Page1时,直接从内存返回。


WAL之后若读数据,是否一定要读盘?一定要从redo log将数据更新后才能返回?

其实不用。看上图状态,虽然磁盘上还是之前的数据,但这里直接从内存返回结果,结果是正确的。


读Page2时,需将Page2从磁盘读入内存,然后应用change buffer里的操作日志,生成一个正确版本并返回结果。所以一直到需要读Page2时,该数据页才会被从磁盘读入内存。


综上,这俩机制的更新性能:


redo log 主要节省随机写磁盘的I/O消耗(转成顺序写)

change buffer主要节省随机读磁盘的I/O消耗


7 总结


因为唯一索引用不了change buffer,若业务可以接受,从性能角度,优先考虑非唯一索引。


到底何时使用唯一索引

问题就在于“业务可能无法确保”,而本文前提是“业务代码已保证不会写入重复数据”,才讨论的性能问题。


若业务无法保证或业务就是要求数据库来做约束

没有撤退可言,必须创建唯一索引。那本文意义就在于,若碰上大量插入数据慢、内存命中率低时,多提供了一个排查思路

“归档库”场景,可考虑使用唯一索引

比如线上数据只需保留半年,然后历史数据存在归档库。此时,归档数据已是确保没有唯一键冲突。要提高归档效率,可考虑把表的唯一索引改为普通索引。

若某次写入使用了change buffer,之后主机异常重启,是否会丢失change buffer数据

不会!虽然是只更新内存,但在事务提交时,change buffer的操作也被记录到了redo log。所以崩溃恢复时,change buffer也能找回。


merge时是否会把数据直接写回磁盘

merge流程

从磁盘读入数据页到内存(老版本数据页)

从change buffer找出该数据页的change buffer 记录(可能多个),依次应用,得到新版数据页

写redo log

该redo log包含数据的变更和change buffer的变更

至此merge结束。

这时,数据页和内存中change buffer对应磁盘位置都尚未修改,是脏页,之后各自刷回自己物理数据,就是另外一过程。


在构造第一个例子的过程,通过session A的配合,让session B删除数据后又重新插入一遍数据,然后就发现explain结果中,rows字段从10001变成37000多。

而如果没有session A的配合,只是单独执行delete from t 、call idata()、explain这三句话,会看到rows字段其实还是10000左右。这是什么原因呢?


如果没有复现,检查


隔离级别是不是RR(Repeatable Read,可重复读)

创建的表t是不是InnoDB引擎

为什么经过这个操作序列,explain的结果就不对了?

delete 语句删掉了所有的数据,然后再通过call idata()插入了10万行数据,看上去是覆盖了原来10万行。

但session A开启了事务并没有提交,所以之前插入的10万行数据是不能删除的。这样,之前的数据每行数据都有两个版本,旧版本是delete之前数据,新版本是标记deleted的数据。

这样,索引a上的数据其实有两份。


不对啊,主键上的数据也不能删,那没有使用force index的语句,使用explain命令看到的扫描行数为什么还是100000左右?(潜台词,如果这个也翻倍,也许优化器还会认为选字段a作为索引更合适)

是的,不过这个是主键,主键是直接按照表的行数来估计的。而表的行数,优化器直接用的是show table status的值。

大家的机器如果IO能力比较差的话,做这个验证的时候,可以把innodb_flush_log_at_trx_commit 和 sync_binlog 都设成0。


参考


https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
354 9
|
4天前
|
缓存 算法 关系型数据库
MySQL底层概述—8.JOIN排序索引优化
本文主要介绍了MySQL中几种关键的优化技术和概念,包括Join算法原理、IN和EXISTS函数的使用场景、索引排序与额外排序(Using filesort)的区别及优化方法、以及单表和多表查询的索引优化策略。
MySQL底层概述—8.JOIN排序索引优化
|
8天前
|
SQL 存储 关系型数据库
MySQL原理简介—9.MySQL索引原理
本文详细介绍了MySQL索引的设计与使用原则,涵盖磁盘数据页的存储结构、页分裂机制、主键索引设计及查询过程、聚簇索引和二级索引的原理、B+树索引的维护、联合索引的使用规则、SQL排序和分组时如何利用索引、回表查询对性能的影响以及索引覆盖的概念。此外还讨论了索引设计的案例,包括如何处理where筛选和order by排序之间的冲突、低基数字段的处理方式、范围查询字段的位置安排,以及通过辅助索引来优化特定查询场景。总结了设计索引的原则,如尽量包含where、order by、group by中的字段,选择离散度高的字段作为索引,限制索引数量,并针对频繁查询的低基数字段进行特殊处理等。
MySQL原理简介—9.MySQL索引原理
|
6天前
|
存储 关系型数据库 MySQL
MySQL底层概述—6.索引原理
本文详细回顾了:索引原理、二叉查找树、平衡二叉树(AVL树)、红黑树、B-Tree、B+Tree、Hash索引、聚簇索引与非聚簇索引。
MySQL底层概述—6.索引原理
|
1月前
|
SQL 存储 关系型数据库
MySQL秘籍之索引与查询优化实战指南
最左前缀原则。不冗余原则。最大选择性原则。所谓前缀索引,说白了就是对文本的前几个字符建立索引(具体是几个字符在建立索引时去指定),比如以产品名称的前 10 位来建索引,这样建立起来的索引更小,查询效率更快!
115 22
 MySQL秘籍之索引与查询优化实战指南
|
25天前
|
存储 关系型数据库 MySQL
MySQL索引学习笔记
本文深入探讨了MySQL数据库中慢查询分析的关键概念和技术手段。
100 13
|
28天前
|
存储 关系型数据库 MySQL
浅入浅出——MySQL索引
本文介绍了数据库索引的概念和各种索引结构,如哈希表、B+树、InnoDB引擎的索引运作原理等。还分享了覆盖索引、联合索引、最左前缀原则等优化技巧,以及如何避免索引误用,提高数据库性能。
|
2月前
|
存储 缓存 关系型数据库
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
MySQL的存储引擎是其核心组件之一,负责数据的存储、索引和检索。不同的存储引擎具有不同的功能和特性,可以根据业务需求 选择合适的引擎。本文详细介绍了MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案。
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
|
1月前
|
存储 关系型数据库 MySQL
MySQL中为什么要使用索引合并(Index Merge)?
通过这些内容的详细介绍和实际案例分析,希望能帮助您深入理解索引合并及其在MySQL中的
132 10
|
2月前
|
存储 Oracle 关系型数据库
索引在手,查询无忧:MySQL索引简介
MySQL 是一款广泛使用的关系型数据库管理系统,在2024年5月的DB-Engines排名中得分1084,仅次于Oracle。本文介绍MySQL索引的工作原理和类型,包括B+Tree、Hash、Full-text索引,以及主键、唯一、普通索引等,帮助开发者优化查询性能。索引类似于图书馆的分类系统,能快速定位数据行,极大提高检索效率。
78 8

推荐镜像

更多