[MySQL] 5.索引(三)——聚簇索引
聚簇索引
聚簇索引是一种数据存储方式,InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。当表有聚簇索引时,它的数据行实际上存放在索引的叶子页中(叶子页包含了行的全部数据,节点页只包含了索引列)。“聚簇”表示数据行和相邻的键值紧凑的存储在一起。一个表只能有一个聚簇索引。
InnoDB会选择主键列进行聚簇索引,如果没有定义主键,InnoDB会选择唯一的非空索引代替。如果还是没有,InnoDB会隐式定义一个主键来作为聚簇索引。InnoDB只聚集在同一个页面中的记录。
InnoDb的普通索引(二级索引)的叶子结点中存放的是主键的值,所以需要先查询普通索引(二级索引)的叶子节点找到对应的主键值,然后再根据主键值去聚集索引中查询到对应的数据。
非聚集索引的索引与数据是存在不同文件的。
聚簇索引优点:
- 可以把相关的数据保存在一起,例如实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘读取少数数据页就能获取某个用户的全部邮件,减少磁盘I/O次数。
- 数据访问更快:聚簇索引同时将索引和数据保存在同一个B-Tree中,因此从聚簇索引中获取数据要比非聚簇索引更快。
- 使用覆盖索引扫描的查询可以直接使用节点中的主键值。
聚簇索引缺点:
- 聚簇索引最大限度地提高了I/O密集型应用的性能。如果数据全放放在内存中,那么聚簇索引就没了优势。
- 插入速度严重依赖于插入顺序。按照主键顺序插入最快。
- 更新聚簇索引列的代价很高,因为会强制InnoDB将每个被更新的行移动到新的位置。
- 基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临“页分裂”问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳改行,这就是一次页分裂操作。页分裂会导致表占用更多磁盘空间
- 当行比较稀疏,或者有由于页分裂导致数据存储不连续的时候,可能会导致全表扫描变慢。
- 二级索引(费聚簇索引)可能比想象的要更大,因为在二级索引的叶子节点包含了引用行的主键列。
- 二级索引访问需要两次索引查找,而不是一次(因为二级索引叶子节点保存的不是指向行的物理位置的指针,而是行的主键值)。
InnoDB和MyISAM数据分布对比
对于以下这个表,InnoDB和MyISAM存储方式不同:
1 | CREATE TABLE layout_test ( |
MyISAM的数据分布
MyISAM按照数据插入的顺序存储在磁盘上,如图所示。因为这里行是定长的,所以MyISAM可以从表的开头跳过所需字节直接找到需要的行。可以看出MyISAM的主键索引和其他索引在结构上没有不同。
InnoDB的数据分布
如图所示,可以发现图中显示了整个表,而不仅仅是索引。因为在InnoDB中,聚簇索引就是表,不需要像MyISAM那样需要独立的行存储。
聚簇索引的每一个叶子结点都包含了主键值、事务ID、用于事务和MVCC的回滚指针以及所有的剩余列(在这里是col2)。如果主键是一个列前缀索引,InnoDB也会包含完整的主键列和剩下的其他列。
另一点和MyISAM不同的是,InnoDB的二级索引和聚簇索引并不相同。InnoDB二级索引的叶子节点中存储的不是“行指针”,而是主键值,并以此作为指向行的“指针”。这样会减少当出现行移动或者数据页分裂
(上文有提到)时二级索引的维护工作。使用主键值当作指针会让二级索引占用更多的空间,换来的好处是,InnoDB在移动时无需更新二级索引中的这个“指针”。
在InnoDB表中按主键顺序插入行
尽量按主键顺序插入行,最简单的方法使用自增列(AUTO_INCREMENT)。因为这样填充时,当页满时,下一条记录就可以写在新页中。而如果无序插入,那么每次都要为行找到合适的位置,会增加许多额外工作。有以下一些缺点
- 写入的目标页可能已经刷新到磁盘上,并从缓存中移除,InnoDB在插入前必须先找到并从磁盘读取目标页到内存中,导致大量随机I/O。
- 因为写入是乱序的,InnoDB不得不频繁地做页分裂操作,以便为新的行分配空间。页分裂会导致移动大量数据,一次插入最少需要修改三个页,而不是一个页。
- 频繁的页分裂,页会变得稀疏并被不规则地填充,最终会有碎片。
对于高并发工作负载,按主键顺序可能导致性能下降。
1 | 参考内容 >> 高性能MySQL第三版 |
原文地址:https://www.cnblogs.com/wangziqiang123/p/11642325.html
- React Native如何消除启动时白屏
- Observer观察者设计模式
- 创建 GitHub 仓库的步骤及方法
- React Native和原生app通信机制详解
- Python高效编程技巧
- 模块和处理程序之通过HttpModule和HttpHandler拦截入站HTTP请求执行指定托管代码模块
- iOS如何实现多个环境一次打包
- iOS 轻量级存储
- 深入理解React Native页面构建渲染原理
- React native城市列表组件
- iframe 解析
- React Native之StyleSheet样式表
- jQuery对象扩展方法(Extend)深度解析
- 线程同步:System.Core中新的读写锁
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- Python 经典面试题 一
- Python 经典面试题 二
- Linux磁盘管理之LVM快速入门配置
- 你熟悉Python的代码规范吗?如何一键实现代码排版
- Deepin安装与基础使用
- Golang 单元测试详尽指引
- Pigeon- Flutter多端接口一致性以及规范化管理实践
- Linux之PAM系统模块详解说明
- 快速上手联邦学习——腾讯自研联邦学习平台PowerFL实战
- Linux发行版的镜像网站及开源软件收集
- 自建图床应用,我只推荐 Serverless
- 手把手教你使用 Nginx Ingress 实现金丝雀发布
- Kettle构建Hadoop ETL实践(六):数据转换与装载
- 从面试角度一文学完 Kafka
- Kettle构建Hadoop ETL实践(七):定期自动执行ETL作业