此文已由作者温正湖授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

这篇文章,源于RDS组内的一次饭后闲聊,两位小伙伴在探讨InnoDB启用压缩后的种种,比如在磁盘上是怎么存放数据的,数据页的大小是多少?怎么知道一个页里面可以写入多少压缩前的数据等等。两年前曾看过InnoDB压缩的文档和代码,现在好多东西都模糊了,趁此机会,又重温了一把,下面通过问答的方式来阐述。

压缩干嘛用呢?直白点,就是为了省空间。假如你的手机是16G版iphone 6s,那么你肯定明白可用的存储空间是多么宝贵。如果能够把10G的数据变成5G甚至2G,而且还能不丢信息,那么你是不是乐翻了,做梦都在笑吧。好了,压缩就是干这事儿了。当然在iphone上压缩这招一般是行不通的,不信你试试。

压缩是百利而无一害的吗?骚年,如果有这种想法,那真是too young too simple了。虽然压缩有可能节省存储空间,但也是有代价的,压缩是要消耗cpu资源的,甚至是过多的cpu资源。目前已知的压缩算法有很多种,不同算法的压缩比和cpu消耗也是不一样,常用的算法中zlib的压缩比较高,但cpu消耗也偏高,早期程序一般采用zlib较多,如InnoDB的压缩等。snappy和quicklz在压缩比和cpu消耗上保存了较好的平衡,这两种算法在新开发的软件中使用更为广泛,包括TokuDB、MongoDB等。当然,如果你选择JPG等已经压缩过的图片进行压缩,那就是no zuo no die了,谁都救不了你。

什么样的数据适合做压缩呢?我们拿MySQL表作为例子,通常情况下表结构中包含字符型数据列如char, varchar, text或blob等时,具有较高的压缩率,而一些二进制数据,如整形或浮点型数据列,或者一些已经压缩的多媒体文档,如jpeg、jpg、png等格式图片及mp4、avi等格式视频,其压缩率都不会好。你可以从一个非压缩表中拷贝数据到一个相同的压缩表中,观察数据大小,来决定是否适合压缩。

假如我的MySQL实例的表中就存在较多字符型数据列,我想启用压缩,怎么整?不急,给你宝典,自己去练。首先你的MySQL版本必须高于5.1,现在都已经进入5.7时代了,这个不成问题。接下来把innodb_file_per_table设置为1,即除了系统表外,其他表都是单独存一个文件,还需要把innodb_file_format设置为Barracuda。然后在建新表或修改现有表的语句中加入row_format=compressed key_block_size=8就可以了。你可以仅加入row_format=compressed,这样key_block_size就取默认值8KB了。你也可以仅加入key_block_size={1/2/4/8/16},也会默认开启压缩。那么,key_block_size到底设置为多大才合理呢,这是一个深奥的问题,官方建议如下:

To determine the best value for KEY_BLOCK_SIZE, typically you create several copies of the same table with different values for this clause, then measure the size of the resulting .ibd files and see how well each performs with a realistic workload。

我的MySQL业务类型会对压缩效益有影响吗?为了尽量避免压缩、解压带来的额外消耗,InnoDB在压缩页中新增了modification log区,通过记录当前的页的修改日志,来避免频繁压缩解压。不同的业务场景,会对压缩效益产生不同的影响,下面几种场景相对适用压缩:如果业务中查询占了绝大部分,只有很少的更新,那么只有较少的页中会出现modification log空间不足,进而需要重组或者重新压缩,这种场景下是个不错的选择;如果应用只是单纯的插入,此时数据的插入按照主键递增的方式组织,即使存在二级索引也基本上不需要频繁重组和重压缩;对于删除操作,由于InnoDB删除行采用打标志位的方式来删除,对记录的删除是通过修改页中没有被压缩的元数据的方式实现,所以效率很高;对于更新操作,如果不是对索引列或者存储在off-page的blob,text,长字符串的列的更新,这种场景下使用压缩也是可以接受的。

以上说了那么多,压缩最终还是通过消耗更多的cpu资源来换取减少IO消耗,最终带来性能的提升,如果应用是IO密集型,而不是cpu密集型,那么可以利用剩余的cpu来提升应用性能。

InnoDB的压缩是怎么实现的呢?其实,截止目前InnoDB已经包括两种压缩模式,一种是这里提到的对记录(包括索引)的压缩。该压缩从MySQL 5.1开始既已存在;另一种是在MySQL 5.7中加入的,是对数据块的压缩,本文不对其做介绍。

这里对记录压缩做简单介绍:InnoDB启用压缩后,btr_page_create会调用page_create_zip从磁盘分配一张空的大小为zip_size(即key_block_size)的压缩页,该页在buffer pool(bp)中驻留,不断接收插入及更新等操作的数据,InnoDB的flush或checkpoint机制将该页数据写回磁盘,在回刷前会调用page_zip_compress进行压缩后,其实也不会每次写入磁盘都会进行压缩,就像之前问答中已经提到的,InnoDB压缩页中开辟有modification log区,压缩页中一段时间内的记录增删改会先写入该区,再通过一定条件触发合并,合并的过程就是将原来的压缩数据解压,再将modification log区的内容合并掉,重新进行压缩。随着数据的不断写入,到了一个时间点,总会出现压缩后的数据大小仍超过了设定的key_block_size,这种场景下会触发压缩页的分裂,此时就需要分配一个新页,将原页部分数据移到新页中,再分别进行压缩。如此,周而复始。

