每张表都一定存在主键吗?

关于这个问题,各位小伙伴们不妨先自己想一想,再往下寻找答案。

首先公布结论:对于 InnoDB 存储引擎来说,每张表都一定有个主键(Primary Key)!

让人非常遗憾的是,网络上至今仍然有非常多的文章是这样的描述:“一张表中必须有聚集索引,但不一定需要主键”。前半句是正确的,后半句是大错特错!

对于 InnoDB 存储引擎来说,表采用的存储方式称为索引组织表(index organizedtable),也即表都是根据主键的顺序来进行组织存放的。如果主键都没有,表怎么存?

那下面这段没定义主键的建表语句是正确的吗?

CREATE TABLE test(
a INT NOT NULL,
b INT NULL,
c INT NOT NULL,
d INT NOT NULL,
UNIQUE KEY(b),
UNIQUE KEY(d),
UNIQUE KEY(c)
);

当然是没有任何问题的。

因为 不显示定义主键 != 没有主键

如果在创建表时没有显式地定义主键,InnoDB 存储引擎会按如下方式选择或创建主键:

  • 首先判断表中是否有非空的唯一索引(Unique NOT NULL),如果有,则该列即为主键
  • 如果不符合上述条件,InnoDB 存储引擎自动创建一个 6 字节大小的指针 _rowid 作为主键

如果表中有多个非空唯一索引时怎么办呢? InnoDB 存储引擎将选择建表时第一个定义的非空唯一索引为主键。需要注意的是!主键的选择根据的是非空唯一索引定义的顺序,而不是建表时列的顺序。

比如上面那段代码,有 a、b、c、d 四个列,b、c、d 三列上都有唯一索引。不过 b 列不是非空的,所以不可能成为主键了。而 d 列首先被定义为非空的唯一索引,所以 InnoDB 存储引擎将其视为主键。

B+ 树索引总览

InnoDB 存储引擎支持以下几种常见的索引:

  • B+ 树索引
  • 全文索引
  • 哈希索引

所谓哈希索引也就是得益于哈希算法的快速查找特性,不过哈希索引的致命缺点就是无法范围查询。并且 InnoDB 中哈希索引是自适应的,也就是说 InnoDB 存储引擎会根据表的使用情况自动为表生成哈希索引,不能人为干预是否在一张表中生成哈希索引

全文索引本文先暂且不做赘述。

再来看 B+ 树索引,B+ 树索引的本质就是 B+ 树在数据库中的实现,它是目前关系型数据库系统中查找最为常用的索引。

关于 B+ 树的数据结构我就不详细说了,B 代表平衡(Balance),而不是二叉(Binary),B+ 树是从最早的平衡二叉树演化而来的,但是 B+ 树不是一个二叉树。

简单介绍下:B+ 树是为磁盘或其他直接存取辅助设备设计的一种平衡查找树。在 B+ 树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上,各叶子节点之间通过双向链表进行连接。

也就是说,B+ 树的叶子节点存储真正的记录,而非叶子节点的存在是为了更快速的找到对应记录所在的叶子节点。如下图是一个高度为 2 的 B+ 树:

另外,需要注意的是,B+ 树索引并不能找到一个给定键值的具体“行”!B+ 树索引能找到的只是被查找数据行所在的“页”。然后数据库通过把页读入到内存,再在内存中进行查找,最后得到要查找的数据

肯定有些小伙伴会懵逼了,“页” 又是什么东西?

这就得说到 InnoDB 存储引擎的逻辑存储结构。

InnoDB 存储引擎中,所有数据都被逻辑地存放在一个空间中,称之为 表空间(tablespace),也就是说我们常说的表,可以看作是 InnoDB 存储引擎逻辑结构的最高层。表空间又由 段(segment)区(extent)页(page) 组成(页有时也称为块 block)。如下图:

页是 InnoDB 磁盘管理的最小单位,在 InnoDB 存储引擎中,默认每个页的大小为 16KB。而页里面存放的东西就是一行一行的记录。

我们接下来要说的 聚集索引(clustered inex)和辅助索引(secondary index)其实都是一种 B+ 树索引。也就是说不管是聚集索引还是辅助索引,其内部都是 B+树,即高度平衡的,叶子节点存放着所有的数据。(需要注意的是,索引是存储引擎负责实现的,因此不是所有的存储引擎都支持聚簇索引)

聚集索引与辅助索引不同之处就是,叶子节点存放的是否是一整行的信息。下文我们会详细解释。

主键和聚集索引的关系

先来看聚集索引,上面我们说过,InnoDB 存储引擎表是索引组织表结构,即表中数据都是按照主键顺序进行存放的。而聚集索引就是按照每张表的主键构造一棵 B+ 树,同时叶子节点中存放的即为表中一行一行的数据,所以聚集索引的叶子节点也被称为数据节点。

