Mysql 索引以及锁初探
前段时间在面试字节的时候,面试官拿着 Sql 问我这个锁和索引之间的联系是什么的时候?人懵住了,这玩意还有联系?之后意识到是自己学习东西太过于肤浅,没有建立体系,导致这么简单的问题没答出来,悔不当初 QAQ。 Mysql 存储数据的方式 Mysql 底层存储数据是通过一个一个数据页所组成的,一页中存储的数据属于同一张表,但同一张表的数据不一定分布在同一页,从粗到细粒度大致是: 表空间 (Tablespace) └── 段 (Segment) └── 区 (Extent) └── 页 (Page, 16KB) └── 行记录 (Row) 我们这里主要分析页和行的结构 数据页(Data Page) 上图展示的就是一页的数据内容,Mysql 的行数据被单向链表的形式组织起来,为了提高页内检索效率,所以有了槽和分组的概念。他们的对应关系是,一个数据页中有多个槽,每个槽中有一个或多个数据行,数据行以单向链表的形式组织起来,然后链表头部和尾部分别是记录中的最小记录和最大记录标识。通过这样的形式组织起来的数据有什么好处呢?答曰提高检索效率。如果只单纯使用单向链表,那就意味着每次查找只能单向搜索。而使用槽,槽指针指向了一个分组内的最大记录,那就意味着在一页内可以用二分查找来提高检索的时间复杂度。 行记录(Data Row) 上面讲述了一页是如何组织的,下面来看看一行是如何组织的。 这就是一行数据,从左到右分别记载了: 变长字段列表:记录行数据中变长字段占用的字节数 Null 值列表:记录行数据中,可以为 Null 的列是否为 Null,也就是说一列对应一个 Bit 位,如果为 Null,在对应的 bit 位为 0 记录头:这里主要包含了一些辅助信息,比如记录的类型(是否是 B+树的叶子)、下一条记录的位置以及是否被删除标记。 RowId:当表设置了主键或者唯一性约束,则这一列隐藏,如果没设置,则 Mysql 会默认生成 RowId 字段。 TrxId:这条记录是由哪行所生成的。 Rollptr:UndoLog 的列表挂载,用于回滚和 MVCC。 上面就是一行记录,也就是说,当你创建一个表,Mysql 底层就是这么组织的。 索引 Mysql 索引分类 按照数据结构:Hash 索引、FullText 索引、B+ 树索引 按照底层存储:聚簇索引、非聚簇索引 按照建立索引的字段类型:主键索引、唯一索引、前缀索引、普通索引 按照字段数量:单列索引、组合索引 我们主要来关注聚簇索引和非聚簇索引,我们知道聚簇索引就是主键索引,其存储了主键和整行的数据,而非聚簇索引的一行第一列是索引值,第二行是主键值。为什么?Why?为什么第存全量数据?这是因为 Mysql 的底层数据组织结构造成的,B+ 树存储索引是要存储全量的数据页的,如果说非聚簇索引也存储全量数据,就等于在建立索引的时候,需要将所有数据页进行一次全量拷贝 + 组织,就意味着底层会出现大量重复的数据,所以 InnoDB 选择第二列存储主键值,这样当检索到索引后,只需要再去聚簇索引中,找到对应的数据即可。 Mysql 索引的数据组织结构 我们知道 InnoDB 的索引底层是基于 B+ 树的,而索引和页的关系是什么呢?答案是索引由数据页组成。 Mysql 的数据是这样存的,当你创建一个表并存入数据时,Mysql 会默认生成一个基于主键的聚簇索引,就是无论你主动创建索引与否,Mysql 都会生成聚簇索引,这不只是为了提高检索速度,而是 InnoDB 的行锁机制是基于索引的。所以这里也就 CallBack 到了开头的,索引和锁的关系。 ...