在内存充足的情况下,InnoDB bp中包括压缩页的压缩状态(buf_page_t*)page->zip.data和解压状态(buf_block_t*)page->frame。当内存不足时,InnoDB可能将解压的frame回收,保留压缩的zip.data在bp中。如果一个压缩页在一段时间内没有被使用,压缩格式的zip.data也会被写回到磁盘中,以释放内存。

Innodb使用一个自适应 LRU算法来维持bf内zip.data和frame的平衡,目的是为了避免在cpu繁忙时减少解压页的开销,当cpu富余时避免过多的IO。当系统是IO密集型时,倾向于回收frame,以留下内存给其他从磁盘读入的页。当cpu密集型时,InnoDB选择全部回收zip.data和frame,也就是回收整个压缩页,这样内存可以更多的留给热点数据,并减少内存中需要解压的页。

最后,如何逼格更高得使用InnoDB压缩呢? InnoDB一如既往得友好,information_schema中提供了两组系统表来查看运行时的压缩行为。

系统表innodb_cmp和innodb_cmp_reset用来分析运行中的压缩状态,包括压缩页的压缩/解压次数,所花费时间,压缩成功次数以及压缩页的大小等。其中innodb_cmp保存历史汇总数据,而innodb_cmp_reset则记录的是一个较为实时的统计值,表结构如下:

mysql> show create table innodb_cmp\G

*************************** 1. row ***************************