也就是说,聚集索引能够在 B+ 树索引的叶子节点上直接找到数据。并且由于定义了数据的逻辑顺序,查询优化器能够快速发现到底是哪一段范围的数据页需要扫描。比如用户需要查询一张用户表,查询最后注册的 10 位用户,由于 B+ 树索引的叶子节点是基于双向链表的,所以用户可以快速找到最后一个数据页,并取出 10 条记录。这也就是为什么大部分情况下查询优化器倾向于采用聚集索引了。

可以这么说:在聚集索引中,索引即数据,数据即索引

另外,由于数据页只能按照一棵 B+ 树进行查找排序,或者说无法同时把数据行存放在两个不同的地方,所以每张表只能拥有一个聚集索引

讲了这么多,好像还没讲到主键和聚集索引有啥区别。一张表只能有一个主键,并且也只能有一个聚集索引,聚集索引还是按照主键来构建的,那这种种迹象不都表明主键就是聚集索引?

事实上,主键和索引就不是一个层次的东西!

主键是一种约束,这个约束用来强制表的实体完整性,一个表中只能有一个主键约束,并且主键约束中的列值必须是非空且唯一的。

而聚集索引它作为一种索引,其目的不是为了约束啥,而是为了对数据行进行排序以提高查询的效率,换句话说它决定的是数据库的物理存储结构。

形象点说,一个没加聚集索引的表,它的数据是一行一行 无序 地存放在磁盘存储器上的。而如果给表添加了聚集索引,那么表在磁盘上的存储结构就由一行一行排列的结构转变成了 树状结构,也就是 B+ 树结构,换句话说,就是整个表就变成了一个索引,也就是上面提到的 “索引即数据,数据即索引”。

而至于 “主键就是索引” 这种观点的由来,是因为:InnoDB 存储引擎中,每张表都一定存在主键(显示或隐式),而聚集索引依赖于主键的建立,所以如果没有强制指定使用非聚集索引,InnoDB 在创建主键的同时会建立一个唯一的聚集索引(也有些文章称之为 主键索引)。

所以,不要说 “主键就是聚集索引”,应该这样说:“聚集索引一般都是加在主键上的”。

聚集索引和辅助索引的关系

辅助索引(Secondary Index)也称为 非聚集索引、二级索引。其和聚集索引的最大区别就在于,辅助索引的叶子节点并不包含行记录的全部数据。

简单来说,一行记录我们可以用 “主键 + 其他数据” 这样的组合来标识,聚集索引中的叶子节点存储的就是这一整个组合,而非聚集索引中的叶子节点只存储了这个组合中的主键,那其他数据我怎么获得呢?

非聚集索引的叶子节点说还包含了一个 书签(bookmark),该书签用来告诉 InnoDB 存储引擎哪里可以找到与索引相对应的行数据。

那各位不妨想一想,行数据存储在哪里呢?

没错,上文说过,聚集索引中的叶子节点中存放的就是表中一行一行的数据,所以 InnoDB 存储引擎的辅助索引中的书签其实就是相应行数据的聚集索引键

也就是说,辅助索引的叶子节点包含的是:每行数据的主键 + 该行数据对应的聚集索引键

当通过辅助索引来寻找数据时,InnoDB 存储引擎会先遍历辅助索引并通过叶子节点获得某个主键对应的聚集索引键,然后再通过聚集索引来找到一个完整的行记录。

举个例子,如果在一棵高度为 3 的辅助索引树中查找数据,那需要对这棵辅助索引树遍历 3 次找到指定聚集索引键,如果聚集索引树的高度同样为 3,那么还需要对聚集索引树进行 3 次查找,最终找到一个完整的行数据所在的页,因此一共需要 6 次逻辑 IO 访问以得到最终的一个数据页。

另外,很显然的是,辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引

关注公众号 | 飞天小牛肉,即时获取更新

  • 博主东南大学硕士在读,携程 Java 后台开发暑期实习生,利用课余时间运营一个公众号『 飞天小牛肉 』,2020/12/29 日开通,专注分享计算机基础(数据结构 + 算法 + 计算机网络 + 数据库 + 操作系统 + Linux)、Java 技术栈等相关原创技术好文。本公众号的目的就是让大家可以快速掌握重点知识,有的放矢。关注公众号第一时间获取文章更新,成长的路上我们一起进步

  • 并推荐个人维护的开源教程类项目: CS-Wiki(Gitee 推荐项目,现已累计 1.8k+ star), 致力打造完善的后端知识体系,在技术的路上少走弯路,欢迎各位小伙伴前来交流学习 ~

  • 如果各位小伙伴春招秋招没有拿得出手的项目的话,可以参考我写的一个项目「开源社区系统 Echo」Gitee 官方推荐项目,目前已累计 900+ star,基于 SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + ... 并提供详细的开发文档和配套教程。公众号后台回复 Echo 可以获取配套教程,目前尚在更新中。

