转自https://yq.aliyun.com/articles/59256?spm=5176.100239.blogcont59257.9.5MLR2d

摘要: 背景 线上发现一张表,1亿的数据量,物理大小尽然惊人的大,1.2T 最终发现,原来有很多字段,10个varchar,1个text 这么大的表,会给运维带来很大的痛苦:DDL咋办?恢复咋办?备份咋办? 基本知识:InnoDB Storage Architecture for InnoDB On

背景

线上发现一张表,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

  1. [MySQL 5.6.27]
  2. * char的最大限制是: N<=255
  3. root:test> create table test( a char(65535))charset=latin1 engine=innodb;
  4. ERROR 1074 (42000): Column length too big for column 'a' (max = 255); use BLOB or TEXT instead
  5. * 测试后发现,65535并不是最大限制,最大的限制是65532
  6. root:test> create table test( a varchar(65535))charset=latin1 engine=innodb;
  7. ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
  8. root:test> create table test( a varchar(65532))charset=latin1 engine=innodb;
  9. Query OK, 0 rows affected (0.00 sec)
  10. * varchar 的最大限制是: N<=65535 , 注意官方文档说的是N是字节,并且说的是一行的所有字段的总和小于65535,而varchar(N)中的N表示的是字符
  11. root:test> create table test_1( a varchar(30000),b varchar(30000),c varchar(5535))charset=latin1 engine=innodb;
  12. ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
  13. * varchar(N)中的N表示的是字符
  14. root:test> create table test_1( a varchar(50000))charset=utf8 engine=innodb;
  15. Query OK, 0 rows affected, 1 warning (0.00 sec)
  16. root:test> show warnings;
  17. +-------+------+--------------------------------------------+
  18. | Level | Code | Message |
  19. +-------+------+--------------------------------------------+
  20. | Note | 1246 | Converting column 'a' from VARCHAR to TEXT |
  21. +-------+------+--------------------------------------------+
  22. 1 row in set (0.00 sec)
  23. root:test> show create table test_1;
  24. +--------+-------------------------------------------------------------------------------+
  25. | Table | Create Table |
  26. +--------+-------------------------------------------------------------------------------+
  27. | test_1 | CREATE TABLE `test_1` (
  28. `a` mediumtext
  29. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
  30. +--------+-------------------------------------------------------------------------------+
  31. 1 row in set (0.00 sec)

off-page: 行溢出

  • 为什么会有行溢出off-page这个概念呢

假设创建了一张表,里面有一个字段是a varchar(30000) , innoDB的页才16384个字节,如何存储的下呢?所以行溢出就来了嘛

  • 如何看出行溢出了?

可以通过姜承尧写的工具查看
其中溢出的页有 Uncompressed BLOB Page: 243453

  1. [root()@xx script]# python py_innodb_page_info.py t.ibd
  2. Total number of page: 537344:
  3. Insert Buffer Bitmap: 33
  4. Freshly Allocated Page: 74040
  5. File Segment inode: 1
  6. B-tree Node: 219784
  7. File Space Header: 1
  8. 扩展描述页: 32
  9. Uncompressed BLOB Page: 243453
  • 溢出有什么危害

溢出的数据不再存储在B+tree中
溢出的数据使用的是uncompress BLOB page,并且存储独享,这就是存储越来越大的真正原因
通过下面的测试,你会发现,t_long 插入的数据仅仅比 t_short 多了几个字节,但是最终的存储却是2~3倍的差距

  1. * 表结构
  2. root:test> show create table t_long;
  3. +--------+---------------------------------------------------------------------------------------------------------+
  4. | Table | Create Table |
  5. +--------+---------------------------------------------------------------------------------------------------------+
  6. | t_long | CREATE TABLE `t_long` (
  7. `id` int(11) DEFAULT NULL,
  8. `col1` text
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
  10. +--------+---------------------------------------------------------------------------------------------------------+
  11. 1 row in set (0.00 sec)
  12. root:test> show create table t_short;
  13. +---------+----------------------------------------------------------------------------------------------------------+
  14. | Table | Create Table |
  15. +---------+----------------------------------------------------------------------------------------------------------+
  16. | t_short | CREATE TABLE `t_short` (
  17. `id` int(11) DEFAULT NULL,
  18. `col1` text
  19. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
  20. +---------+----------------------------------------------------------------------------------------------------------+
  21. 1 row in set (0.00 sec)
  22. * 测试案例
  23. foreach $num (1 .. 48849){
  24. $sql_1 = "insert into $table_short select $num,repeat('a',8090)";
  25. $sql_2 = "insert into $table_long select $num,repeat('a',8098)";
  26. `$cmd -e " $sql_1 "`;
  27. `$cmd -e " $sql_2 "`;
  28. }
  29. * 最终的记录数
  30. root:test> select count(*) from t_short;
  31. +----------+
  32. | count(*) |
  33. +----------+
  34. | 48849 |
  35. +----------+
  36. 1 row in set (0.03 sec)
  37. root:test> select count(*) from t_long;
  38. +----------+
  39. | count(*) |
  40. +----------+
  41. | 48849 |
  42. +----------+
  43. 1 row in set (0.02 sec)
  44. * 页类型的比较
  45. [root()@xx script]# python py_innodb_page_info.py /data/mysql_data/test/t_short.ibd
  46. Total number of page: 25344:
  47. Insert Buffer Bitmap: 2
  48. Freshly Allocated Page: 887
  49. File Segment inode: 1
  50. B-tree Node: 24452
  51. File Space Header: 1
  52. 扩展描述页: 1
  53. [root()@xx script]# python py_innodb_page_info.py /data/mysql_data/test/t_long.ibd
  54. Total number of page: 60160:
  55. Insert Buffer Bitmap: 4
  56. Freshly Allocated Page: 8582
  57. File Segment inode: 1
  58. B-tree Node: 2720
  59. File Space Header: 1
  60. 扩展描述页: 3
  61. Uncompressed BLOB Page: 48849
  62. * 最终大小的对比
  63. [root()@xx test]# du -sh * | grep 'long\|short' | grep ibd
  64. 941M t_long.ibd
  65. 397M t_short.ibd
  66. * 结论
  67. t_short 的表,在400M左右可以理解,因为 8k * 48849 = 400M
  68. t_long 的表,由于独享48849Uncompressed BLOB Page,严重浪费空间
  • 什么情况下会溢出

原则:只要一行记录的总和超过8k,就会溢出。
所以:varchar(9000) 或者 varchar(3000) + varchar(3000) + varchar(3000),当实际长度大于8k的时候,就会溢出
所以:Blob,text,一行数据如果实际长度大于8k会溢出,如果实际长度小于8k则不会溢出,并非所有的blob,text都会溢出

  • 多列总和大字段 vs 一列大字段

多个大字段会导致多次off-page

  1. root:test> show create table t_3_col;
  2. +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------
  3. ---------------------------------------+
  4. | Table | Create Table
  5. |
  6. +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------
  7. ---------------------------------------+
  8. | t_3_col | CREATE TABLE `t_3_col` (
  9. `id` int(11) DEFAULT NULL,
  10. `col1` varchar(7000) DEFAULT NULL,
  11. `col2` varchar(7000) DEFAULT NULL,
  12. `col3` varchar(7000) DEFAULT NULL
  13. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
  14. +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------
  15. ---------------------------------------+
  16. 1 row in set (0.00 sec)
  17. root:test> show create table t_1_col;
  18. +---------+---------------------------------------------------------------------------------------------------------------------------------+
  19. | Table | Create Table |
  20. +---------+---------------------------------------------------------------------------------------------------------------------------------+
  21. | t_1_col | CREATE TABLE `t_1_col` (
  22. `id` int(11) DEFAULT NULL,
  23. `col1` varchar(21000) DEFAULT NULL
  24. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
  25. +---------+---------------------------------------------------------------------------------------------------------------------------------+
  26. 1 row in set (0.00 sec)
  27. root:test>
  28. root:test>
  29. root:test> insert into t_1_col(col1) select repeat('a',21000);
  30. Query OK, 1 row affected (0.00 sec)
  31. Records: 1 Duplicates: 0 Warnings: 0
  32. root:test>
  33. root:test>
  34. root:test> insert into t_3_col(col1,col2,col3) select repeat('a',7000),repeat('a',7000),repeat('a',7000);
  35. Query OK, 1 row affected (0.00 sec)
  36. Records: 1 Duplicates: 0 Warnings: 0
  37. [root()@xx script]# python py_innodb_page_info.py /data/mysql_data/test/t_1_col.ibd
  38. Total number of page: 6:
  39. Insert Buffer Bitmap: 1
  40. Uncompressed BLOB Page: 2
  41. File Space Header: 1
  42. B-tree Node: 1
  43. File Segment inode: 1
  44. [root()@xx script]# python py_innodb_page_info.py /data/mysql_data/test/t_3_col.ibd
  45. Total number of page: 7:
  46. Insert Buffer Bitmap: 1
  47. Uncompressed BLOB Page: 3
  48. File Space Header: 1
  49. B-tree Node: 1
  50. File Segment inode: 1

如何对大字段进行优化

如果有多个大字段,尽量序列化后,存储在同一列中,避免多次off-page
将text等大字段从主表中拆分出来,a)存储到key-value中 b)存储在单独的一张子表中,并且压缩
必须保证一行记录小于8k

0809MySQL实战系列:大字段如何优化|数据存储结构的更多相关文章

  1. memcached实战系列(六)理解Memcached的数据存储方式

    Memcached的数据存储方式被称为Slab Allocator,其基本方式是: 1:先把内存分成很多个Slab,这个大小是预先规定好的,以解决内存碎片的问题.启动参数的时候配置进去的不懂得可以参考 ...

  2. 【mysql】关于InnoDB表text blob大字段的优化

    最近在数据库优化的时候,看到一些表在设计上使用了text或者blob的字段,单表的存储空间已经达到了近100G,这种情况再去改变和优化就非常难了 一.简介 为了清楚大字段对性能的影响,我们必须要知道i ...

  3. ByteArrary(优化数据存储和数据流)

    原地址:http://www.unity蛮牛.com/blog-1801-799.html 首页 博客 相册 主题 留言板 个人资料   ByteArrary(优化数据存储和数据流) 分类:unity ...

  4. Spring Boot干货系列:(八)数据存储篇-SQL关系型数据库之JdbcTemplate的使用

    Spring Boot干货系列:(八)数据存储篇-SQL关系型数据库之JdbcTemplate的使用 原创 2017-04-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章介绍了一些基础,但都是静 ...

  5. Berkeley DB的数据存储结构——哈希表(Hash Table)、B树(BTree)、队列(Queue)、记录号(Recno)

    Berkeley DB的数据存储结构 BDB支持四种数据存储结构及相应算法,官方称为访问方法(Access Method),分别是哈希表(Hash Table).B树(BTree).队列(Queue) ...

  6. kafka 数据存储结构+原理+基本操作命令

    数据存储结构: Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相互独立的.每个topic又可以分成几个不同的partition(每个topic有几个partitio ...

  7. Cassandra 的数据存储结构——本质是SortedMap<RowKey, SortedMap<ColumnKey, ColumnValue>>

    Cassandra 的数据存储结构 Cassandra 的数据模型是基于列族(Column Family)的四维或五维模型.它借鉴了 Amazon 的 Dynamo 和 Google's BigTab ...

  8. memcached实战系列(七)理解Memcached的数据过期方式、新建过程、查找过程

    1.1.1. 新建Item分配内存过程 1:快速定位slab classid,先计算Item长度 key键长+flag+suffix(16字节)+value值长+结构大小(32字节),如90byte ...

  9. Redis之数据存储结构

    今天去中关村软件园面试,被问到:你做项目用到的Redis处理数据用的什么结构?顿时石化,”用到的结构,不就是key-value嘛,还有什么结构?“.面试官说:“平时除了工作,要加强学习,下面的面试我觉 ...

随机推荐

  1. POJ2559 Largest Rectangle in a Histogram 单调栈

    题目大意 有一个直方图,其所有矩形的底均是1(以后简称小矩形).给出这些矩形的高度,求这些矩形的并集中存在的面积最大的矩形(简称大矩形)的面积. 题解 大矩形的高必然一边等于一个小矩形的高,另一边小于 ...

  2. 使用playonlinux安装windows软件

    转载 http://qspy.is-programmer.com/posts/40913.html Wine提供了一个用来运行Windows程序的平台.PlayOnLinux 是使用 Python 写 ...

  3. 麦森数--NOIP2003

    题目描述 形如2P−12^{P}-12P−1 的素数称为麦森数,这时PPP 一定也是个素数.但反过来不一定,即如果PPP 是个素数,2P−12^{P}-12P−1 不一定也是素数.到1998年底,人们 ...

  4. PCB SQL MS 将多行有序数据转为一行数据(一列转一行)

    一.原数据:多行有序 SELECT CC.techname FROM PPEflow BB LEFT JOIN pubgyxxb CC ON BB.techno = CC.techno ORDER B ...

  5. 0420-mysql命令(数据库操作层级,建表,对表的操作)

    注意事项: 符号必须为英文. 数据库操作层级: 建表大全: #新建表zuoye1:drop table if exists zuoye1;create table zuoye1(    id int ...

  6. [Swift]LeetCode1071.字符串的最大公因子 | Greatest Common Divisor of Strings

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  7. Spring Boot (6) Spring Data JPA

    JPA 全称Java Persistence API,JPA通过JDK 5.0注解或xml描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中 JPA是sun官方提出的java持久化规范, ...

  8. mysqlslap: Error when connecting to server: 2001 Can't create UNIX socket (24) 解决方法

    在用mysqlslap对mysql进行压力测试遇到mysqlslap: Error when connecting to server: 2001 Can't create UNIX socket ( ...

  9. java如何设置文件的权限

    import java.io.File; 2.import java.io.IOException; 3./× 4.×只能给当前用户赋予对该文件的权限,调用createNewFile()方法默认的权限 ...

  10. HDU_1969_二分

    Pie Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...