How does InnoDB behave without a Primary Key(11.InnoDB在没用主键情况下的行为)
今天下午,我和Arjen Lentz讨论了InnoDB在没有声明主键的情况下的行为,这个话题很有趣,也没有足够的文档证明,所以有必要写一个简短的帖子。
InnoDB聚集索引的背景
在InnoDB索引页的物理结构中,我描述了“在InnoDB中的任何内容都是索引”。这意味着InnoDB必须为每个表都有一个“聚簇索引”,这通常是主键。手册上说在聚集和二级索引: 如果表没有主键或合适的唯一索引,InnoDB内部会在一个包含行ID值的合成列上生成一个隐藏的聚集索引。这些行是按照InnoDB给表中的行分配的ID排序的。行ID是一个6字节的字段,在插入新行时单调地增加。因此,按行ID排序的行在物理上是按插入顺序排列的。
我之前假设这意味着将使用一个不可见的列和用于实现auto_increment的相同序列生成代码(它本身存在一些可伸缩性问题)。然而,实际上它们是完全不同的实现。
实现隐式的 Row IDs
这实际上是这样实现的,如手册所说,如果一个表声明没有主键和非空的唯一键,InnoDB会自动添加一个6字节(48位)的整数列ROW_ID到表中,并基于该列集群数据。该列不能被任何查询访问,也不能在内部用于任何事情,比如基于行的复制。 手册没有提到的是,所有使用这样的ROW_ID列的表共享相同的全局序列计数器(手册上说“单调递增”,但没有澄清),这是数据字典的一部分。所有行ID的最大使用值(从技术上说,是下一个要使用的ID)存储在第7页(类型SYS)的系统表空间(例如ibdata1)中,在数据字典头(字段DICT_HDR_ROW_ID)中。 这个全局序列计数器由dict_sys->互斥锁保护,甚至用于递增(与使用原子递增相反)。实现在include/dict0boot中。ic(删除多空行):
38 UNIV_INLINE
39 row_id_t
40 dict_sys_get_new_row_id(void)
41 /*=========================*/
42 {
43 row_id_t id;
44
45 mutex_enter(&(dict_sys->mutex));
47 id = dict_sys->row_id;
49 if (0 == (id % DICT_HDR_ROW_ID_WRITE_MARGIN)) {
51 dict_hdr_flush_row_id();
52 }
54 dict_sys->row_id++;
56 mutex_exit(&(dict_sys->mutex));
57
58 return(id);
59 }
您可能还注意到,这段代码缺乏任何保护措施,以防止分配给行id的48位溢出。这是不必要的冗余代码,但即使是连续每秒100万次插入(这可能有点乐观;),也需要大约9年的时间来耗尽ID空间。我想应该是这样吧。
确保生成不冲突的id
计数器每生成第256个ID(上面定义的DICT_HDR_ROW_ID_WRITE_MARGIN)就刷新到磁盘,方法是修改SYS数据字典页面中的值,该值被记录到事务日志中。在启动时,InnoDB会将存储在磁盘上的DICT_HDR_ROW_ID增加至少256,最多511。这确保了生成的任何id都小于新的起始值,因此不会有任何冲突。
性能和竞争的影响
鉴于InnoDB中的其他代码受到了dict_sys->互斥锁的保护,我认为可以公平地说,任何具有隐式集群键(ROW_ID)的表都可能在删除(不相关的)表的操作中都会遇到插入卡顿。在具有隐式键的多个表中并行插入可能会受到性能限制,因为它将在共享互斥锁和共享计数器变量的缓存争用上序列化。此外,每生成第256个值,就会导致对SYS页面修改进行一次日志写入(和刷新),而不管事务是否已经提交(或将要提交)。
- ASM 翻译系列第十三弹:ASM 高级知识 - Forcing the issue
- ASM 翻译系列第十四弹:ASM Internal Rebalancing act
- DIY网站统计:WordPress排除管理员评论及精准友链数的方法
- Linux运维工程师:30道面试题整理
- ASM 翻译系列第十五弹:ASM Internal ASM File Directory
- ASM 翻译系列第十六弹:ASM Internal ASM Active Change Directory
- ASM 翻译系列第十七弹:ASM Internal ASM Disk Directory
- Windows 7下获取System权限
- ASM 翻译系列第十八弹:ASM Internal ASM file number 5
- 菜单式Shell运维脚本调试小记
- 优化Postgres-x2 GTM
- 启用某些Linux发行版的root帐号
- Linux中的完美截图工具:Deepin-ScreenShot
- ASM 翻译系列第二十弹:ASM Internal ASM file number 7
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 容器服务 TKE 上服务暴露的几种方式
- 【论文分享】ACL 2020 信息抽取任务中的新动向
- 工匠人iOS 代码规范
- 写一个通用的幂等组件,我觉得很有必要
- PC性能监测工具,您不可或缺的好帮手~~
- 特征工程之处理时间序列数据
- Matplotlib中的“plt”和“ax”到底是什么?
- 使用深度学习模型创作动漫故事,比较LSTM和GPT2的文本生成方法
- 聊聊BitCaskLock
- LightGBM的参数详解以及如何调优
- 聊聊BitCaskKeyDir
- 使用2D卷积技术进行时间序列预测
- 在PyTorch中使用DistributedDataParallel进行多GPU分布式模型训练
- 同城双活与异地多活架构分析
- leetcode多线程之按序打印