布景
前面提到,咱们有个云文档项目的快照内容是直接存储到db的,属于大文本存储,文档快照的内容字段大部分都是kb等级,部分乃至到MB等级。现在关于数据的读取,已经进行了CDN缓存优化(静态资源缓存利器——CDN),关于数据的写入和存储还有待优化,假设能够经过一些紧缩算法在大文本进行紧缩存储,能够在很大程度上节约DB的存储空间,缓解DB的I/O压力。
存量数据剖析
select
table_name as '表名',
table_rows as '记载数',
truncate(data_length/1024/1024, 2) as '数据容量(MB)',
truncate(index_length/1024/1024, 2) as '索引容量(MB)',
truncate(DATA_FREE/1024/1024, 2) as '碎片占用(MB)'
from
information_schema.tables
where
table_schema=${数据库名}
order by
data_length desc, index_length desc;
相关内容介绍
innodb引擎页数据超出16kb怎么办?
咱们都知道innodb的页块默许巨细为16k,假设表中一行数据长度超出了16k,就会出现行溢出,溢出的行是寄存在另外的当地(uncompress blob page)。因为innodb选用聚簇索引把数据进行寄存起来,即B+Tree结构,因而每个页块中至少有两行数据,不然就失去了B+Tree的含义,这样就得出一行数据最大的长度限制为8k(大字段在数据页会存储768个字节数据,剩下的数据溢出到另外的页中,数据页还有20个字节记载溢出页的地址)
- 对 dynamic 格局来说,假设大目标字段(text/blob)存储数据巨细小于 40 字节,那全部放在数据页,剩下的场景,数据页只保存一个 20 字节的指针指向溢出页。 这种场景下,假设每个大目标字段保存的数据小于 40 个字节,也就和 varchar(40),作用相同。
- innodb-row-format-dynamic:dev.mysql.com/doc/refman/…
Linux 稀少文件 & 空泛
- 稀少文件(Sparse File):稀少文件与其他一般文件基本相同,差异在于文件中的部分数据全为0,且这部分数据不占用磁盘空间
- 文件空泛:文件位移量能够大于文件的实践长度(位于文件中但未被写过的字节被设为0),空泛是否占用磁盘空间由操作体系决定
文件空泛部分不占用磁盘空间、文件所占用的磁盘空间仍然是接连的
innodb供给的紧缩计划
页面紧缩
适用场景:因为数据量太大,磁盘空间不足,负载首要体现在IO上,而服务器的CPU又有比较多的余量的场景。
1)COMPRESS页紧缩
相关文档:dev.mysql.com/doc/refman/…
- 在MySQL5.7版本之前就供给的页紧缩功能,在创建表时指定 ROW_FORMAT = COMPRESS,并经过 KEY_BLOCK_SIZE 设置紧缩页的巨细
- 存在规划上的缺陷,有可能会导致功能下降明显,然后其规划初衷是为了进步功能,引入了“日志即数据”的理念
- 关于紧缩页的数据修正,并不会直接修正页本身,而是将修正日志存储在这个页中,这的确对数据的变更比较友好,不用每次修正都进行紧缩/解压
- 关于数据的读取,紧缩的数据是无法直接读取的,所以这种算法会在内存中保存一个解压后的16K的页,以供数据的读取
- 这就导致了一个页在缓冲池中可能会有两个版本(紧缩版和非紧缩版),引发一个十分严重的问题,即缓冲池中能缓存的页的数量大大的削减了,然后可能会导致数据库的功能极大的下降
- 关于紧缩页的数据修正,并不会直接修正页本身,而是将修正日志存储在这个页中,这的确对数据的变更比较友好,不用每次修正都进行紧缩/解压
2)TPC(通明页紧缩)
相关文档:dev.mysql.com/doc/refman/…
- 工作原理:写入页面时,运用指定的紧缩算法对页面进行紧缩,紧缩后写入磁盘,其间经过打孔机制从页面末尾开释空(需求操作体系支撑
空泛
特性) -
ALTER TABLE xxx COMPRESSION = ZLIB
能够启用TPC页紧缩功能,但这仅仅对后续增量数据进行紧缩,假设希望对整个表进行紧缩,则需求履行OPTIMIZE TABLE xxx
- 实现过程:一个紧缩页在缓冲池中都是一个16K的非紧缩页,只要在数据刷盘的时分,会进行一次紧缩,紧缩后剩下的空间会用 0x00 填满,运用文件体系的空泛特性(hole punch)对文件进行裁剪,开释 0x00 占用的稀少空间
- TPC虽好,但它依靠操作体系的 Hole Punch 特性,且裁剪后的文件巨细需求和文件体系块巨细对齐(4K)。即假设紧缩后的页巨细是9K,那么实践占用的空间是12K
列紧缩
MySQL现在没有直接针对列紧缩的计划,有一个曲线救国的方法,就是在事务层运用MySQL供给的紧缩和解压函数来针对列进行紧缩和解压操作。也就是假设需求对某一列做紧缩,在写入时调用COMPRESS
函数对那个列的内容进行紧缩,读取的时分,运用UNCOMPRESS
函数对紧缩过的数据进行解压。
- 运用场景:针对表中某些列数据长度比较大的情况,一般是 varchar、text、blob、json等数据类型
- 相关函数:
- 紧缩函数:
COMPRESS()
- 解紧缩函数:
UNCOMPRESS()
- 字符串长度函数:
LENGTH()
- 未解压字符串长度函数:
UNCOMPRESSED_LENGTH()
- 紧缩函数:
- 测验:
- 刺进数据:
insert into xxx (content) values (compress('xxx....'))
- 读取紧缩的数据:
select c_id, uncompressed_length(c_content) uncompress_len, length(c_content) compress_len from xxx
- 刺进数据:
为什么innodb供给的都是根据页面的紧缩技术?
- 记载紧缩:每次读写记载的时分,都要进行紧缩或解压,过度依靠CPU的核算才能,功能相对会比较差
- 表空间紧缩:紧缩效率高,但要求表空间文件是静态不增长的,这关于咱们大部分的场景都是不适用的
- 页面紧缩:既能进步效率,又能在功能中取得一定的平衡
总结
- 关于一些功能不灵敏的事务表,如日志表、监控表、告警表等,这些表只希望对存储空间进行优化,对功能的影响不是很重视,能够运用COMPRESS页紧缩
- 关于一些比较核心的表,则比较推荐运用TPC紧缩
- 列紧缩过度依靠CPU,功能方面会稍差,且对事务有一定的改造本钱,不够灵活,需求评估影响规模,做好切换的计划。好处是能够由事务端决定哪些数据需求紧缩,并控制解压操作
- 对页面进行紧缩,在事务侧不用进行什么改动,对线上完全通明,紧缩计划也十分老练
为什么要进行数据紧缩?
- 因为处理器和高速缓存存储器的速度进步超过了磁盘存储设备,因而许多时分工作负载都是受限于磁盘I/O。数据紧缩能够使数据占用更小的空间,能够节约磁盘I/O、削减网络I/O然后进步吞吐量,虽然会牺牲部分CPU资源作为代价
- 关于OLTP体系,经常进行update、delete、insert等操作,经过紧缩表能够削减存储占用和IO耗费
- 紧缩其实是一种平衡,并不一定是为了进步数据库的功能,这种平衡取决于解紧缩带来的收益和开销之间的一种权衡,但紧缩对存储空间来说,收益无疑是很大的
简单测验
innodb通明页紧缩(TPC)
参阅:dev.mysql.com/doc/refman/…
测验数据
1)创建表
- create table table_origin ( …… ) comment ‘测验原表’;
- create table table_compression_zlib ( …… ) comment ‘测验紧缩表_zlib’ compression = ‘zlib’;
- create table table_compression_lz4 ( …… ) comment ‘测验紧缩表_lz4’ compression = ‘lz4’;
2)往表中写入10w行测验数据
紧缩率
SELECT NAME, FS_BLOCK_SIZE, FILE_SIZE, ALLOCATED_SIZE
FROM information_schema.INNODB_TABLESPACES WHERE NAME like 'test_compress%';
-
FS_BLOCK_SIZE
:文件体系块巨细,也就是打孔运用的单位巨细 -
FILE_SIZE
:文件的表观巨细,表示文件的最大巨细,未紧缩 -
ALLOCATED_SIZE
:文件的实践巨细,即磁盘上分配的空间量
紧缩率:
- zlib:1320636416/3489660928 = 37.8%
- lz4:1566949376/3489660928 = 45%
耗时
- 循环刺进10w条记载
- 原表:918275 ms
- zlib:878540 ms
- lz4:875259 ms
- 循环查询10w条记载
- 原表:332519 ms
- zlib:373387 ms
- lz4:343501 ms