MySQL 压缩的使用场景和解决方案

导语

描述 MySQL 压缩的使用场景和解决方案,包括压缩传输协议、压缩列解决方案和压缩表解决方案。

提到 MySQL 压缩相关的内容,我们能想到的可能是如下几种和压缩相关的场景:

1、客户端和服务器之间传输的数据量太大,需要进行压缩,节约带宽

2、MySQL 某个列的数据量大,只针对某个列的数据压缩

3、MySQL 某个或者某几个表数据太多,需要将表数据压缩存放,减少磁盘空间的占用

这几个问题在 MySQL 侧都有很好的解决方案 ,针对第 1 个问题,可以使用 MySQL 的压缩协议解决;针对第 2 个问题,可以采用 MySQL 的压缩和解压函数完美解决;而针对最复杂的第 3 个问题,则可以在引擎层面进行解决,目前 myisam、innodb、tokudb、MyRocks 等引擎都支持表的压缩。本篇文章要详细讨论的就是此类关于 MySQL 压缩机制相关 的问题,下面是主要的内容:

一、MySQL 压缩协议介绍

1、适用场景

MySQL 压缩协议适合的场景是 MySQL 的服务器端和客户端之间传输的数据量很大,或者可用带宽不高的情况,典型的场景有如下两个:

a、查询大量的数据,带宽不够(比如导出数据的时候);

b、复制的时候 binlog 量太大,启用 slave_compressed_protocol 参数进行日志压缩复制。

2、压缩协议简介

压缩协议是 MySQL 通信协议的一部分,要启用压缩协议进行数据传输,需要 MySQL 服务器端和客户端都支持 zlib 算法。启动压缩协议会导致 CPU 负载略微上升。使用启用压缩协议使用-C 参数或者 --compress=true 参数启动客户端的压缩功能。如果启用了-C 或者 compress=true 选项,那么在连接到服务器段的时候,会发送 0x0020(CLIENT_COMPRESS)的服务器权能标志位,和服务器端协商通过后(3 次握手以后),就支持压缩协议了。由于采用压缩,数据包的格式会发生变化,具体的变化如下:

未压缩的数据包格式:

压缩后的数据包格式:

大家可能留意到压缩后的数据报格式有压缩和未压缩之分,这个是 MySQL 为了较少 CPU 开销而做的一个优化。如果内容小于 50 个字节的时候,就不对内容进行压缩,而大于 50 字节的时候,才会启用压缩功能。具体的规则如下:

当第三个字段的值等于 0x00 的时候,表示当前包没有压缩,因此 n * byte 的内容为 1 * byte,n * byte,即请求类型和请求内容。

当第三个字段的值大于 0x00 的时候,表示当前包已采用 zlib 压缩,因此使用的时候需要对 n * byte 进行解压,解压后内容为 1 * byte,n * byte,即请求类型和请求内容。

3、方案实践

在客户端连接的时候加上-C 或者--compress=true 参数。如果是对同步添加压缩协议支持的时候,则需要配置 slave_compressed_protocol=1。下面是采用压缩协议连接 MySQL 服务端的范例:

MySQL -h hostip -uroot -p password --compress

MySQLdump -h hostip -uroot -p password -default-character-set=utf8 --compress --single-transaction dbname tablename > tablename.sql

如果需要在主从复制中启用压缩传输,则在从机开启 slave_compressed_protocol=1 参数就 OK。

4、压缩效果

可以通过在 MySQLdump 中使用--compress 选项来观察压缩传输的效果,也可以通过主从复制中已用 slave_compressed_protocol 参数来观察压缩传输的效果,很容易看出效果,这里不再截图说明。

二、MySQL 列压缩解决方案

MySQL 针对列的压缩目前直接的方案并不支持,映象中腾讯的 TMySQL 可以直接针对列的压缩。这里主要介绍一个曲线救国的办法,那就是在业务层面使用 MySQL 提供的压缩和解压函数来针对列进行压缩和解压操作。也就是要对某一列做压缩,就需要在写入的时候调用 COMPRESS 函数对那个列的内容进行压缩,然后存放到对应的列。读取的时候,使用 UNCOMPRESSED 函数对压缩的内容进行解压缩。