Are You OK?主键、聚集索引、辅助索引的更多相关文章

  1. Oracle基础维护02-表、主键、索引、表结构维护手册

    目录 一.项目新建表.主键.索引注意事项 二.举例说明建表.主建.索引的操作方法 2.1 设定需求如下 2.1.1 查询数据库有哪些表空间 2.1.2 本文档假设数据库有这两个业务用户的表空间 2.2 ...

  2. 删除表空间的时候遇到的问题:ORA-02429: 无法删除用于强制唯一/主键的索引

    今天打算删除orcale数据库中无用的表空间,发现报错,查资料删除,写个过程留着备用.1.drop tablespace dldata INCLUDING CONTENTS CASCADE CONST ...

  3. ora-02429:无法删除用于强制唯一/主键的索引

    今天打算删除orcale数据库中无用的表空间,发现报错,查资料删除,写个过程留着备用. 1.drop tablespace dldata INCLUDING CONTENTS CASCADE CONS ...

  4. SQL语句新建表,同时添加主键、索引、约束

    SQL语句新建数据表   主键,索引,约束 CREATE TABLE [dbo].[T_SendInsideMessageRec]( [SendInsideMID] [uniqueidentifier ...

  5. Oracle 添加主键和索引

    数据的主键和索引一般情况下都是必须的,特别是表有大量数据的时候,索引和主键更是必不可少,这样可以提供数据的查询效率: 一.创建表的同时创建主键约束 (1)无命名 create table studen ...

  6. MySQL主键和索引的联系及区别

    转载自:http://www.nowamagic.net/librarys/veda/detail/1954 关系数据库依赖于主键,它是数据库物理模式的基石.主键在物理层面上只有两个用途: 惟一地标识 ...

  7. MySQL主键与索引的区别和联系

    MySQL主键与索引的区别和联系   关系数据库依赖于主键,它是数据库物理模式的基石.主键在物理层面上只有两个用途: 惟一地标识一行. 作为一个可以被外键有效引用的对象. 索引是一种特殊的文件(Inn ...

  8. mysql,主键与索引的区别和联系

    关系数据库依赖于主键,它是数据库物理模式的基石.主键在物理层面上只有两个用途: 惟一地标识一行. 作为一个可以被外键有效引用的对象. 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成 ...

  9. 执行sql失败之后,再次执行的时候提示:ora-02429:无法删除用于强制唯一/主键的索引

    SQL 1:select segment_name,partition_name,tablespace_name from --显示出表的主键的表空间是:SYSTEM --以下是生成删除主键约束的脚本 ...

  10. Mysql-学习笔记(==》增删主键建立索引 七)

    SHOW FULL COLUMNS FROM teacher; -- 增加删除主键 alterCREATE TABLE tt(t INT);ALTER TABLE tt ADD CONSTRAINT ...

随机推荐

  1. mybatis常用标签(转)

    1. 定义sql语句 select 标签 属性介绍: id :唯一的标识符. parameterType:传给此语句的参数的全路径名或别名 例:com.test.poso.User或user resu ...

  2. 00JAVA语法基础 原码、反码、补码

    记得之前学C语言的时候老师课上讲过一些,不过当时觉得考试不考,也就上课听了下,下课也没怎么多做了解.这次,Java课上再次提出来了,自己也超越了些资料,对这三种概念算是有所初步了解. 1.原码 数据储 ...

  3. Java基础00-多线程28

    1. 实现多线程 1.1 进程 1.2 线程 1.3 多线程的实现方式(方式一:继承Thread类) 代码示例:定义类MyThread: //1:定义一个类MyThread继承Thread类 publ ...

  4. IO编程之NIO

    从jdk1.4开始,java提供了一系列改进的输入/输出处理的新功能,这些功能被统称为新IO(New IO,简称NIO),这些类都被放在java.nio包以及子包中,并且对原java.io包中的很多类 ...

  5. Java-数组有关

    1.复制数组 复制数组主要有三类方法: 1.使用循环语句逐个赋值数组元素 2.使用System类中的静态方法arraycopy 3.使用clone方法复制数组 对于2,详述如下: arraycopy( ...

  6. PAT乙级:1066 图像过滤 (15分)

    PAT乙级:1066 图像过滤 (15分) 题干 图像过滤是把图像中不重要的像素都染成背景色,使得重要部分被凸显出来.现给定一幅黑白图像,要求你将灰度值位于某指定区间内的所有像素颜色都用一种指定的颜色 ...

  7. 搭建NFS服务

    说明:这里是Linux服务综合搭建文章的一部分,本文可以作为单独搭建yum仓库的参考. 注意:这里所有的标题都是根据主要的文章(Linux基础服务搭建综合)的顺序来做的. 如果需要查看相关软件版本和主 ...

  8. 解决pip下载速度慢的问题

    解决链接:https://blog.csdn.net/u013901768/article/details/82343512 感谢这位博主的分享. 博客内容如下: 1. 安装时强制使用国内源进行安装, ...

  9. 第十三篇 -- 关于C++不支持int

    如果是exe的程序的话,DWORD是非法字符,所以需要添加头文件"windows.h"

  10. Vue 2升级 Vue 3初探小细节

    前言 嗯,偶尔看看学习Vue 3技能啦,此前用过Vue 2做过一点东西,Vue 3已面世一段时间,于是乎,我来看看所遇到的问题是否在Vue 3中得到解决,首先,我们来讲讲一个例子在Vue 2中的实现, ...