InnoDB Tidbit:The doublewrite buffer wastes 32 pages (512 KiB) (12.双写缓冲区会导致512KB的浪费)
在我不断探索完全理解InnoDB数据存储的过程中,我遇到了一个非常小而无关紧要的问题。这个问题还是比较有趣的。我注意到下面的页面的块,他们很早就在ibdata1系统标空间中分配,但是显然没用使用。(不必要的行从输出的过程中删除):
$ innodb_space -f ibdata1 space-page-type-regions
start end count type
13 44 32 ALLOCATED
双写缓冲区的背景
大多数使用InnoDB的人都听说过“双写缓冲区”——InnoDB页面刷新策略的一部分。双写缓冲区用作一个“暂存区”,在将128页刷新到最终目的地(可能多达128个不同的写操作)之前,连续地写入(默认情况下)128页。MySQL手册上说,在“InnoDB磁盘I/O”中: InnoDB使用了一种新的文件刷新技术,涉及到一种叫做双写缓冲区的结构。它增加了在操作系统崩溃或停电后恢复的安全性,并通过减少对fsync()操作的需求提高了大多数Unix上的性能。 在将页面写入数据文件之前,InnoDB首先将它们写入一个连续的表空间区域,称为双写缓冲区。只有在对双写缓冲区的写入和刷新完成后,InnoDB才会将页面写入到数据文件中的正确位置。如果操作系统在写页面的过程中崩溃了,InnoDB可以在恢复过程中从双写缓冲区中找到一个好的页面副本。
双写缓冲区需要被考虑
通常,双写缓冲区由两个区段组成,每个区段是64个连续的页(1 MiB),总共有128个页(2 MiB)。但是,InnoDB不能盲目地借用这两个区段;它必须在空间文件中申明它们。为此,它创建一个文件段(又名Fseg)并使用一个Inode指向它。在InnoDB空间文件的页面管理中,我描述了文件段是如何包含的:
- 最多32个单独分配的“片段”页面的数组
- “完整”区段列表(无页面空闲)
- “未满”区段列表(部分分配)
- “空闲”区列表(没有分配页)
导致分配完整的区段
在分配完整的区段之前,分配给一个文件段总是会填满片段数组。奇怪的是,双写缓冲区在这种情况下并不特殊。分配它的代码在trx/trx0sys.c中第335行使用了以下循环:
for (i = 0; i < 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE
+ FSP_EXTENT_SIZE / 2; i++) {
不幸的是,这段代码没有任何注释,但总共分配了160页:
FSP_EXTENT_SIZE / 2 → 64 / 2 → 32 pages
2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE → 2 * 64 → 128 pages
最初分配的32个页面纯粹是为了填充片段数组,从而迫使随后的fseg_alloc_free_page调用开始为剩下的128个页面分配完整的区段(这是双写缓冲区实际需要的)。然后,该代码检查分配了哪些区段,并将这些区段的初始页号添加到TRX_SYS报头中,作为双写缓冲区分配。在一个典型的系统中,InnoDB会分配以下页面:
- 片段页13-44——永久未使用的片段页,但保留分配给双写缓冲区的文件段。
- 范围从第64页开始,到第127页结束,实际上是双写缓冲区的第1块。
- 范围从第128页开始,到第191页结束,实际上是双写缓冲区的第2块。
使用innodb_ruby转储文件段(通过inode)
我最近在innodb_ruby的innodb_space程序中添加了一个新的空格-inode -detail和空格-inode -summary模式,它可以方便地精确地显示分配给给定文件段的页面和区段(为了清晰而剪裁,为了换行而重新格式化;通常打印在一行上):
$ innodb_space -f ibdata1 space-inodes-detail
INODE fseg_id=15, pages=160,
frag=32 pages (13, 14, ..., 43, 44),
full=2 extents (64-127, 128-191),
not_full=0 extents () (0/0 pages used),
free=0 extents ()
在这里,您可以清楚地看到文件段的“full”列表中的两个完整区段,以及32个片段页。
总结
有几种方法可以避免这种情况,比如在分配两个区段之后释放各个页面,或者添加特殊的“无片段”分配方法。然而,正如我在开始时所说的,这是非常无关紧要的,因为每次安装总共只有512 KiB。考虑到这些细节的行为,代码绝对可以使用重写来保持清晰。它还可以使用现有的定义,如fseg_frag_arr_n_slot或FSEG_FRAG_LIMIT,而不是重复基本无法解释的计算FSP_EXTENT_SIZE / 2。此外,重写它以使用一个更有意义的循环结构将是有益的;它没有理由在同一个for循环中分配所有三组页面(特别是在没有注释的情况下)。
- 如何在不影响asp.net默认安全性的前提下使用ckeditor/fckeditor?
- Linux下防御DDOS攻击的操作梳理
- Android新手之旅(8) ListView的使用
- 更换Ubuntu源为国内源的操作记录
- Android新手之旅(8) ListView的使用
- CKEditor/CKFinder升级心得
- Docker容器学习梳理-Dockerfile构建镜像
- 再谈web开中几种经典的大文件上传组件
- Nginx负载均衡中后端节点服务器健康检查的操作梳理
- Linux系统下CPU使用(load average)梳理
- 基于组件的.NET技术(5)
- Silverlight与WPF中BeginInvoke的差异
- Linux下部署SSH登录时的二次身份验证环境记录(利用Google Authenticator)
- Linux下DNS简单部署(主从域名服务器)
- 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 数组属性和方法
- K8S 生态周报| KIND v0.9 发布带来众多更新
- oracle 数据回滚,恢复误删的数据,闪回表功能的使用
- C语言 | 关于结构体内存对齐,看这篇就够了
- Python 图像处理篇-利用opencv库展示本地图片实例演示
- Python 图像处理篇-利用opencv库和numpy库读取包含中文路径下的本地图片实例演示
- 从头创建您自己的vue.js——第2部分(虚拟DOM基础)
- Manage Jenkins报错:"依赖错误: 部分插件由于缺少依赖无法加载...",解决办法
- 从头创建您自己的vuei .js——第3部分(构建VDOM)
- adb 模拟上下左右滑动,示例演示
- python 技术篇-pythoncom.PumpMessag()关闭、杀死它的进程,pythoncom.PumpMessag()运行卡住解决办法
- PyQt5 技术篇-QWidget、QDialog程序窗口关闭closeEvent()触发事件方法重写
- 恕我直言你可能真的不会java第6篇:Stream性能差?不要人云亦云
- python-技术篇-打印详细报错日志,获取报错信息位置行数
- React从入门到放弃,一个关于网页速度的故事
- python 技术篇-日志定期清理设置,自动清理上个月的日志实例演示