静暖沐阳 發表於 2025-6-24 17:15:00

在 OceanBase 中,如何应对存储引擎的读放大问题?

<blockquote>
<p>首先为大家推荐这个 OceanBase 开源负责人老纪的公众号 “老纪的技术唠嗑局”,会持续更新和 #<strong>数据库</strong>、#<strong>AI</strong>、<strong>#技术架构</strong> 相关的各种技术内容。欢迎感兴趣的朋友们关注!</p>
</blockquote>
<h1 id="前言">前言</h1>
<p>OceanBase 的 LSM-Tree 存储引擎天生具有高效的写入性能,而且既能够通过旁路导入高效处理定期的批量数据同步,又能够承载一些实时数据同步和历史库数据修改的场景。</p>
<p>但任何事物都有两面性,LSM-Tree 存储引擎虽然对写操作友好,但在数据读取时,必然绕不开多级数据归并带来的读放大问题。</p>
<p>最近一段时间,经常在社区论坛和微信群中出现:<strong>在高频插入、删除和更新的表中,读取性能不符合预期的问题。</strong> 这篇文章就简单为大家介绍一下应对方法。</p>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2025/png/132317/1746259692851-b2cd4f73-6819-48cf-a2b0-7914a081a45c.png"></p>
<h1 id="背景oceanbase-的存储引擎架构">背景:OceanBase 的存储引擎架构</h1>
<p>OceanBase 的存储引擎基于 LSM-Tree 架构,将数据分为基线数据(放在基线 SSTable 中)和增量数据(放在 MemTable / 转储的 SSTable 中)两部分。其中基线数据是只读的,一旦生成就不再被修改;增量数据支持读写。</p>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2025/png/132317/1746256607610-b5f32806-6bf1-4423-a047-d833d5f9793b.png"></p>
<p>OceanBase 数据库的 DML 操作插入、更新、删除等操作,首先写入内存里的 MemTable,<strong>所以在写入性能上就相当于内存数据库的写入性能</strong>。等到 MemTable 达到一定大小时转储到磁盘成为增量的 SSTable(上图中的转储 SSTable 部分),转储到磁盘上的过程是批量的顺序写,相比 B+ 树架构离散的随机写,还会大大提高写盘的性能。</p>
<p>当增量的 SSTable 达到一定规模的时候,会触发增量数据和基线数据的合并,把增量数据和基线数据做一次整合,基线数据在合并完成之后就不会发生变化了,直到下一次合并。同时每天凌晨的业务低峰期,系统也会自动进行每日合并。</p>
<p><strong>但是 LSM-Tree 的架构也存在一个问题,就是读放大</strong>(上图中的右侧箭头向上的部分)。在进行查询时,需要分别对 SSTable 和 MemTable 进行查询,并将查询结果进行一次归并,然后再将归并后的查询结果返回 SQL 层。OceanBase 为了减小读放大带来的影响,在内存实现了多级的缓存,例如 Block Cache 和 Row cache,来避免对基线数据频繁的进行随机读。</p>
<h1 id="问题buffer-表效应">问题:Buffer 表效应</h1>
<p>当用户在某张表上频繁的执行插入并且同时进行批量删除,或者有大量的并发更新操作时,可能会遇到一种现象:表中的数据行数并不大,但是查询和更新的性能出现明显下降。这种现象在 OceanBase 中称为 Queuing 表(业务上有时又称 Buffer 表)效应。</p>
<p>Buffer 表是 LSM-Tree 架构数据库都要面对的一类问题。LSM-Tree 架构下的删除操作,在合并之前都只是逻辑上标记删除而非物理删除,当增量数据中存在大量标记删除的数据时,物理行数量将远多于逻辑行数,从而会严重加剧存储引擎的读放大现象,并影响优化器对最优计划的选择。</p>
<h1 id="分析通过-explain-extended_noaddr">分析:通过 explain extended_noaddr</h1>
<p>分析方法很简单,用 EXPLAIN EXTENDED_NOADDR 命令对 SQL 进行详细的计划展示即可(通常用户在排查 SQL 性能问题时,会使用这种展示模式)。需要重点关注 physical_range_rows 和 logical_range_rows 的值。</p>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2025/png/132317/1746258341259-4c1b3661-0366-4d71-9813-e10f656cb98d.png"></p>
<p>上图中,physical_range_rows 和 logical_range_rows 分别表示 t1 表需要扫描的物理行数和逻辑行数。如果走了索引的话,含义为 t1 表在索引上需要扫描的物理行数和逻辑行数。</p>
<p>一般来说,physical_range_rows 和 logical_range_rows 这两个指标是相近的,看任意一个都可以。仅在这个特殊的 Buffer 表(频繁更新)场景下,physical_range_rows 可能会远大于 logical_range_rows,所以可以通过 physical_range_rows 和 logical_range_rows 的数值来判断是否出现 Buffer 表问题。</p>
<p>例如我向 t1 表插入 33 行数据,立即删除 29 行,那在增量数据里就会有 33 个物理行,其中 29 个物理行有 Delete 标记,查询真正的 4 个逻辑行时,就会出现大量无效扫描动作。</p>
<h1 id="应对buffer-表自适应合并优化">应对:Buffer 表自适应合并优化</h1>
<p>为了更灵活地解决 Buffer 带来的性能下降问题,从 OceanBase 4.2.3 开始,为大家带来了 <strong>Buffer 表自适应合并优化特性</strong>。</p>
<p>该特性允许用户根据业务场景为每张表设置不同的表级配置项 <code>table_mode</code> 以指定不同的快速冻结与自适应合并策略,以应对 Buffer 表引起的读放大现象,从而提高系统长期运行下的 QPS 等性能指标。</p>
<p>针对 Buffer 表问题,OceanBase 提供了 5 种档位的 Table Mode 支持。不同的 Table Mode 对应不同的统计信息阈值与合并策略。存储层每次转储时都会根据转储的统计信息及 Table Mode 对应的阈值判断是否需要执行一次针对 Buffer 表场景的特殊合并以消除增量数据里的所有 Delete 标记,从而避免原有的大量无效扫描动作。</p>
<table>
<thead>
<tr>
<th><strong>Table Mode</strong></th>
<th><strong>转储后触发合并阈值</strong></th>
<th><strong>转储后触发合并概率</strong></th>
<th><strong>转储后合并类型</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Normal(默认值)</td>
<td>较高(为基准1.0 )</td>
<td>极低</td>
<td>仅做 Medium Compaction</td>
</tr>
<tr>
<td>Queuing</td>
<td>高(0.9)</td>
<td>低</td>
<td></td>
</tr>
<tr>
<td>Moderate</td>
<td>中(0.8)</td>
<td>中</td>
<td>优先做 Medium Compaction,   若 Medium 冷却中,做 Meta</td>
</tr>
<tr>
<td>Super</td>
<td>低(0.6)</td>
<td>高</td>
<td></td>
</tr>
<tr>
<td>Extreme</td>
<td>极低(0.5)</td>
<td>较高</td>
<td>仅做 Meta Compaction</td>
</tr>
</tbody>
</table>
<blockquote>
<p>备注:</p>
<p>合并是 OceanBase 数据库将动静态数据做归并的行为,能有效消除增量数据</p>
<ul>
<li>Medium Compaction 是一类触发链路较长,需要保证多副本上 Major 一致性的合并,合并<strong>较慢</strong>;</li>
<li>Meta Compaction 是由各个 Observer 主动发起,生成只读 Meta SSTable 的合并,合并<strong>较快</strong>;</li>
</ul>
</blockquote>
<p>简单来说,默认场景(Normal)下转储后发起合并的门槛比较高(例如认为转储中删除行超过 30 万才判断可能是 Buffer 表场景),因此转储后触发合并概率较低,触发的也是合并速度较慢的 Medium Compaction。</p>
<p>而随着 Table Mode 的逐步上调,转储后发起合并的门槛逐渐降低,触发合并的概率也逐渐提高,并且更倾向于做合并速度较快的 Meta Compaction,从而能及时通过合并消除增量数据避免 Buffer 表带来的大量无效扫描。</p>
<p>具体来说,不同参数下的策略可以分为如下三个模块:</p>
<h2 id="触发快速冻结">触发快速冻结</h2>
<p>对创建时间 &gt; 存活时间阈值,且 memtable 有热点行(不在本次优化考虑内)或墓碑现象的触发快速冻结:</p>
<table>
<thead>
<tr>
<th>Table Mode</th>
<th>存活时间阈值</th>
<th>墓碑现象阈值</th>
</tr>
</thead>
<tbody>
<tr>
<td>Normal(默认)</td>
<td>120s = 120s * 1.0 <br></td>
<td>(更新行 + 删除行)&gt; 25w = 25w * 1.0</td>
</tr>
<tr>
<td>Queuing</td>
<td>108s = 120s * 0.9</td>
<td>(更新行 + 删除行)&gt; 22.5w = 25w * 0.9</td>
</tr>
<tr>
<td>Moderate</td>
<td>96s = 120s * 0.8</td>
<td>(更新行 + 删除行)&gt; 20w = 25w * 0.8</td>
</tr>
<tr>
<td>Super</td>
<td>72s = 120s * 0.6</td>
<td>(更新行 + 删除行)&gt; 15w = 25w * 0.6</td>
</tr>
<tr>
<td>Extreme</td>
<td>60s = 120s * 0.5</td>
<td>(更新行 + 删除行)&gt; 12.5w = 25w * 0.5</td>
</tr>
</tbody>
</table>
<blockquote>
<p>备注:</p>
<p>存活时间阈值的 120s 为 4.2 的阈值。在 4.3 及以上版本,此阈值为 300s。</p>
</blockquote>
<h2 id="转储时收集统计信息并尝试触发分区合并">转储时收集统计信息并尝试触发分区合并</h2>
<p>冻结后紧接着会做一次转储,若转储同时满足如下条件,将会进行 tablet 级别的信息汇报:</p>
<ul>
<li>dml 数量 &gt; 1000</li>
<li>insert + update + delete 行数 &gt; 1000</li>
</ul>
<p>当然, tablet 信息的汇报并不止于转储,一些慢查询场景也会做相应处理,只是不在本任务范围内。</p>
<p>当 memtable 超过预设内存阈值或快速冻结触发后都会执行一次转储。如果触发了快速冻结,意味着墓碑现象可能已经出现了,因此根据上述快速冻结策略的墓碑策略再判断一次,如果命中,直接发起一次合并。</p>
<h2 id="分区合并调度时调度分区合并">分区合并调度时调度分区合并</h2>
<p>分区合并的调度由一个独立的调度线程负责,基本逻辑是通过自适应合并的策略来判断某个 tablet 是否需要执行一次分区合并。相应地,也会根据不同的 table mode 调整自适应策略。</p>
<p>与 table mode 有关的自适应合并策略如下:</p>
<blockquote>
<p>备注:</p>
<p>这里引用一下 OB 官网的 自适应合并 内容:</p>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2024/png/105956359/1711008481869-c37ed0b1-a3c0-42af-8ca9-5acbeb28d716.png"></p>
<p>其中第三点:根据统计增量数据的总行数这个并不受 table mode 控制,增量行数的阈值是超过 10w 或者增量行达到极限 70%,对于非 Normal 表,大概率已经触发合并了。</p>
</blockquote>
<h3 id="导数场景">导数场景</h3>
<table>
<thead>
<tr>
<th>Table Mode</th>
<th>导数场景条件(需同时满足)<br>根据统计的 10 分钟内信息</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>热点 tablet</td>
<td>插入比例 &gt;= 70%<br>且操作行数满足   (操作行数 = insert + delete + update)</td>
</tr>
<tr>
<td>Normal(默认)</td>
<td>查询次数 + 转储次数 &gt; 5 = 5 * 1.0</td>
<td>操作行数 &gt;= 1w = 1w * 1.0</td>
</tr>
<tr>
<td>Queuing</td>
<td>查询次数 + 转储次数 &gt; 4.5 = 5 * 0.9</td>
<td>操作行数 &gt;= 9k = 1w * 0.9</td>
</tr>
<tr>
<td>Moderate</td>
<td>查询次数 + 转储次数 &gt; 4 =5 * 0.8</td>
<td>操作行数 &gt;= 8k= 1w * 0.8</td>
</tr>
<tr>
<td>Super</td>
<td>查询次数 + 转储次数 &gt; 3 = 5 * 0.6</td>
<td>操作行数 &gt;= 6k = 1w * 0.6</td>
</tr>
<tr>
<td>Extreme</td>
<td>查询次数 + 转储次数 &gt; 2.5 = 5 * 0.5</td>
<td>操作行数 &gt;= 5k = 1w * 0.5</td>
</tr>
</tbody>
</table>
<h3 id="墓碑场景">墓碑场景</h3>
<table>
<thead>
<tr>
<th>Table Mode</th>
<th>墓碑场景条件(需同时满足)<br> 当转储次数 ≥ 2 <br> 且根据统计的 10 分钟内信息<br></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>操作行数 = insert + delete + update</td>
<td>更新删除行数比例</td>
</tr>
<tr>
<td>Normal(默认)</td>
<td>操作行数 &gt; 1w = 1w * 1.0</td>
<td>更新删除比例 &gt;= 30% = 30% * 1.0</td>
</tr>
<tr>
<td>Queuing</td>
<td>操作行数 &gt; 9k = 1w * 0.9</td>
<td>更新删除比例 &gt;= 27% = 30% * 0.9</td>
</tr>
<tr>
<td>Moderate</td>
<td>操作行数 &gt; 8k= 1w * 0.8</td>
<td>更新删除比例 &gt;= 24% = 30% * 0.8</td>
</tr>
<tr>
<td>Super</td>
<td>操作行数 &gt; 6k = 1w * 0.6</td>
<td>更新删除比例 &gt;= 18% = 30% * 0.6</td>
</tr>
<tr>
<td>Extreme</td>
<td>操作行数 &gt; 5k = 1w * 0.5</td>
<td>更新删除比例 &gt;= 15% = 30% * 0.5</td>
</tr>
</tbody>
</table>
<p>与此同时,为了防止 10 分钟内的信息不够全面,统计了 tablet 自上次 medium/meta 时的删除行总数,并有满足如下条件时触发一次合并。</p>
<table>
<thead>
<tr>
<th>Table Mode</th>
<th>总删除行数</th>
</tr>
</thead>
<tbody>
<tr>
<td>Normal(默认)</td>
<td>总删除行 &gt; 30w</td>
</tr>
<tr>
<td>Queuing</td>
<td>总删除行 &gt; 20w</td>
</tr>
<tr>
<td>Moderate</td>
<td>总删除行 &gt; 10w</td>
</tr>
<tr>
<td>Super</td>
<td>总删除行 &gt; 5w</td>
</tr>
<tr>
<td>Extreme</td>
<td>总删除行 &gt; 1k</td>
</tr>
</tbody>
</table>
<p>但是值得一提的是, 总删除行这个条件属于锦上添花类型,类似于 cache,很有可能未满足条件时 tablet 统计信息就被别的 tablet 刷掉了。同时,按照最宽松的 10 分钟内 1w 的条件,如果每个 10 分钟内信息都是 9999 行,要达到 30w 的阈值也得近 300 分钟,作用是更多是在 tablet 数量少的时候留下来兜底。</p>
<h3 id="低效读场景">低效读场景</h3>
<table>
<thead>
<tr>
<th>Table Mode</th>
<th>低效读场景条件(需同时满足)<br>根据统计的 10 分钟内信息</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>热点 tablet</td>
<td>慢查询比例(满足其一即可)</td>
</tr>
<tr>
<td>Normal(默认)</td>
<td>查询次数 + 转储次数 &gt; 5 = 5 * 1.0</td>
<td>+ 扫描物理行数 &gt;= 1000 = 1000 * 1.0<br>+ 扫描微块数 &gt;= 10 = 10 * 1.0<br>+ rowkey exists 数 &gt;= 5 = 5 * 1.0</td>
</tr>
<tr>
<td>Queuing</td>
<td>查询次数 + 转储次数 &gt; 4.5 = 5 * 0.9</td>
<td>+ 扫描物理行数 &gt;= 900 = 1000 * 0.9<br>+ 扫描微块数 &gt;= 9 = 10 * 0.9<br>+ rowkey exists 数 &gt;= 4.5 = 5 * 0.9</td>
</tr>
<tr>
<td>Moderate</td>
<td>查询次数 + 转储次数 &gt; 4 =5 * 0.8</td>
<td>+ 扫描物理行数 &gt;= 800 = 1000 * 0.8<br>+ 扫描微块数 &gt;= 8 = 10 * 0.8<br>+ rowkey exists 数 &gt;= 4 = 5 * 0.8</td>
</tr>
<tr>
<td>Super</td>
<td>查询次数 + 转储次数 &gt; 3 = 5 * 0.6</td>
<td>+ 扫描物理行数 &gt;= 600 = 1000 * 0.6<br>+ 扫描微块数 &gt;= 6 = 10 * 0.6<br>+ rowkey exists 数 &gt;= 3 = 5 * 0.6</td>
</tr>
<tr>
<td>Extreme</td>
<td>查询次数 + 转储次数 &gt; 2.5 = 5 * 0.5</td>
<td>+ 扫描物理行数 &gt;= 500 = 1000 * 0.5<br>+ 扫描微块数 &gt;= 5 = 10 * 0.5<br>+ rowkey exists 数 &gt;= 2.5 = 5 * 0.5</td>
</tr>
</tbody>
</table>
<h1 id="使用指南">使用指南</h1>
<h2 id="创建表时指定-table-mode">创建表时指定 Table Mode</h2>
<p>用户可以在创建表时就显式指定 Table Mode,若不指定则默认为 Normal 模式。</p>
<pre><code class="language-sql"># 语法
create table t1 (c1 int) table_mode = 'normal/queuing/moderate/super/extreme';

