blob及行外数据
本文中我们假设innodb_page_size为16k,记录格式为compact.
1 大字段
大字段的类型可以参看这里,
Data Type | Storage Required |
---|---|
TINYBLOB, TINYTEXT | L + 1 bytes, where L < 28 |
BLOB, TEXT | L + 2 bytes, where L < 216 |
MEDIUMBLOB, MEDIUMTEXT | L + 3 bytes, where L < 224 |
LONGBLOB, LONGTEXT | L + 4 bytes, where L < 232 |
这里除了TINYBLOB, TINYTEXT,字段长度都可能超过16k.这时一个page肯定是存储不下这个字段,于是就需要行外存储了。
2 行外存储
2.1 记录格式
行外记录存储方式与记录格式有关,记录格式可以参考这里
对于compact格式:768字节前缀数据和20字节行外指针。
对于dynamic格式:20字节行外指针,没有前缀数据。
20个字节的行外信息包括
表空间id(4个字节)
页号pageno(4个字节)
行外首页页偏移(4个字节)
剩余8字节存行外数据长度,其中高两位分表存行外数据拥有标记和继承表标记。下一节介绍。
2.2 行外页存储(off-page)
innodb的文件是通过表空间(space),段(segment),簇(extent),页(page)的方式进行管理的。表空间文件按page_size划分为页。每64个页组成一个簇。簇可以属于某个段,也可以不属于任何段,不属于任何段的簇中的页称为frament页。每个段包含若干簇和一些(<=16)frament page. 每个innodb表有两个段,叶子段和非叶子段。非叶子段中都是Btree非叶子页。叶子段中的页包括叶子页和blob行外页(如果有行外数据)。innodb数据组织方式可以参考这里。
行外页包括文件页头38个字节,blob页头8字节,文件尾8字节。
这里说明下blob页头信息
1)当前blog页存储的行外数据长度
2)当前blob页的下一个blob页。如果没有下一个页,则置0xFFFFFFFF
行外也最多可以存16k-(38+8+8)=16330字节的数据。
当一个50000字节的blob字段存储是需要((50000-768)/16330≈3.01)4个blog页来存储。在这种情况下,最后一个页才存储了242字节的数据。其余空间都浪费了。
2.3 行外记录存储条件(external record)
1 索引列不会产生行外数据。
2 条件:当行记录长度超过空page剩余空间的一半,则会产生行外记录。空page剩余空间是指没有任何记录的页中除去页头和页尾所占的空间。即超过(16k-(头120+尾12))/2=8126字节就会产生行外记录。(参考page_zip_rec_needs_ext)
3 一行记录可产生多个行外数据。生成行外数据的规则根据2中的条件需产生行外数据时,是每次从记录中选择最长的字段转为行外存储,如果转化后行列记录还超过8126字节,那么会选择次长的字段作为行外数据存储。这个过程会递归下去,直至行内数据小于8126字节。还有种极端情况就是,将所有可以转为行外存储的字段都转换后,行内数据还超过6866字节的情况,这是就会报错。ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 8126. You have to change some columns to TEXT or BLOBs
drop table t1;
create table t1(c1 blob,c2 blob,c3 blob,c4 blob,c5 blob,c6 blob,c7 blob,c8 blob,c9 blob,c10 blob,c11 blob);
insert into t1 values(repeat('0', 10000),repeat('1', 10000),repeat('2', 10000),repeat('3', 10000),repeat('4', 10000),repeat('5', 10000),repeat('6', 10000),repeat('7', 10000),repeat('8', 10000),repeat('9', 10000),repeat('10', 10000));
这条记录所有字段转化为行外数据后记录长度为8716>8126.因此innodb认为这条记录太大了存不下。
2*11+2+5+6+6+7+11*788=8716> 8126
Compact记录格式:物理存储的记录包括记录头和记录体 详细可以参考这里和这里。
记录头:变长非空字段长度数组+可空字段bitmap+extrabytes
记录体: 主键+trx_id+roll_ptr+其它字段
对于上面的记录:
记录头:变长非空字段长度数组(2*11)+可空字段bitmap(2)+extrabytes(5)
记录体: 主键(row_id 6)+trx_id(6)+roll_ptr(7)+其它字段(11*788)
4 并不是只有大字段会产生行外数据。其他变长类型如varachar, varbinary.当其长度超过M时,也可能产生行外数据。compact格式M=788,dynamic格式M=20.同样大字段也不一定都产生行外数据。
3 行外页的管理
3.1 何时申请行外页
1 insert:插入数据时如果有字段满足行外记录存储的条件,则会从叶子段中申请blob page。
2 update: 更新变长字段时,innodb先delete 老记录(真正删除,不是delete mark),再插入新记录。插入的新记录又会进行是否满足行外记录存储的判断。如满足条件则会从叶子段中申请blob page。因此,更新变长记录有可能导致原来不是行外存储的非更新字段变为行外存储。如果更新的是行外存储的字段,新记录字段有可能变为行内存储。
3.2 何时释放行外页
1 rollabak:
1) insert产生行外页回滚时需将申请的行外页释放
2) update操作产生新的行外页回滚时需将申请的行外页释放
2 purge
1)delete 包含行外数据的行,提交后,数据没有真正删除,而只是在记录头extrabytes中打上了删除标记。在purge清理undo时,根据undo记录真正删除记录时,将行外页释放。
2)update更新有行外数据的列,真正删除老记录时不会把行外页释放。而是等到提交后purge时才真正释放。
3.4 行外页的重用
1 当更新变长字段时,innodb先delete 老记录,再插入新记录。delete老记录并没有释放行外页。这个就是为了重用。例如,当更新的字段为非行外字段数据时,其他有行外数据的字段并没有更新。此时插入的新记录行外并没有改变,仍然指向老的行外数据页。这样新插入记录不需要新分配blog页,再copy行外数据。
2)更新的字段为行外字段数据时。老记录行外页并没有释放。这样回滚日志中不需要记录整个行外数据,只记录行内数据即可。回滚时可以重用老的行外数据。
3.5 拥有标记和继承标记
前面讲到行外信息中有拥有标记和继承标记。标记的意义:
高一位0表示拥有者,只有拥有者purge时才可以free
高二位1表示继承者,继承者在回滚时不能free
行外页的释放原则:
1)rollback时free,必须是拥有者,并且不能是继承者。
2)purge时free,必须是拥有者。
4 行外页的使用实践
初始化空表
drop table t;
CREATE TABLE t(a INT PRIMARY KEY,b TEXT, c varchar(100),d int)ENGINE=InnoDB;
4.1 insert
INSERT INTO t VALUES(1,repeat('a',12345),'cc',1);
insert: 产生的行外记录,行外信息标记为拥有者(0)和非继承者(0)
回滚:释放blob页
purge:insert 不需要purge
4.2 update
1 更新主键
update t set a=10 where a=1;
老记录delete mark, insert新记录,blog 页重用。
修改老记录行外信息标记为非拥有者(1)
新记录行外信息标记为拥有者(0)和继承者(1)
回滚:1)删除新插入的记录,跟据回滚释放blog页规则不释放blob page.
2)在delete mark的记录上的基础上修改。并将所有行外数据置为拥有者。
purge: 删除delete mark记录,但也不释放blob page.
2 更新行外数据字段
update t set b=repeat('b',12345) where a=1;
老记录真正删除,插入新记录。
老记录真正删除时blob页不free
新老记录的标记均为拥有者(0)和非继承者(0)
回滚:1)在插入的记录的基础上构造还原记录。释放更新新产生的blob page,删除插入的记录。
2)插入还原记录,并修改所有行外字段为拥有者。
purge: 释放老的blob page.
3 更新其他变长字段
update t set c='ccccc' where a=1;
老记录真正删除,插入新记录。老记录真正删除时blob页不free
新老记录的标记均为拥有者(0)
回滚:1)根据插入的记录更新构造还原记录,删除插入的记录。
2)插入还原记录,并修改所有行外字段为拥有者。
purge:不释放blob页。
4 更新定长字段
update t set d=2 where a=1;
原地更新,行外字段没有变化。
回滚和purge时均不需要操作blob 页。
4.3 delete
delete from t where a=1;
delete时记录头delete mark, blob页不free
回滚不需要处理blob页。
purge时free blob页。
4.4 混合实践
上节介绍了insert,delete,udpate这些单一原子操作,混合操作都由这些原子操作组成。
1 更新主键,再更新行外字段
update t set a=10 where a=1;
update t set b=repeat('b',12345) where a=10;
回滚:反向应用undo日志,只释放更新行外产生的日志。
purge:正向purge undo日志,purge delete mark时,不释放。purge更新主键时才释放。
2 更新主键,再更新变长字段
update t set a=10 where a=1;
update t set c='ccccc' where a=10;
回滚:反向应用undo日志,不释放blob页。
purge:正向purge undo日志,purge delete mark时,不释放。purge更新主键时才释放。
思考:
1 blob pape属于叶子段。叶子段存储真正的行内数据。blog page也在页子段。这样会出现同一个extent中即有叶子页也有blob页。一个extent中的页在物理上是连续的,这样可以增加记录在物理上的连续性。而blog page则破坏了这种连续性。对于只查询非行外字段的查询有较影响,同时还会影响预读机制。因此,blog page可以用单独的段存储,从而不会影响叶子段。
2 同一个page只存储一个行外字段数据。多个行外数据不能公用blob page.造成blob page的浪费。
blob及行外数据的更多相关文章
- TCP带外数据
传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方.为了发送这些数据,协议一般不使用与普通数据相同 ...
- InnoDB的行溢出数据,Char的行结构存储
行溢出数据 InnoDB存储引擎可以将一条记录中的某些数据存储在真正的数据页面之外,即作为行溢出数据.一般认为BLOB.LOB这类的大对象列类型的存储会把数据存放在数据页面之外.但是,这个理解有点偏差 ...
- 在HDInsight中从Hadoop的兼容BLOB存储查询大数据的分析
在HDInsight中从Hadoop的兼容BLOB存储查询大数据的分析 低成本的Blob存储是一个强大的.通用的Hadoop兼容Azure存储解决方式无缝集成HDInsight.通过Hadoop分布式 ...
- MySQL 行溢出数据
MySQL 行溢出数据 MySQL 对一条记录占用的最大储存空间是有限制的,除了 BLOB 和 TEXT 类型之外,其他所有列 (不包括隐藏列和记录头信息) 占用的字节长度不能超过 65535 个字节 ...
- .net dataGridView当鼠标经过时当前行背景色变色;然后【给GridView增加单击行事件,并获取单击行的数据填充到页面中的控件中】
1.首先在前台dataGridview属性中增加onRowDataBound属性事件 2.然后在后台Observing_RowDataBound事件中增加代码 protected void Obser ...
- 在VC下采用ADO实现BLOB(Binary)数据的存储,读取,修改,删除。
在VC下采用ADO实现BLOB(Binary)数据的存储,读取,修改,删除. 作者:邵盛松 2009-09-05 前言 1关于的BLOB(Binary)数据的存储和读取功能主要参考了MSDN上的一篇& ...
- resultset 对象获取行字段数据时报:java.sql.SQLException: Column 'id' not found.
resultset 对象获取行字段数据时报:java.sql.SQLException: Column 'id' not found. 代码: String sql="SELECT d.co ...
- jqGrid中选择的行的数据[转]
如何获取jqGrid中选择的行的数据? 下面可以获取选择一行的id,如果你选择多行,那下面的id是最后选择的行的id: var id=$(‘#gridTable’).jqGrid(‘getGridPa ...
- 网络IPC:套接字之带外数据
带外数据(Out-of-band data)是一些通信协议所支持的可选特征,允许更高优先级的数据比普通数据优先传输.即使传输队列已经有数据,带外数据先行传输.TCP支持带外数据,但是UDP不支持.套接 ...
随机推荐
- c++ primer 5th 练习3.43
#include <iostream> using namespace std; int main() { ][]={,,,,,,,,,,,}; /* for(int (&i)[4 ...
- APK Downgrade Method working fine on LINE latest version 6.7.1
Line is one of the most popular messaging Apps, especially in Asia. On March 3 I downgraded the app ...
- windows命令行(Command Prompt / Console)字体设置
1.运行 regedit 打开注册表编辑器,打开注册表定位至[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Conso ...
- CentOS 问题集锦
在CentOS 6更新后,不可避免的会在启动选项中产生多个内核选项,一个内核文件大概占100兆左右(一般100M以下),可以使用以下命令进行删除多余的内核. 1.首先列出系统中正在使用的内核: # u ...
- PHPDocument 代码注释规范总结
PHPDocument 代码注释规范 1. 安装phpDocumentor(不推荐命令行安装)在http://manual.phpdoc.org/下载最新版本的PhpDoc放在web服务器目录下使得通 ...
- 《算法设计手册》面试题解答 第五章:图的遍历 附:DFS应用之找挂接点
第五章面试题解答 5-31. DFS和BFS使用了哪些数据结构? 解析: 其实刚读完这一章,我一开始想到的是用邻接表来表示图,但其实用邻接矩阵也能实现啊?后来才发现应该回答,BFS用队列实现:DFS可 ...
- Apache错误:[error] (OS 10038)在一个非套接字上尝试了一个操作
Apache错误:[error] (OS 10038)在一个非套接字上尝试了一个操作 博客分类: vb2005xu软件学习 OSApache防火墙PHPWindows 日志如下:[ ...
- jqeury datatable
1.自定义列信息 "aoColumnDefs":[ { ...
- 三天没有写题了,罪过!--Hash Table Start
(1)Island Perimeter 解题思路: 在矩阵上循环并记录岛(1)的个数;如果当前节点是岛,则检查其是否具有任何右邻居或下邻居,有的话邻居计数加1 ;岛的周长结果为islands * 4 ...
- OpenLDAP与phpldapadmin的搭建
最近一直在看LDAP的东西,把自己的记录下来,以后可以看看. 1:环境 1):关闭防火墙 service iptables stop 2):setenforce 0 vim /etc/sysconfi ...