Table: INNODB_CMP
Create Table: CREATE TEMPORARY TABLE `INNODB_CMP` (
  `page_size` int(5) NOT NULL DEFAULT '0',
  `compress_ops` int(11) NOT NULL DEFAULT '0',
  `compress_ops_ok` int(11) NOT NULL DEFAULT '0',
  `compress_time` int(11) NOT NULL DEFAULT '0',
  `uncompress_ops` int(11) NOT NULL DEFAULT '0',
  `uncompress_time` int(11) NOT NULL DEFAULT '0'
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
 
mysql> show create table innodb_cmp_reset\G

*************************** 1. row ***************************

Table: INNODB_CMP_RESET
Create Table: CREATE TEMPORARY TABLE `INNODB_CMP_RESET` (
  `page_size` int(5) NOT NULL DEFAULT '0',
  `compress_ops` int(11) NOT NULL DEFAULT '0',
  `compress_ops_ok` int(11) NOT NULL DEFAULT '0',
  `compress_time` int(11) NOT NULL DEFAULT '0',
  `uncompress_ops` int(11) NOT NULL DEFAULT '0',
  `uncompress_time` int(11) NOT NULL DEFAULT '0'
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

如果compress_ops_ok/compress_ops的比率很高的话,则表明系统运行的良好,反之则表明InnoDB经常进行reorganize, recompress和split,此时对该表禁用压缩或者选择更大的key_block_size。

InnoDB bp使用伙伴分配系统(buddy allocator)来分配不同大小的内存页用来缓存压缩数据:1KB到16KB(即对应不同的key_block_size),information_schema为此提供了innodb_cmpmem和innodb_cmpmem_reset来记录压缩页使用bp的一些信息,包括:bp为压缩页分配的正在使用的页数,可供使用的空闲页数,伙伴系统重新分配页的次数及时间等,详细表结构如下:

mysql> show create table innodb_cmpmem\G

*************************** 1. row ***************************

Table: INNODB_CMPMEM
Create Table: CREATE TEMPORARY TABLE `INNODB_CMPMEM` (
  `page_size` int(5) NOT NULL DEFAULT '0',
  `buffer_pool_instance` int(11) NOT NULL DEFAULT '0',
  `pages_used` int(11) NOT NULL DEFAULT '0',
  `pages_free` int(11) NOT NULL DEFAULT '0',
  `relocation_ops` bigint(21) NOT NULL DEFAULT '0',
  `relocation_time` int(11) NOT NULL DEFAULT '0'
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
 
mysql> show create table innodb_cmpmem_reset\G

*************************** 1. row ***************************

Table: INNODB_CMPMEM_RESET
Create Table: CREATE TEMPORARY TABLE `INNODB_CMPMEM_RESET` (
  `page_size` int(5) NOT NULL DEFAULT '0',
  `buffer_pool_instance` int(11) NOT NULL DEFAULT '0',
  `pages_used` int(11) NOT NULL DEFAULT '0',
  `pages_free` int(11) NOT NULL DEFAULT '0',
  `relocation_ops` bigint(21) NOT NULL DEFAULT '0',
  `relocation_time` int(11) NOT NULL DEFAULT '0'
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

在使用InnoDB压缩时,周期性观察information_schema中提供的这两组压缩相关的临时表,对于评估压缩的效果和性能非常有参考价值。

网易云免费体验馆,0成本体验20+款云产品!

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 网易考拉Android客户端网络模块设计
【推荐】 白木彰:具有普遍性的设计力
【推荐】 网易有数的搭积木原则阐述

InnoDB记录压缩及使用分析的更多相关文章

  1. InnoDB页压缩技术

    Ⅰ.想起一个报错 1.1 创建表报错 (root@localhost) [(none)]> create tablespace ger_space add datafile 'ger_space ...

  2. MySQL之 InnoDB记录结构(转自掘金小册 MySQL是怎样运行的,版权归作者所有!)

    以下内容来自掘金小册 MySQL 是怎样运行的:从根儿上理解 MySQL 版权归原作者所有! 页是MySQL中磁盘和内存交互的基本单位,也是MySQL是管理存储空间的基本单位. 指定和修改行格式的语法 ...

  3. 在InnoDB,记录在 non-clustered indexes(也被称为secondary indexes) 包含了主键值

    In InnoDB, the records in non-clustered indexes (also called secondary indexes) contain the primary ...

  4. MySQL InnoDB表压缩

    MySQL InnoDB表压缩 文件大小减小(可达50%以上) ==> 查询速度变快(count * 约减少20%以上时间) 如何设置mysql innodb 表的压缩: 第一,mysql的版本 ...

  5. 第五节:从一条记录说起——InnoDB记录结构

    <MySQL 是怎样运行的:从根儿上理解 MySQL>第五节:从一条记录说起——InnoDB记录结构 准备工作 现在只知道客户端发送请求并等待服务器返回结果.    MySQL什么方式来访 ...

  6. (转)innodb 与 myisam 读写性能分析

    前提: mysql在5.0之前,读写性能相差很大,读性能:myisam 很强 mysql在5.0之后,差距不是很大 http://passover.blog.51cto.com/2431658/507 ...

  7. 深入浅出分析MySQL MyISAM与INNODB索引原理、优缺点分析

    本文浅显的分析了MySQL索引的原理及针对主程面试的一些问题,对各种资料进行了分析总结,分享给大家,希望祝大家早上走上属于自己的"成金之路". 学习知识最好的方式是带着问题去研究所 ...

  8. 鸟哥的linux私房菜学习记录之认识与分析登录档

    logwatch分析登录档

  9. mybatis 调用 oracle 存储过程 select into 无记录时NO_DATA_FOUND异常处理分析

    首先根据这篇文章:http://www.cnblogs.com/coolzdp/p/7717332.html 我们知道存储过程中 SELECT * INTO 如果没有记录是不会往下执行的,直接抛出NO ...

随机推荐

  1. flask之instance_path实例路径

    Flask 0.8 introduces instance folders. Flask for a long time made it possible to refer to paths rela ...

  2. AFN 请求数据https

    第一步: 导入afn库 第二步: 在pch中添加 #import <SystemConfiguration/SystemConfiguration.h> #import <Mobil ...

  3. [转载]嵌入式linux下操作GPIO

    本文转自:http://blog.csdn.net/mirkerson/article/details/8464231 在嵌入式设备中对GPIO的操作是最基本的操作.一般的做法是写一个单独驱动程序,网 ...

  4. (转)C# -- 扩展方法的应用(Extension Methods)

    本文转载自:http://blog.csdn.net/zxz414644665/article/details/9793205 当你有下面这样一个需求的时候,扩展方法就会起到作用:在项目中,类A需要添 ...

  5. 经典SQL问题: 行转列,列转行

    情景简介 学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列.数据库grade里面数据如下图,假定每个人姓名都不一样,作为主键.本文以MySQL为基础,其他数据库会有 ...

  6. 使用Spring AMQP开发消费者应用

    前一篇中我们介绍了使用RabbitMQ Java Client访问RabbitMQ的方法.但是使用这种方式访问RabbitMQ,开发者在程序中需要自己管理Connection,Channel对象,Co ...

  7. 第十四届华中科技大学程序设计竞赛决赛同步赛 F Beautiful Land(01背包,背包体积超大时)

    链接:https://www.nowcoder.com/acm/contest/119/F来源:牛客网 Beautiful Land 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 1 ...

  8. Android 4学习(7):用户界面 - 基础

    参考<Professional Android 4 Development> Android UI基本元素 下面这些概念是Android UI设计的基础,深入学习和理解它们是Android ...

  9. 探究QA职能

    测试人员一般是被外界普遍认为是QC,即对产品的质量进行检测,找出质量问题并配合相关人员解决问题,从而管控产品质量,说通俗点就是帮开发找漏洞,给开发擦屁股:如果线上出现bug,就是你没有测试完整,最累的 ...

  10. mysql case

    1.table CREATE TABLE `lee`(`id` INT(10) NOT NULL auto_increment,`name` varchar(20) DEFAULT null,`bir ...