1、适用场景

针对 MySQL 中某个列或者某几个列数据量特别大,一般都是 varchar、text、char 等数据类型。

2、压缩函数简介

MySQL 的压缩函数 COMPRESS 压缩一个字符串,然后返回一个二进制串。使用该函数需要 MySQL 服务端支持压缩,否则会返回 NULL,压缩字段最好采用 varbinary 或者 blob 字段类型保存。使用 UNCOMPRESSED 函数对压缩过的数据进行解压。注意,采用这种方式需要在业务侧做少量改造。压缩后的内容存储方式如下:

a、空字符串就以空字符串存储

b、非空字符串存储方式为前 4 个 bype 保存未压缩的字符串,紧接着保存压缩的字符串

3、方案实践

字段压缩方案涉及到的几个相关的函数如下:

压缩函数

COMPRESS()

解压缩函数

UNCOMPRESS()

字符串长度函数

LENGTH()

未解压字符串长度函数

UNCOMPRESSED_LENGTH()

实践步骤:

a、创建一张测试表

CREATE TABLE IF NOT EXISTS `test`.`test_compress` (

`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',

`content` blob NOT NULL COMMENT '内容列',

PRIMARY KEY (`id`)

 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='压缩测试表';

b、网表中插入压缩的数据

insert into `test`.`test_compress`(content) values(COMPRESS(REPEAT('a',1000)));

c、读取压缩的数据

select UNCOMPRESS(content) from `test`.`test_compress`;

d、查询对应的长度和内容

代码如下:

SELECT UNCOMPRESSED_LENGTH(content) AS length, LENGTH(content) AS compress_length, UNCOMPRESS(content), content FROM `test`.`test_compress`

4、压缩效果

从上面截图可以看出压缩效果比较好,针对 text、char、varchr、blob 等,如果里面重复的数据越多压缩效果就越好。

三、InnoDB 表压缩方案解决方案

1、适用场景

采用压缩表一般都用在由于数据量太大,磁盘空间不足,负载主要体现在 IO 上,而服务器的 CPU 又有比较多的余量的场景。

2、表压缩简介 a、为什么需要压缩

目前很多表都支持压缩,比如 Myisam、InnoDB、TokuDB、MyRocks 。由于使用 InnoDB 主要是不需要做什么改动,对线上完全透明,压缩方案也非常成熟,因此这里只对 InnoDB 做详细说明。对于 TokuDB 和 MyRocks 的压缩方案将在 MySQL 的压缩方案(二)中撰文说明。

在 SSD 没有大量横行的时候,数据库几乎都是 IO 负载型的,在 CPU 有大量余量的时候,磁盘 IO 的瓶颈就已经凸显出来。而数据的大量存储,尤其是日志型数据和监控类型的数据,会导致磁盘空间快速增长。硬盘不够用也会在很多业务中凸显出来。一种比较好的方式就诞生了,那就是通过牺牲少量 CPU 资源,采用压缩来减少磁盘空间占用,以及优化 IO 和带宽。尤其针对读多些少的业务。

SSD 出来后,数据库的 IO 负载有所降低,但是对于磁盘空间的问题还是没有很好的解决。因此压缩表使用还是非常的广泛。这也就是为什么那么多的引擎都支持压缩的原因。而 innodb 在 MySQL 5.5 的时候就支持了压缩功能,只是压缩比比较低,通常在 50%左右。而 tokuDB 能达到 80%左右,MyRocks 的压缩比能达到 70%左右。

注意:压缩比和你存储的数据组成有很大的关系,并不是所有的数据都能达到上面所说的压缩比。如果大部分都是字符串,并且重复的数据比较多,压缩比会很好。

b、innodb 的压缩介绍

使用 innodb 压缩的前提条件是,innodb_file_per_table 这个参数要启用,innodb_file_format 这个参数设置成 Barracuda。

你可以使用 ROW_FORMAT=COMPRESSED 来 create 或者 alter 表来开启 innodb 的压缩功能,如果没有指定 KEY_BLOCK_SIZE 的大小,默认 KEY_BLOCK_SIZE 为 innodb_page_size 大小的一半,也可以通过指定 KEY_BLOCK_SIZE=n 参数来开启 innodb 的压缩功能,n 可以为 1、2、4、8、16,单位是 K。n 的值越小,压缩比越高,消耗的 CPU 资源也越多。注意 32K 或者 64K 的页不支持压缩。启用压缩后,索引数据也同样会被压缩。

你也可以通过调整 innodb_compression_level 来设置压缩的级别,级别从 1~9,默认是 6。级别越低,意味着压缩比越高,同时也意味着需要更多的 CPU 资源。

c、压缩算法

innodb 压缩借助的是著名的 zlib 库,采用 L777 压缩算法,这种算法在减少数据大小和 CPU 利用方面很成熟高效。同时这种算法是无损的,因此原生的未压缩的数据总是能够从压缩文件中重构,LZ777 实现原理是查找重复数据的序列号然后进行压缩,所以数据模式决定了压缩效率,一般而言,用户的数据能够被压缩 50%以上。

d、压缩表在 buffer_pool 中如何处理

在 buffer_pool 缓冲池中,压缩的数据通过 KEY_BLOCK_SIZE 的大小的页来保存,如果要提取压缩的数据或者要更新压缩数据对应的列,则会创建一个未压缩页来解压缩数据,然后在数据更新完成后,会将为压缩页的数据重新写入到压缩页中。内存不足的时候,MySQL 会讲对应的未压缩页踢出去。因此如果你启用了压缩功能,你的 buffer_pool 缓冲池中可能会存在压缩页和未压缩页,也可能只存在压缩页。不过可能仍然需要将你的 buffer_pool 缓冲池调大,以便能同时能保存压缩页和未压缩页。

MySQL 采用最少使用(LRU)算法来确定将哪些页保留在内存中,哪些页剔除出去,因此热数据会更多地保留在内存中。当压缩表被访问的时候,MySQL 使用自适应的 LRU 算法来维持内存中压缩页和非压缩页的平衡。当系统 IO 负载比较高的时候,这种算法倾向于讲未压缩的页剔除,一面腾出更多的空间来存放更多的压缩页。当系统 CPU 负载比较高的时候,MySQL 倾向于将压缩页和未压缩页都剔除出去,这个时候更多的内存用来保留热的数据,从而减少解压的操作。

e、如何评估 KEY_BLOCK_SIZE 是否合适

为了更深入地了解压缩表对性能的影响,在 Information Schema 库中有对应的表可以用来评估内存的使用和压缩率等指标。INNODB_CMP 是收集的是某一类的 KEY_BLOCK_SIZE 压缩表的整体状况的信息,汇总的是所有 KEY_BLOCK_SIZE 压缩表的统计。而 INNODB_CMP_PER_INDEX 表则是收集各个表和索引的压缩情况信息,这些信息对于在某个时间评估某个表的压缩效率或者诊断性能问题很有帮助。INNODB_CMP_PER_INDEX 表的收集会导致系统性能受到影响,必须 innodb_cmp_per_index_enabled 选项才会记录,生产环境最好不要开启。

我们可以通过观察 INNODB_CMP 表的压缩失败情况,如果失败比较多,则需要调大 KEY_BLOCK_SIZE。一般建议 KEY_BLOCK_SIZE 设置为 8。

3、方案实践

a、设置好 innodb_file_per_table 和 innodb_file_format 参数

SET GLOBAL innodb_file_per_table=1;SET GLOBAL innodb_file_format=Barracuda;

b、创建对应的压缩表

代码如下:

CREATE TABLE compress_test (c1 INT PRIMARY KEY,content varchar(255)) ROW_FORMAT=COMPRESSEDKEY_BLOCK_SIZE=8;

如果是已经存在的表,则通过 alter 来修改,SQL 如下:

ALTER TABLE compress_test ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;

4、压缩效果

压缩效果通过线上的一个监控的表修改为压缩后的文件大小来说明,压缩前后对比如下:

(0)

相关推荐

  • mysql数据库备份命令分享(mysql压缩数据库备份)

    备份MySQL数据库的命令 复制代码 代码如下: mysqldump -hhostname -uusername -ppassword databasename > backupfile.sql 备份MySQL数据库为带删除表的格式 备份MySQL数据库为带删除表的格式,能够让该备份覆盖已有数据库而不需要手动删除原有数据库. 复制代码 代码如下: mysqldump -–add-drop-table -uusername -ppassword databasename > backupfile

  • MySQL 加密/压缩函数

    这些问题可能导致数据值的改变.一般而言,上述问题可能在你使用非二进制串数据类型(如char,varchar,text等数据类型)的情况下发生. AES_ENCRYPT()和AES_DECRYPT() AES_ENCRYPT()和AES_DECRYPT()可以加密/解密使用官方AES算法的数据.该算法使用128位密钥来编码,但用户可以将其扩展到256位.MySQL选用128位密钥,因为这样算法实现更快,而且对大多数用户而言它也足够安全了. AES_ENCRYPT(str,key_str)函数加密一

  • MySQL实现MYISAM表批量压缩的方法

    本文实例讲述了MySQL实现MYISAM表批量压缩的方法.分享给大家供大家参考,具体如下: 关于对MYISAM表的压缩,可以使用myisampack和myisamchk完成(myisampack完之后必须进行myisamchk才能使用压缩后的表,而且是只读的), 其详细地用法可以参考官方文档: http://dev.mysql.com/doc/refman/5.1/zh/client-side-scripts.html. 这两个操作需要谨慎使用,在压缩之前需要确认mysqld已关闭或者要压缩的表

  • MySQL 压缩的使用场景和解决方案

    导语 描述 MySQL 压缩的使用场景和解决方案,包括压缩传输协议.压缩列解决方案和压缩表解决方案. 提到 MySQL 压缩相关的内容,我们能想到的可能是如下几种和压缩相关的场景: 1.客户端和服务器之间传输的数据量太大,需要进行压缩,节约带宽 2.MySQL 某个列的数据量大,只针对某个列的数据压缩 3.MySQL 某个或者某几个表数据太多,需要将表数据压缩存放,减少磁盘空间的占用 这几个问题在 MySQL 侧都有很好的解决方案 ,针对第 1 个问题,可以使用 MySQL 的压缩协议解决:针对

  • MySQL索引失效场景及解决方案

    目录 一.前言 二.最左前缀匹配原则 三.MySQL逻辑架构和优化器 四.索引失效场景以及为何会失效 五.总结 一.前言 在对SQL语句进行索引查询时会遇到索引失效的时候,对于该语句的可行性以及性能效率方面有至关重要的影响,本篇剖析索引为何失效,有哪些情况会导致索引失效以及对于索引失效时的优化解决方案,其中着重介绍最左前缀匹配原则.MySQL逻辑架构和优化器.索引失效场景以及为何会失效. 二.最左前缀匹配原则 之前有写了一篇关于MySQL添加索引特点及优化问题方面的文章,下面将介绍索引失效的相关

  • 详解MySQL双活同步复制四种解决方案

    对于数据实时同步,其核心是需要基于日志来实现,是可以实现准实时的数据同步,基于日志实现不会要求数据库本身在设计和实现中带来任何额外的约束. 基于MySQL原生复制主主同步方案  这是常见的方案,一般来说,中小型规模的时候,采用这种架构是最省事的. 两个节点可以采用简单的双主模式,并且使用专线连接,在master_A节点发生故障后,应用连接快速切换到master_B节点,反之也亦然.有几个需要注意的地方,脑裂的情况,两个节点写入相同数据而引发冲突,同时把两个节点的auto_increment_in

  • 分享15个Mysql索引失效的场景

    目录 背景 数据库及索引准备 创建表结构 初始化数据 数据库版本及执行计划 1 联合索引不满足最左匹配原则 2 使用了select * 3 索引列参与运算 4 索引列参使用了函数 5 错误的Like使用 6 类型隐式转换 7.使用OR操作 8 两列做比较 9 不等于比较 10 is not null 11 not in和not exists 12 order by导致索引失效 13 参数不同导致索引失效 14 其他 小结 背景 无论你是技术大佬,还是刚入行的小白,时不时都会踩到Mysql数据库不

  • MySQL死锁的产生原因以及解决方案

    数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性.加锁是实现数据库并 发控制的一个非常重要的技术.在实际应用中经常会遇到的与锁相关的异常情况,当两个事务需要一组有冲突的锁,而不能将事务继续下去的话,就会出现死锁,严 重影响应用的正常执行. 在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Lock

  • node连接mysql数据库遇到的问题和解决方案

    今天安装了新版的MySQL(8.0.21),用node框架连接数据库的时候报了个错: Client does not support authentication protocol requested by server; consider upgrading MySQL client 原因在于MySQL8.0换了加密插件,而数据库管理客户端还是原来旧版本的加密方式,主要在于安装MySQL8.0过程中的Authentication Method这一配置过程,有了不同的加密方式,所以如果选择强加密

  • MySQL触发器的使用场景及方法实例

    触发器: 触发器的使用场景以及相应版本: 触发器可以使用的MySQL版本: 版本:MySQL5以上 使用场景例子: 每当增加一个顾客到某个数据库表时,都检查其电话号码格式是否正确,州的缩写是否为大写 每当订购一个产品时,都从库存数量中减去订购的数量 无论何时删除一行,都在某个存档表中保留一个副本 即:在某个表发生更改时自动处理. 如遇到触发器报错"Not allowed to return a result set from a trigger":请划到最后看详解: 触发器的使用: 创

  • vue.js watch经常失效的场景与解决方案

    使用过watch的,应该起码经历过一次失效. 比如,我们监听对象的时候,在没有踩坑之前,很容易这样写: 但是,❌,这里的watch是无效的!!! 因为 obj 是引用类型!!! 引用类型的指针是固定的,所以如果不是重新赋值,那么其赋值的变量自然也不会发生变化. 举个例子: let obj = { a: 1 }; let obj1 = obj; let obj2 = { ...obj }; obj1.a = 2; obj2.a = 3; // 这里肯定是true,因为obj和obj1都是同一个指针

  • MySQL 1130异常,无法远程登录解决方案详解

    目录 问题: 一.开启远程登录权限: 二.刷新MySQL权限: 三.测试: 问题: mysql:1130 is not allowed to connect to this MariaDB server(没有远程登录权限,注:这里的MariaDB 是MySQL的延伸版) 一.开启远程登录权限: 将localhost用户数据复制一份,并将localhost更名为%(即,所有的链接都可以登录服务器),修改成服务器对应IP地址亦可,步骤如图: 二.刷新MySQL权限: 具体sql如下: mysql>

  • MySQL在Windows中net start mysql 启动MySQL服务报错 发生系统错误解决方案

    目录 1-错误详情 2-单次解决方法 2.1-目录 C:\Windows\System32 -> 找到 cmd.exe 2.2-右击属性 -> 以管理员身份运行 2.3-运行成功 3-永久解决方法 3.1-在 2.2 中:右击属性 -> 发送到(N) -> 桌面快捷方式 3.2-右击属性 -> 快捷方式 -> 高级 -> 以管理员身份运行 -> 确定 3.3-运行成功: 4-备注 4.1-Windows 服务名不区分大小写 4.2-两条语句含义 1-错误详情

随机推荐