# 创建一张 queuing 模式的表
create table t1 (k int, v double) table_mode = 'queuing';

# 创建一张 extreme 模式的表
create table t1 (k int, v double) table_mode = 'extreme';
</code></pre>
<h2 id="修改表的-table-mode">修改表的 Table Mode</h2>
<p>用户可以通过 DDL 语句显式修改 Table Mode</p>
<pre><code class="language-sql"># 语法
alter table t1 set table_mode = 'normal/queuing/Moderate/Super/Extreme';

# 修改表的 table_mode 为 moderate
alter table t1 set table_mode = 'moderate';
</code></pre>
<p>例如,当用户观测到某张表的读放大现象严重,出现 Buffer 表现象时,可以视业务量规模与特点手动修改该表的 Table Mode 到更高档位,如从 normal 调整到queuing,从 queuing 调整到 super 等。当 Table Mode 在存储层生效后,将会通过调度自适应合并的方式发起合并以解决 Buffer 表现象。</p>
<p>与此同时,更激进的 Table Mode 将会更频繁地发起合并,从而消耗更多的计算资源,如果此时业务体量或场景能够容忍一定程度的 Buffer 表效应,也可以手动调低数据表的 Table Mode。</p>
<h2 id="table-mode-生效说明">Table Mode 生效说明</h2>
<p>值得说明的是,尽管 Table Mode 在 Table Schema 中是实时生效的,但是在存储层后台对 tablet 信息的统计是是延迟更新的(2 分钟刷新一次),因此当修改表的 Table Mode 后,平均得经过 60s 才能生效。</p>
<p>另外,存储层后台对每个租户下转储统计信息的覆盖范围是有限的,由于资源有限,并不是所有 tablet 上的转储信息都会被统计到,只有转储时满足某些特定条件的 tablet(写入数据量较大,读放大严重等)才会被纳入统计,Table Mode 对转储/合并行为的影响也仅限于这些被纳入统计的 tablet 上。</p>
<blockquote>
<p>简单来说,Queuing 表的调度依赖于缓冲池内定期从内部表更新的 Table Mode,我们认为满足 buffer 表行为的 tablet 进入缓冲池的概率是极大的,近似于不会被淘汰(除非 Queuing 表的 分区数量过于巨大)。</p>
<p>如果用户想主动让 table 加入缓冲池,就手动执行一次转储即可。</p>
</blockquote>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2024/jpeg/105956359/1711011797213-0e8f7228-e80a-4887-a31f-57b2ade0c9ec.jpeg"></p>
<h1 id="性能说明与表现形式">性能说明与表现形式</h1>
<p>通过构造频繁插入/更新/删除的 workload 对比了不同 Table Mode 下表对查询的性能表现。</p>
<h2 id="基本测试流程">基本测试流程</h2>
<p>首先创建 5 张表,Table Mode 分别为 <code>NORMAL</code>、<code>QUEUING</code>、<code>MODERATE</code>、<code>SUPER</code>、<code>EXTREME</code></p>
<pre><code class="language-sql">CREATE TABLE table_name (k int primary key, v int) table_mode='table_mode'
</code></pre>
<p>之后重复执行如下流程 500 轮,每轮起 5 个线程对 5 张表分别进行采样,等所有线程都 return 后再开始下一轮</p>
<pre><code class="language-sql">-- 1. 第 1 轮采样,记录时间
SELECT COUNT(*) FROM table_name;

