MySQL实战系列:大字段如何优化
Updated:
背景
线上发现一张表,1亿的数据量,物理大小尽然惊人的大,1.2T
最终发现,原来有很多字段,10个varchar,1个text
这么大的表,会给运维带来很大的痛苦:DDL咋办?恢复咋办?备份咋办?
基本知识:InnoDB Storage Architecture for InnoDB On Disk Format
蓝图: database —> tablespaces —> pages —> rows —> columns
InnoDB 物理结构存储结构
InnoDB 逻辑存储结构
InnoDB page 存储结构
页类型
数据页(B-tree Node)
undo页(undo Log Page)
系统页(System Page)
事务数据页(Transaction system Page)
插入缓冲位图页(Insert Buffer Page)
未压缩的二进制大对象页(Uncompressd BLOB Page)
压缩的二进制大对象页(compressd BLOB Page)
页大小
默认16k(若果没有特殊情况,下面介绍的都是默认16k大小为准)
一个页内必须存储2行记录,否则就不是B+tree,而是链表了
结构图
InnoDB row 存储结构
rows 文件格式总体规划图
row-fomat为Compact的结构图
row-fomat为Redundant的结构图
不常用
compress & dynamic 与 Compact 的区别之处
字段之字符串类型
char(N) vs varchar(N)
不管是char,还是varchar,在compact row-format格式下,NULL都不占用任何存储空间
在多字节字符集的情况下,CHAR vs VARCHAR 的实际行存储基本没区别
CHAR不管是否是多字符集,对未能占满长度的字符还是会填充0x20
规范中:对char和varchar可以不做要求
varchar(N) : 255 vs 256
当实际长度大于255的时候,变长字段长度列表需要用两个字节存储,也就意味着每一行数据都会增加1个字节
实测下来存储空间增长并不算大,且性能影响也不大,所以,尽量在256之内吧
varchar(N) & char(N) 的最大限制
char的最大限制是: N<=255
varchar 的最大限制是: N<=65535 , 注意官方文档说的是N是字节,并且说的是一行的所有字段的总和小于65535,而varchar(N)中的N表示的是字符。
测试后发现,65535并不是最大限制,最大的限制是65532
|
|
off-page: 行溢出
- 为什么会有行溢出off-page这个概念呢
假设创建了一张表,里面有一个字段是a varchar(30000) , innoDB的页才16384个字节,如何存储的下呢?所以行溢出就来了嘛
- 如何看出行溢出了?
可以通过姜承尧写的工具查看
其中溢出的页有 Uncompressed BLOB Page: 243453
|
|
- 溢出有什么危害
溢出的数据不再存储在B+tree中
溢出的数据使用的是uncompress BLOB page,并且存储独享,这就是存储越来越大的真正原因
通过下面的测试,你会发现,t_long 插入的数据仅仅比 t_short 多了几个字节,但是最终的存储却是2~3倍的差距
|
|
- 什么情况下会溢出
原则:只要一行记录的总和超过8k,就会溢出。
所以:varchar(9000) 或者 varchar(3000) + varchar(3000) + varchar(3000),当实际长度大于8k的时候,就会溢出
所以:Blob,text,一行数据如果实际长度大于8k会溢出,如果实际长度小于8k则不会溢出,并非所有的blob,text都会溢出
- 多列总和大字段 vs 一列大字段
多个大字段会导致多次off-page
|
|
如何对大字段进行优化
如果有多个大字段,尽量序列化后,存储在同一列中,避免多次off-page
将text等大字段从主表中拆分出来,a)存储到key-value中 b)存储在单独的一张子表中,并且压缩
必须保证一行记录小于8k
参考
http://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html
INNOBASE 官方文档
MySQL技术内幕 InnoDB存储引擎 —姜承尧
http://hedengcheng.com/ —何登成
http://imysql.cn/ —叶金荣