learn.lianglianglee.com/专栏/MySQL实战宝典/06 表压缩:不仅仅是空间压缩.md.html
2022-05-11 18:52:13 +08:00

851 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>06 表压缩:不仅仅是空间压缩.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/MySQL实战宝典/00 开篇词 从业务出发,开启海量 MySQL 架构设计.md">00 开篇词 从业务出发,开启海量 MySQL 架构设计.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/01 数字类型:避免自增踩坑.md">01 数字类型:避免自增踩坑.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/02 字符串类型:不能忽略的 COLLATION.md">02 字符串类型:不能忽略的 COLLATION.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/03 日期类型TIMESTAMP 可能是巨坑.md">03 日期类型TIMESTAMP 可能是巨坑.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/04 非结构存储:用好 JSON 这张牌.md">04 非结构存储:用好 JSON 这张牌.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/05 表结构设计:忘记范式准则.md">05 表结构设计:忘记范式准则.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/MySQL实战宝典/06 表压缩:不仅仅是空间压缩.md">06 表压缩:不仅仅是空间压缩.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/07 表的访问设计:你该选择 SQL 还是 NoSQL.md">07 表的访问设计:你该选择 SQL 还是 NoSQL.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/08 索引:排序的艺术.md">08 索引:排序的艺术.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/09 索引组织表:万物皆索引.md">09 索引组织表:万物皆索引.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/10 组合索引:用好,性能提升 10 倍!.md">10 组合索引:用好,性能提升 10 倍!.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/11 索引出错:请理解 CBO 的工作原理.md">11 索引出错:请理解 CBO 的工作原理.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/12 JOIN 连接:到底能不能写 JOIN.md">12 JOIN 连接:到底能不能写 JOIN.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/13 子查询:放心地使用子查询功能吧!.md">13 子查询:放心地使用子查询功能吧!.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/14 分区表:哪些场景我不建议用分区表?.md">14 分区表:哪些场景我不建议用分区表?.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/15 MySQL 复制:最简单也最容易配置出错.md">15 MySQL 复制:最简单也最容易配置出错.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/16 读写分离设计:复制延迟?其实是你用错了.md">16 读写分离设计:复制延迟?其实是你用错了.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/17 高可用设计:你怎么活用三大架构方案?.md">17 高可用设计:你怎么活用三大架构方案?.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/18 金融级高可用架构:必不可少的数据核对.md">18 金融级高可用架构:必不可少的数据核对.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/19 高可用套件:选择这么多,你该如何选?.md">19 高可用套件:选择这么多,你该如何选?.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/20 InnoDB Cluster改变历史的新产品.md">20 InnoDB Cluster改变历史的新产品.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/21 数据库备份:备份文件也要检查!.md">21 数据库备份:备份文件也要检查!.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/22 分布式数据库架构:彻底理解什么叫分布式数据库.md">22 分布式数据库架构:彻底理解什么叫分布式数据库.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/23 分布式数据库表结构设计:如何正确地将数据分片?.md">23 分布式数据库表结构设计:如何正确地将数据分片?.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/24 分布式数据库索引设计:二级索引、全局索引的最佳设计实践.md">24 分布式数据库索引设计:二级索引、全局索引的最佳设计实践.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/25 分布式数据库架构选型:分库分表 or 中间件 .md">25 分布式数据库架构选型:分库分表 or 中间件 .md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/26 分布式设计之禅:全链路的条带化设计.md">26 分布式设计之禅:全链路的条带化设计.md.html</a>
</li>
<li>
<a href="/专栏/MySQL实战宝典/27 分布式事务:我们到底要不要使用 2PC.md">27 分布式事务:我们到底要不要使用 2PC.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>06 表压缩:不仅仅是空间压缩</h1>
<p>前面几讲,我们从最早的各种列类型的选择,过渡到表结构的设计,相信学完前面几讲,你已经能够较好地设计出各种业务表,比如用户表、订单表。既然我们已经掌握了表的逻辑设计,那这一讲就继续学习不同业务表的物理存储设计。</p>
<p>据我观察,很多同学不会在表结构设计之初就考虑存储的设计,只有当业务发展到一定规模才会意识到问题的严重性。而物理存储主要是考虑是否要启用表的压缩功能,默认情况下,所有表都是非压缩的。</p>
<p>但一些同学一听到压缩,总会下意识地认为压缩会导致 MySQL 数据库的性能下降。<strong>这个观点说对也不对,需要根据不同场景进行区分。</strong> 这一讲,我们就来看一看表的物理存储设计:不同场景下,表压缩功能的使用。</p>
<h3>表压缩</h3>
<p>数据库中的表是由一行行记录rows所组成每行记录被存储在一个页中在 MySQL 中,一个页的大小默认为 16K一个个页又组成了每张表的表空间。</p>
<p>通常我们认为,<strong>如果一个页中存放的记录数越多,数据库的性能越高</strong>。这是因为数据库表空间中的页是存放在磁盘上MySQL 数据库先要将磁盘中的页读取到内存缓冲池,然后以页为单位来读取和管理记录。</p>
<p>一个页中存放的记录越多,内存中能存放的记录数也就越多,那么存取效率也就越高。若想将一个页中存放的记录数变多,可以启用压缩功能。此外,启用压缩后,存储空间占用也变小了,同样单位的存储能存放的数据也变多了。</p>
<p>若要启用压缩技术,数据库可以根据记录、页、表空间进行压缩,不过在实际工程中,我们普遍使用页压缩技术,<strong>这是为什么呢?</strong></p>
<ul>
<li><strong>压缩每条记录:</strong> 因为每次读写都要压缩和解压,过于依赖 CPU 的计算能力,性能会明显下降;另外,因为单条记录大小不会特别大,一般小于 1K压缩效率也并不会特别好。</li>
<li><strong>压缩表空间:</strong> 压缩效率非常不错,但要求表空间文件静态不增长,这对基于磁盘的关系型数据库来说,很难实现。</li>
</ul>
<p>而基于页的压缩,既能提升压缩效率,又能在性能之间取得一种平衡。</p>
<p>可能很多同学认为,启用表的页压缩功能后,性能有明显损失,因为压缩需要有额外的开销。的确,压缩需要消耗额外的 CPU 指令但是压缩并不意味着性能下降或许能额外提升性能因为大部分的数据库业务系统CPU 的处理能力是剩余的,而 I/O 负载才是数据库主要瓶颈。</p>
<p>借助页压缩技术MySQL 可以把一个 16K 的页压缩为 8K甚至 4K这样在从磁盘写入或读取时就能将 I/O 请求大小减半,甚至更小,从而提升数据库的整体性能。</p>
<p>当然,压缩是一种平衡,并非一定能提升数据库的性能。这种性能“平衡”取决于解压缩开销带来的收益和解压缩带来的开销之间的一种权衡。但无论如何,压缩都可以有效整理数据原本的容量,对存储空间来说,压缩的收益是巨大的。</p>
<h3>MySQL 压缩表设计</h3>
<h4>COMPRESS 页压缩</h4>
<p>COMPRESS 页压缩是 MySQL 5.7 版本之前提供的页压缩功能。只要在创建表时指定ROW_FORMAT=COMPRESS并设置通过选项 KEY_BLOCK_SIZE 设置压缩的比例。</p>
<p><strong>需要牢记的是,</strong> 虽然是通过选项 ROW_FORMAT 启用压缩功能,但这并不是记录级压缩,依然是根据页的维度进行压缩。</p>
<p>下面这是一张日志表ROW_FROMAT 设置为 COMPRESS表示启用 COMPRESS 页压缩功能KEY_BLOCK_SIZE 设置为 8表示将一个 16K 的页压缩为 8K。</p>
<pre><code>CREATE TABLE Log (
logId BINARY(16) PRIMARY KEY,
......
)
ROW_FORMAT=COMPRESSED
KEY_BLOCK_SIZE=8
</code></pre>
<p>COMPRESS 页压缩就是将一个页压缩到指定大小。如 16K 的页压缩到 8K若一个 16K 的页无法压缩到 8K则会产生 2 个压缩后的 8K 页,具体如下图所示:</p>
<p><img src="assets/CioPOWCbdc-AOD9KAAGgLxfA42Y169.png" alt="图片3.png" /></p>
<p>COMPRESS 页压缩</p>
<p>总的来说COMPRESS 页压缩,适合用于一些对性能不敏感的业务表,例如日志表、监控表、告警表等,压缩比例通常能达到 50% 左右。</p>
<p>虽然 COMPRESS 压缩可以有效减小存储空间,但 COMPRESS 页压缩的实现对性能的开销是巨大的,性能会有明显退化。主要原因是一个压缩页在内存缓冲池中,存在压缩和解压两个页。</p>
<p><img src="assets/CioPOWCbddiAAGQVAAI_A4jlbKM322.png" alt="图片4.png" /></p>
<p>1 个 COMPRESS 压缩页在内存中存在 2 个页版本</p>
<p>如图所示Page1 和 Page2 都是压缩页 8K但是在内存中还有其解压后的 16K 页。这样设计的原因是 8K 的页用于后续页的更新16K 的页用于读取,这样读取就不用每次做解压操作了。</p>
<p>很明显这样的实现会增加对内存的开销会导致缓存池能存放的有效数据变少MySQL 数据库的性能自然出现明显退化。</p>
<p>为了 解决压缩性能下降的问题从MySQL 5.7 版本开始推出了 TPC 压缩功能。</p>
<h4>TPC 压缩</h4>
<p>TPCTransparent Page Compression是 5.7 版本推出的一种新的页压缩功能其利用文件系统的空洞Punch Hole特性进行压缩。可以使用下面的命令创建 TPC 压缩表:</p>
<pre><code>CREATE TABLE Transaction
transactionId BINARY(16) PRIMARY KEY,
.....
)
COMPRESSION=ZLIB | LZ4 | NONE;
</code></pre>
<p>要使用 TPC 压缩,首先要确认当前的操作系统是否支持空洞特性。通常来说,当前常见的 Linux 操作系统都已支持空洞特性。</p>
<p>由于空洞是文件系统的一个特性,利用空洞压缩只能压缩到文件系统的最小单位 4K且其页压缩是 4K 对齐的。比如一个 16K 的页,压缩后为 7K则实际占用空间 8K压缩后为 3K则实际占用空间是 4K若压缩后是 13K则占用空间依然为 16K。</p>
<p>TPC 压缩的具体实现如下所示:</p>
<p><img src="assets/Cgp9HWCbdf6AHyKzAAEPeKy_lro447.png" alt="图片5.png" /></p>
<p>TPC 页压缩</p>
<p>上图可以看到,一个 16K 的页压缩后是 8K接着数据库会对这 16K 的页剩余的 8K 填充0x00这样当这个 16K 的页写入到磁盘时,利用文件系统空洞特性,则实际将仅占用 8K 的物理存储空间。</p>
<p>空洞压缩的另一个好处是,它对数据库性能的侵入几乎是无影响的(小于 20%),甚至可能还能有性能的提升。</p>
<p>这是因为不同于 COMPRESS 页压缩TPC 压缩在内存中只有一个 16K 的解压缩后的页,对于缓冲池没有额外的存储开销。</p>
<p>另一方面,所有页的读写操作都和非压缩页一样,没有开销,只有当这个页需要刷新到磁盘时,才会触发页压缩功能一次。但由于一个 16K 的页被压缩为了 8K 或 4K其实写入性能会得到一定的提升。</p>
<p><img src="assets/CioPOWCbdTKAMg8_AAFXxolLeJA293.png" alt="图片1.png" /></p>
<p>官方 TPC 测试对比</p>
<p>上图是 MySQL 官方的 LinkBench 测试结果,可以看到,无压缩的测试结果为 13,432 QPS传统的 COMPRESS 页压缩性能下降为 10,480 QPS差不多30%的性能下降。基于TPC压缩的测试结果为 18,882在未压缩的基础上还能有额外 40% 的性能提升。</p>
<h3>表压缩在业务上的使用</h3>
<p>总的来说,对一些对性能不敏感的业务表,例如日志表、监控表、告警表等,它们只对存储空间有要求,因此可以使用 COMPRESS 页压缩功能。</p>
<p>在一些较为核心的流水业务表上,我更推荐使用 TPC压缩。因为流水信息是一种非常核心的数据存储业务通常伴随核心业务。如一笔电商交易用户扣钱、下单、记流水这就是一个核心业务的微模型。</p>
<p>所以,用户对流水表有性能需求。此外,流水又非常大,启用压缩功能可更为有效地存储数据。</p>
<p>若对压缩产生的性能抖动有所担心,<strong>我的建议</strong>:由于流水表通常是按月或天进行存储,对当前正在使用的流水表不要启用 TPC 功能,对已经成为历史的流水表启用 TPC 压缩功能,如下所示:</p>
<p><img src="assets/Cgp9HWCbdRyAO8QQAAIDb8I7ubs097.png" alt="图片2.png" /></p>
<p>流水表的设计</p>
<p><strong>需要特别注意的是:</strong> 通过命令 ALTER TABLE xxx COMPRESSION = ZLIB 可以启用 TPC 页压缩功能但是这只对后续新增的数据会进行压缩对于原有的数据则不进行压缩。所以上述ALTER TABLE 操作只是修改元数据,瞬间就能完成。</p>
<p>若想要对整个表进行压缩,需要执行 OPTIMIZE TABLE 命令:</p>
<pre><code>ALTER TABLE Transaction202102 COMPRESSION=ZLIB
OPTIMIZE TABLE Transaction202102;
</code></pre>
<h3>总结</h3>
<p>在进行表结构设计时,除了进行列的选择外,还需要考虑存储的设计,特别是对于表的压缩功能的设计,总结来说:</p>
<ul>
<li>MySQL 中的压缩都是基于页的压缩;</li>
<li>COMPRESS 页压缩适合用于性能要求不高的业务表,如日志、监控、告警表等;</li>
<li>COMPRESS 页压缩内存缓冲池存在压缩和解压的两个页,会严重影响性能;</li>
<li>对存储有压缩需求,又希望性能不要有明显退化,推荐使用 TPC 压缩;</li>
<li>通过 ALTER TABLE 启用 TPC 压缩后,还需要执行命令 OPTIMIZE TABLE 才能立即完成空间的压缩。</li>
</ul>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/MySQL实战宝典/05 表结构设计:忘记范式准则.md">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/MySQL实战宝典/07 表的访问设计:你该选择 SQL 还是 NoSQL.md">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997306b8da3d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>