在 OceanBase 中,如何应对存储引擎的读放大问题?
首先为大家推荐这个 OceanBase 开源负责人老纪的公众号 “老纪的技术唠嗑局”,会持续更新和 #数据库、#AI、#技术架构 相关的各种技术内容。欢迎感兴趣的朋友们关注!前言
OceanBase 的 LSM-Tree 存储引擎天生具有高效的写入性能,而且既能够通过旁路导入高效处理定期的批量数据同步,又能够承载一些实时数据同步和历史库数据修改的场景。
但任何事物都有两面性,LSM-Tree 存储引擎虽然对写操作友好,但在数据读取时,必然绕不开多级数据归并带来的读放大问题。
最近一段时间,经常在社区论坛和微信群中出现:在高频插入、删除和更新的表中,读取性能不符合预期的问题。 这篇文章就简单为大家介绍一下应对方法。
背景:OceanBase 的存储引擎架构
OceanBase 的存储引擎基于 LSM-Tree 架构,将数据分为基线数据(放在基线 SSTable 中)和增量数据(放在 MemTable / 转储的 SSTable 中)两部分。其中基线数据是只读的,一旦生成就不再被修改;增量数据支持读写。
OceanBase 数据库的 DML 操作插入、更新、删除等操作,首先写入内存里的 MemTable,所以在写入性能上就相当于内存数据库的写入性能。等到 MemTable 达到一定大小时转储到磁盘成为增量的 SSTable(上图中的转储 SSTable 部分),转储到磁盘上的过程是批量的顺序写,相比 B+ 树架构离散的随机写,还会大大提高写盘的性能。
当增量的 SSTable 达到一定规模的时候,会触发增量数据和基线数据的合并,把增量数据和基线数据做一次整合,基线数据在合并完成之后就不会发生变化了,直到下一次合并。同时每天凌晨的业务低峰期,系统也会自动进行每日合并。
但是 LSM-Tree 的架构也存在一个问题,就是读放大(上图中的右侧箭头向上的部分)。在进行查询时,需要分别对 SSTable 和 MemTable 进行查询,并将查询结果进行一次归并,然后再将归并后的查询结果返回 SQL 层。OceanBase 为了减小读放大带来的影响,在内存实现了多级的缓存,例如 Block Cache 和 Row cache,来避免对基线数据频繁的进行随机读。
问题:Buffer 表效应
当用户在某张表上频繁的执行插入并且同时进行批量删除,或者有大量的并发更新操作时,可能会遇到一种现象:表中的数据行数并不大,但是查询和更新的性能出现明显下降。这种现象在 OceanBase 中称为 Queuing 表(业务上有时又称 Buffer 表)效应。
Buffer 表是 LSM-Tree 架构数据库都要面对的一类问题。LSM-Tree 架构下的删除操作,在合并之前都只是逻辑上标记删除而非物理删除,当增量数据中存在大量标记删除的数据时,物理行数量将远多于逻辑行数,从而会严重加剧存储引擎的读放大现象,并影响优化器对最优计划的选择。
分析:通过 explain extended_noaddr
分析方法很简单,用 EXPLAIN EXTENDED_NOADDR 命令对 SQL 进行详细的计划展示即可(通常用户在排查 SQL 性能问题时,会使用这种展示模式)。需要重点关注 physical_range_rows 和 logical_range_rows 的值。
上图中,physical_range_rows 和 logical_range_rows 分别表示 t1 表需要扫描的物理行数和逻辑行数。如果走了索引的话,含义为 t1 表在索引上需要扫描的物理行数和逻辑行数。
一般来说,physical_range_rows 和 logical_range_rows 这两个指标是相近的,看任意一个都可以。仅在这个特殊的 Buffer 表(频繁更新)场景下,physical_range_rows 可能会远大于 logical_range_rows,所以可以通过 physical_range_rows 和 logical_range_rows 的数值来判断是否出现 Buffer 表问题。
例如我向 t1 表插入 33 行数据,立即删除 29 行,那在增量数据里就会有 33 个物理行,其中 29 个物理行有 Delete 标记,查询真正的 4 个逻辑行时,就会出现大量无效扫描动作。
应对:Buffer 表自适应合并优化
为了更灵活地解决 Buffer 带来的性能下降问题,从 OceanBase 4.2.3 开始,为大家带来了 Buffer 表自适应合并优化特性。
该特性允许用户根据业务场景为每张表设置不同的表级配置项 table_mode 以指定不同的快速冻结与自适应合并策略,以应对 Buffer 表引起的读放大现象,从而提高系统长期运行下的 QPS 等性能指标。
针对 Buffer 表问题,OceanBase 提供了 5 种档位的 Table Mode 支持。不同的 Table Mode 对应不同的统计信息阈值与合并策略。存储层每次转储时都会根据转储的统计信息及 Table Mode 对应的阈值判断是否需要执行一次针对 Buffer 表场景的特殊合并以消除增量数据里的所有 Delete 标记,从而避免原有的大量无效扫描动作。
Table Mode转储后触发合并阈值转储后触发合并概率转储后合并类型Normal(默认值)较高(为基准1.0 )极低仅做 Medium CompactionQueuing高(0.9)低Moderate中(0.8)中优先做 Medium Compaction, 若 Medium 冷却中,做 MetaSuper低(0.6)高Extreme极低(0.5)较高仅做 Meta Compaction备注:
合并是 OceanBase 数据库将动静态数据做归并的行为,能有效消除增量数据
[*]Medium Compaction 是一类触发链路较长,需要保证多副本上 Major 一致性的合并,合并较慢;
[*]Meta Compaction 是由各个 Observer 主动发起,生成只读 Meta SSTable 的合并,合并较快;
简单来说,默认场景(Normal)下转储后发起合并的门槛比较高(例如认为转储中删除行超过 30 万才判断可能是 Buffer 表场景),因此转储后触发合并概率较低,触发的也是合并速度较慢的 Medium Compaction。
而随着 Table Mode 的逐步上调,转储后发起合并的门槛逐渐降低,触发合并的概率也逐渐提高,并且更倾向于做合并速度较快的 Meta Compaction,从而能及时通过合并消除增量数据避免 Buffer 表带来的大量无效扫描。
具体来说,不同参数下的策略可以分为如下三个模块:
触发快速冻结
对创建时间 > 存活时间阈值,且 memtable 有热点行(不在本次优化考虑内)或墓碑现象的触发快速冻结:
Table Mode存活时间阈值墓碑现象阈值Normal(默认)120s = 120s * 1.0
(更新行 + 删除行)> 25w = 25w * 1.0Queuing108s = 120s * 0.9(更新行 + 删除行)> 22.5w = 25w * 0.9Moderate96s = 120s * 0.8(更新行 + 删除行)> 20w = 25w * 0.8Super72s = 120s * 0.6(更新行 + 删除行)> 15w = 25w * 0.6Extreme60s = 120s * 0.5(更新行 + 删除行)> 12.5w = 25w * 0.5备注:
存活时间阈值的 120s 为 4.2 的阈值。在 4.3 及以上版本,此阈值为 300s。
转储时收集统计信息并尝试触发分区合并
冻结后紧接着会做一次转储,若转储同时满足如下条件,将会进行 tablet 级别的信息汇报:
[*]dml 数量 > 1000
[*]insert + update + delete 行数 > 1000
当然, tablet 信息的汇报并不止于转储,一些慢查询场景也会做相应处理,只是不在本任务范围内。
当 memtable 超过预设内存阈值或快速冻结触发后都会执行一次转储。如果触发了快速冻结,意味着墓碑现象可能已经出现了,因此根据上述快速冻结策略的墓碑策略再判断一次,如果命中,直接发起一次合并。
分区合并调度时调度分区合并
分区合并的调度由一个独立的调度线程负责,基本逻辑是通过自适应合并的策略来判断某个 tablet 是否需要执行一次分区合并。相应地,也会根据不同的 table mode 调整自适应策略。
与 table mode 有关的自适应合并策略如下:
备注:
这里引用一下 OB 官网的 自适应合并 内容:
其中第三点:根据统计增量数据的总行数这个并不受 table mode 控制,增量行数的阈值是超过 10w 或者增量行达到极限 70%,对于非 Normal 表,大概率已经触发合并了。
导数场景
Table Mode导数场景条件(需同时满足)
根据统计的 10 分钟内信息热点 tablet插入比例 >= 70%
且操作行数满足 (操作行数 = insert + delete + update)Normal(默认)查询次数 + 转储次数 > 5 = 5 * 1.0操作行数 >= 1w = 1w * 1.0Queuing查询次数 + 转储次数 > 4.5 = 5 * 0.9操作行数 >= 9k = 1w * 0.9Moderate查询次数 + 转储次数 > 4 =5 * 0.8操作行数 >= 8k= 1w * 0.8Super查询次数 + 转储次数 > 3 = 5 * 0.6操作行数 >= 6k = 1w * 0.6Extreme查询次数 + 转储次数 > 2.5 = 5 * 0.5操作行数 >= 5k = 1w * 0.5墓碑场景
Table Mode墓碑场景条件(需同时满足)
当转储次数 ≥ 2
且根据统计的 10 分钟内信息
操作行数 = insert + delete + update更新删除行数比例Normal(默认)操作行数 > 1w = 1w * 1.0更新删除比例 >= 30% = 30% * 1.0Queuing操作行数 > 9k = 1w * 0.9更新删除比例 >= 27% = 30% * 0.9Moderate操作行数 > 8k= 1w * 0.8更新删除比例 >= 24% = 30% * 0.8Super操作行数 > 6k = 1w * 0.6更新删除比例 >= 18% = 30% * 0.6Extreme操作行数 > 5k = 1w * 0.5更新删除比例 >= 15% = 30% * 0.5与此同时,为了防止 10 分钟内的信息不够全面,统计了 tablet 自上次 medium/meta 时的删除行总数,并有满足如下条件时触发一次合并。
Table Mode总删除行数Normal(默认)总删除行 > 30wQueuing总删除行 > 20wModerate总删除行 > 10wSuper总删除行 > 5wExtreme总删除行 > 1k但是值得一提的是, 总删除行这个条件属于锦上添花类型,类似于 cache,很有可能未满足条件时 tablet 统计信息就被别的 tablet 刷掉了。同时,按照最宽松的 10 分钟内 1w 的条件,如果每个 10 分钟内信息都是 9999 行,要达到 30w 的阈值也得近 300 分钟,作用是更多是在 tablet 数量少的时候留下来兜底。
低效读场景
Table Mode低效读场景条件(需同时满足)
根据统计的 10 分钟内信息热点 tablet慢查询比例(满足其一即可)Normal(默认)查询次数 + 转储次数 > 5 = 5 * 1.0+ 扫描物理行数 >= 1000 = 1000 * 1.0
+ 扫描微块数 >= 10 = 10 * 1.0
+ rowkey exists 数 >= 5 = 5 * 1.0Queuing查询次数 + 转储次数 > 4.5 = 5 * 0.9+ 扫描物理行数 >= 900 = 1000 * 0.9
+ 扫描微块数 >= 9 = 10 * 0.9
+ rowkey exists 数 >= 4.5 = 5 * 0.9Moderate查询次数 + 转储次数 > 4 =5 * 0.8+ 扫描物理行数 >= 800 = 1000 * 0.8
+ 扫描微块数 >= 8 = 10 * 0.8
+ rowkey exists 数 >= 4 = 5 * 0.8Super查询次数 + 转储次数 > 3 = 5 * 0.6+ 扫描物理行数 >= 600 = 1000 * 0.6
+ 扫描微块数 >= 6 = 10 * 0.6
+ rowkey exists 数 >= 3 = 5 * 0.6Extreme查询次数 + 转储次数 > 2.5 = 5 * 0.5+ 扫描物理行数 >= 500 = 1000 * 0.5
+ 扫描微块数 >= 5 = 10 * 0.5
+ rowkey exists 数 >= 2.5 = 5 * 0.5使用指南
创建表时指定 Table Mode
用户可以在创建表时就显式指定 Table Mode,若不指定则默认为 Normal 模式。
# 语法
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';修改表的 Table Mode
用户可以通过 DDL 语句显式修改 Table Mode
# 语法
alter table t1 set table_mode = 'normal/queuing/Moderate/Super/Extreme';
# 修改表的 table_mode 为 moderate
alter table t1 set table_mode = 'moderate';例如,当用户观测到某张表的读放大现象严重,出现 Buffer 表现象时,可以视业务量规模与特点手动修改该表的 Table Mode 到更高档位,如从 normal 调整到queuing,从 queuing 调整到 super 等。当 Table Mode 在存储层生效后,将会通过调度自适应合并的方式发起合并以解决 Buffer 表现象。
与此同时,更激进的 Table Mode 将会更频繁地发起合并,从而消耗更多的计算资源,如果此时业务体量或场景能够容忍一定程度的 Buffer 表效应,也可以手动调低数据表的 Table Mode。
Table Mode 生效说明
值得说明的是,尽管 Table Mode 在 Table Schema 中是实时生效的,但是在存储层后台对 tablet 信息的统计是是延迟更新的(2 分钟刷新一次),因此当修改表的 Table Mode 后,平均得经过 60s 才能生效。
另外,存储层后台对每个租户下转储统计信息的覆盖范围是有限的,由于资源有限,并不是所有 tablet 上的转储信息都会被统计到,只有转储时满足某些特定条件的 tablet(写入数据量较大,读放大严重等)才会被纳入统计,Table Mode 对转储/合并行为的影响也仅限于这些被纳入统计的 tablet 上。
简单来说,Queuing 表的调度依赖于缓冲池内定期从内部表更新的 Table Mode,我们认为满足 buffer 表行为的 tablet 进入缓冲池的概率是极大的,近似于不会被淘汰(除非 Queuing 表的 分区数量过于巨大)。
如果用户想主动让 table 加入缓冲池,就手动执行一次转储即可。
性能说明与表现形式
通过构造频繁插入/更新/删除的 workload 对比了不同 Table Mode 下表对查询的性能表现。
基本测试流程
首先创建 5 张表,Table Mode 分别为 NORMAL、QUEUING、MODERATE、SUPER、EXTREME
CREATE TABLE table_name (k int primary key, v int) table_mode='table_mode'之后重复执行如下流程 500 轮,每轮起 5 个线程对 5 张表分别进行采样,等所有线程都 return 后再开始下一轮
-- 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 >= 0;
-- 5. 第 3 轮采样,记录时间
SELECT COUNT(*) FROM table_name;
-- 6. 执行删除语句
DELETE /*+ parallel(8)*/ FROM table_name WHERE k >= 0
-- 7. 第 4 轮采样,记录时间
SELECT COUNT(*) FROM table_name;这样一轮可以得到 4 个采样数据点,最后每张表可以得到 2000 个采样点。
测试结果
Table Mode平均查询时间(秒)相比Normal 提升NORMAL0.47502673316001890QUEUING0.4220401510000229-11.5%MODERATE0.3732480742931366-21.4%SUPER0.2679354891777039-43.6%EXTREME0.23057958698272704-51.5%
从图中可以比较直观地看出几点:
[*]前 375 次左右的采样实际上各个模式的表现是类似的,这是因为设置 table mode 对转储合并的影响需要一段时间内才能传递到存储层:转储线程 2 分钟一轮,且只有转储满足满足一定条件时,tablet 的统计信息才会被更新 tablet 从而影响快速冻结与合并。
[*]激进模式(Extreme)的表现是最好的,当查询时延逐渐增加时,可以及时触发快速冻结及后续合并从而降低 Buffer 表的读放大现象。从图中也可以看到,除了一开始的预热阶段,每次 Extreme 总是及时将波峰降下去,也即及时做了一次合并。
不同 Table Mode 设置下对性能的直观表现
从该图可以直观看出不同 Table Mode 对合并行为以及查询性能的影响。每一次查询时延波峰的下降都代表着一次合并的发起,越高 Table Mode 下的表将会更频繁地发起合并,在图上就体现为合并次数多。同时,由于及时清除增量数据中的 Delete 标记,平均查询时延也更低。
总结
从上述测试数据可以看出,越高的 Table Mode 能够发起更频繁的合并从而降低查询的时延。但是合并的调度与发起也都会消耗更多的计算资源,因此用户需要根据面对的业务场景、数据量大小以及对 Buffer 表效应的容忍程度选择合适的 Table Mode 以达成整体的平衡。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]