-- 2. 随机插入 行测试数据

-- 3. 第 2 轮采样,记录时间
SELECT COUNT(*) FROM table_name;

-- 4. 执行更新语句
UPDATE /*+ parallel(8)*/ table_name SET v = v + 1 WHERE k &gt;= 0;

-- 5. 第 3 轮采样,记录时间
SELECT COUNT(*) FROM table_name;

-- 6. 执行删除语句
DELETE /*+ parallel(8)*/ FROM table_name WHERE k &gt;= 0

-- 7. 第 4 轮采样,记录时间
SELECT COUNT(*) FROM table_name;
</code></pre>
<p>这样一轮可以得到 4 个采样数据点,最后每张表可以得到 2000 个采样点。</p>
<h2 id="测试结果">测试结果</h2>
<table>
<thead>
<tr>
<th>Table Mode</th>
<th>平均查询时间(秒)</th>
<th>相比Normal 提升</th>
</tr>
</thead>
<tbody>
<tr>
<td>NORMAL</td>
<td>0.4750267331600189</td>
<td>0</td>
</tr>
<tr>
<td>QUEUING</td>
<td>0.4220401510000229</td>
<td>-11.5%</td>
</tr>
<tr>
<td>MODERATE</td>
<td>0.3732480742931366</td>
<td>-21.4%</td>
</tr>
<tr>
<td>SUPER</td>
<td>0.2679354891777039</td>
<td>-43.6%</td>
</tr>
<tr>
<td>EXTREME</td>
<td>0.23057958698272704</td>
<td>-51.5%</td>
</tr>
</tbody>
</table>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2024/png/105956359/1705459197452-bb88ab5c-c841-4232-9887-fa834d4f607e.png"></p>
<p>从图中可以比较直观地看出几点:</p>
<ul>
<li>前 375 次左右的采样实际上各个模式的表现是类似的,这是因为设置 table mode 对转储合并的影响需要一段时间内才能传递到存储层:转储线程 2 分钟一轮,且只有转储满足满足一定条件时,tablet 的统计信息才会被更新 tablet 从而影响快速冻结与合并。</li>
<li>激进模式(Extreme)的表现是最好的,当查询时延逐渐增加时,可以及时触发快速冻结及后续合并从而降低 Buffer 表的读放大现象。从图中也可以看到,除了一开始的预热阶段,每次 Extreme 总是及时将波峰降下去,也即及时做了一次合并。</li>
</ul>
<h2 id="不同-table-mode-设置下对性能的直观表现">不同 Table Mode 设置下对性能的直观表现</h2>
<p><img src="https://intranetproxy.alipay.com/skylark/lark/0/2024/png/105956359/1706687745078-8cf6ca66-c67c-4363-aed8-ef927839b1aa.png"></p>
<p>从该图可以直观看出不同 Table Mode 对合并行为以及查询性能的影响。每一次查询时延波峰的下降都代表着一次合并的发起,越高 Table Mode 下的表将会更频繁地发起合并,在图上就体现为合并次数多。同时,由于及时清除增量数据中的 Delete 标记,平均查询时延也更低。</p>
<h1 id="总结">总结</h1>
<p>从上述测试数据可以看出,越高的 Table Mode 能够发起更频繁的合并从而降低查询的时延。但是合并的调度与发起也都会消耗更多的计算资源,因此用户需要根据面对的业务场景、数据量大小以及对 Buffer 表效应的容忍程度选择合适的 Table Mode 以达成整体的平衡。</p>
<p>💌</p>
<blockquote>
<p>老纪的技术唠嗑局&nbsp;不仅希望能持续给大家带来有价值的技术分享,也希望能和大家一起为开源社区贡献力量。如果你对 OceanBase 开源社区认可,点亮一颗小星星 ✨ 吧!你的每一个Star,都是我们努力的动力~💕<br>
https://github.com/oceanbase/oceanbase</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/OBCE666/p/18946557
頁: [1]
查看完整版本: 在 OceanBase 中,如何应对存储引擎的读放大问题?