MySQL表空间

记录下MySQL的表空间。

在MySQL官方文档中,很多行(row)记录在页(page)中,页记录在区(extent)中,区记录在段(segment)中,段则在表空间中。表空间就是tablespace。

 

其中,区是最小的空间申请单位,通常一次性申请4个区。区的固定大小是1M。以MySQL默认的16k页(innodb_page_size)大小为例,一个区就有64个页。另外,如果你设置页大小为32k和64k,区大小会变成2M和4M。

这个参数设小,比如说4k。缺点就是会增加IO,b+树高度会增高。目前的趋势是趋向于宽表,单行会很大,很多你一行数据就1k了。数据再多就涉及行外存了。我是觉得16K就挺不错。

 

 

页则是最小的IO操作单位。

默认是16k。除非你压缩表。MySQL的压缩表是基于页的。看看你表的row_format是不是compressed,key_block_size就是压缩后的页大小。这个也不是页越小,表大小越好。这个它的压缩算法有关。没记错是用得zlib。比如说你要压成8k。他会尝试把你页压缩到8k,能压下来,那就成功。如果只压缩到16k,那么从内存往磁盘写的时候,会分裂成2个8k的页。等于没变。

 另外key_block_size你是可以设置成16k的,你会发现页大小没变,但是数据大小确实是变小了。因为一页能存放更多的row了。

如果你的业务是写远远大于读的。那么其实设置成压缩表是很合适的。

页的结构如下:

 

 

page header和page trailer的作用很简单,里面放着lsn,前后都是一致的,用来保证页的数据是完整的。对于mysql来说,如果页损坏了, 那基本上就寄了。

基本上header的长度不会超过200字节。

其次就是row offset array。对于mysql来说,查询只能定位到页,而不是记录,而页中的记录其实是无序的,比如row 1 是10,row 2 是6, row 3 是20。所以在row offset array中就存放了当前页中,row的排序后的指针,用来指向具体的row。row和row直接是逻辑有序的,有next指针。页中的记录查找是用的二分查找法。

从5.7开始,row格式应该默认用的就是dynamic。

redudent已经淘汰了,不讨论了。

compact是5.0开始的默认。

compressed是压缩格式。

dynamic和compact是一样的。只有在存储大对象的时候有区别。

继续画个图,来说明下compact和dynamic的区别。

假设我们的表有两个列,id int , name varchar。

如果这个varchar很小,两个列加起来只有2,3k。那么毫无疑问,他能完整的存在一个页中,我们这里讨论这个varchar相对大。大到一列数据能有1m以上。

 

 

 

 

 compact就像这样,id占正常的8字节,name占768字节(为什么是768?其实没有为什么,代码写死的。)后面还有20字节,用来存放指向溢出页的地址。然后就一直用页来填充,每个页还会继续指向下一个溢出页,直到数据填满。

 

dynamic如下:

 

 很明显,name列全部弄到溢出页了。

只要单行记录大小 > page_siz/2 就会溢出。

多列的情况下,他会选择最大的一个列,将那列只存20个字节的地址,其他全部数据会放到另一个16k的页。如果这样存储了row还是大于8k,那么递归。

 

另外row还有隐藏的列,如果你没有显示指定主键,那么会增加一个6个字节的int row_id 。除此之外还有6字节trx id列和7字节rollback列。后面这个两个用来实现MVCC的。也就是说,mysql实际存储的大小,会比你实际算出来的要大。

 

5.7之后,mysql还支持透明页压缩。建表的时候加上compressin='lz4'。

lz4更快,zlib压缩比更大。

这种特性,在你压缩表之后,你是不知道页被压缩到多大的,可能是4k,可能是8k。

专业名词叫hole punch。这个不是mysql实现的。是文件系统层面的东西。

我们先抛弃mysql,只从系统层面来说。

windows其实大家可能更熟悉一点,右键一个文件。实际大小,和占用大小。实际大小就是你这个文件真正意义上的大小。而占用大小,是你占用磁盘的大小。

打个比方,你去apple买iphone,店员给了你个盒子,里面装着iphone。你的手机+说明书+数据线就是你的数据大小。但是这一大堆物品,在现实生活中实际占用了一个盒子的大小,这个盒子的大小就是占用大小。

当然,这个盒子和你的文件系统和block大小有关。我的windows都是用的ntfs+4096字节block。这就是说,哪怕我的数据只有1kb,他在磁盘上也占用了4096字节。

 

那么回到linux。

我们之前有提到fopen这个systemcall。他的有一个参数是O_DIRECT.

今天我们介绍他的第二个参数:O_PUNCHHOLE。

假设我们有一页是4K,但是只有前面的3K有数据,那么在这种情况下,fwrite实际上只会加载3k数据。

 

而回到mysql,假设一页16k,我们只写了6k数据。那么此时mysql会在这页后面写10k的0。然后开始向disk落盘。他会以操作系统的block的倍数,向disk中写入。(比如说你操作系统默认4k页大小。那么就只能被压缩成4,8,16k。)

比如上面这个6k的例子,disk中就只有8k大小了。

而这种方式最大的好处,就是在bf中不会增大bf的消耗。都是以16k在内存中进行管理。

 

所以,如果你要开启压缩。我建议透明页压缩。

 5.7版本这玩意好像有bug。建议还是8.0版本用吧。5.7你去做透明页压缩会有warning,mysql给你说你文件系统不支持punch hole,但其实这不是文件系统的问题。是mysql没有把punch hole编译进去。你要用,你得自己去改源码。

改源码也不麻烦,2行代码。或者你直接用percona版本。percona帮你打开了的。

 

posted @   拿什么救赎  阅读(1345)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示