OceanBase调优实战:从合并策略到执行计划的工程化调优体系 OceanBase的运维调优最容易陷入的误区是把注意力集中在参数列表和命令语法上却忽略了参数背后的机制耦合。一个内存参数的调整可能同时影响写入限速、合并触发和查询缓存的行为一个索引的添加可能改善某条查询的执行计划却给写入路径带来未被预期的合并压力。分布式架构把这种耦合放大了因为任何一个局部的变更都会通过副本同步、共识协议和资源调度传播到整个集群。真正有效的调优建立在对三个层次之间关系的清晰理解之上。最底层是LSM-Tree存储引擎的写入与合并机制它决定了数据以什么节奏从内存落到磁盘、以什么代价被查询读取。中间层是SQL优化器与执行引擎它决定了每一条查询走哪条路径、消耗多少资源。最上层是资源调度与路由层它决定了计算和存储在多个租户、多个副本之间如何分配。调优的动作发生在某一层但效果的传导跨越三层。本文沿着这条从底层机制到上层策略的路径展开。先拆解LSM-Tree架构下合并与转储的调优逻辑再进入SQL层的执行计划稳定性与并行执行效率然后讨论锁与等待事件的诊断方法最后落到资源隔离、读写分离与诊断工具链的工程实践。文章的目标不是提供一份参数速查表而是建立一套判断框架在遇到性能异常时知道先看哪一层知道一个调整会在哪些地方产生副作用。第一章 LSM-Tree的调优逻辑1.1 基线加增量的读写分离OceanBase的存储引擎基于LSM-Tree架构将数据分为静态基线数据和动态增量数据两部分。SSTable是只读的一旦生成就不再被修改存储于磁盘MemTable支持读写存储于内存。DML操作首先写入MemTable当MemTable达到一定大小后转储到磁盘成为SSTable。查询时需要分别对SSTable和MemTable进行查询并将结果归并后返回。这种设计的核心取舍是用写放大换取读放大。写入路径始终是顺序追加MemTable到SSTable的转储是批量顺序写写吞吐因此可以很高。但读取时需要合并多个版本的数据合并的代价取决于SSTable的数量和层次。为了控制读放大系统需要通过合并将多个SSTable归并为更少、更大的文件。OceanBase在这个通用LSM-Tree模型上做了一个关键的差异化设计宏块粒度的合并重用。数据文件按照2MB的基本粒度切分为宏块每个宏块内部再拆分出变长的微块。合并时没有更新的数据宏块不会被重新打开读取只有发生变化的数据需要被重写。这种设计显著降低了合并期间的写放大使OceanBase在保持LSM-Tree优点的同时避免了传统LSM-Tree数据库合并代价过高的缺陷。1.2 转储与合并的分工转储和合并是两个不同粒度的操作。转储将MemTable中的数据刷写到磁盘成为增量的SSTable它的粒度是单个租户的单个分区资源消耗相对较小对在线业务的影响较低。合并将增量SSTable与基线SSTable归并为新的基线它的粒度涉及整个租户甚至整个集群的SSTable资源消耗大对QPS和TPS有中度影响。转储的频率由freeze_trigger_percentage控制它决定了租户内存使用达到什么比例时触发冻结。默认值是70取值范围为1到99。降低这个值会让冻结更早发生MemTable中的内存更早被释放但转储的频率也会上升。提高这个值会减少转储次数但MemTable可能在高写入负载下更快逼近内存上限。合并的频率由minor_freeze_times和major_compact_trigger共同控制。minor_freeze_times设置单个租户冻结多少次后触发一次合并默认值为5。这个参数是转储与合并之间的调节阀值越大转储积累的增量SSTable越多才触发合并合并的频率降低写入吞吐可能提升但查询时需要合并的SSTable数量增加读放大上升数据时效性也可能下降。值越小合并更频繁读放大被控制在较低水平但合并占用的CPU和IO资源更频繁地出现。调优的判断依据不是寻找一个最优的默认值而是根据业务的读写比例和延迟要求来校准。读密集且对查询延迟敏感的业务应该让合并更频繁地发生把读放大控制在低位。写密集且可以容忍查询路径上多一层合并的业务可以适当放宽合并频率把资源优先留给写入。1.3 并行合并的边界条件并行合并默认开启它将一个compaction任务按照key range拆分成若干子任务并行执行。并行度与分区数的匹配关系直接影响合并效率。一般来说并行度与分区数符合整比例关系时比如分区数为8、并行度设为4或8能够获得较好的性能。并行合并有一个容易被忽视的边界当Major SSTable的数据量很小而增量数据很多时并行合并的range拆分可能把增量数据都划分到同一个compaction任务中此时并行合并等价于单线程执行增量数据的合并。这意味着并行合并的收益取决于数据在key range上的分布是否均匀。如果增量数据集中在少数key range上即使开启了并行合并实际的有效并行度也远低于配置值。合并线程数由merge_thread_count控制L0层转储线程数由_mini_merge_concurrency控制。这些参数的调整需要与硬件资源匹配。合并是CPU和IO密集型操作线程数超过CPU核心数时线程之间的争用会抵消并行度的收益线程数远低于核心数时合并速度受限于单线程的处理能力积压的增量数据会延长读放大持续的时间。转储的并发分区数需要平衡。并发转储的分区过少MemTable内存释放不够快写入可能因为内存不足而受限并发转储的分区过多会消耗过多资源影响在线交易的性能。1.4 合并策略与压缩的权衡合并过程中数据会被重新编码和压缩。压缩算法的选择直接影响存储成本和查询延迟。更高的压缩率节约磁盘空间但牺牲性能。ZSTD比LZ4节省更多空间但ZSTD合并的时间更长查询的响应时间也更大。对于写入密集且查询以点查为主的OLTP业务LZ4或更轻量的压缩算法更合适因为点查的延迟对解压开销敏感。对于查询频率较低的历史数据或分析型负载ZSTD的高压缩比带来的存储成本节约可能超过解压开销的代价。压缩策略的选择应该与数据的访问模式对齐而不是追求单一的全局最优压缩率。压缩算法的选择还需要考虑合并窗口的约束。高压缩率的算法在合并期间消耗更多的CPU时间如果维护窗口有限合并可能无法在窗口内完成。对于合并窗口紧张的场景可以在维护窗口期间临时切换到低压缩率的算法窗口结束后再切回高压缩率。这种动态调整需要权衡存储成本和合并窗口的约束。1.5 合并积压的诊断与处理合并积压是LSM-Tree架构下最常见的性能退化模式。当增量数据的产生速度超过合并的处理速度时增量SSTable的数量持续增长查询需要归并的文件越来越多读放大线性上升响应时间随之劣化。诊断合并积压的起点是观察增量SSTable的数量和合并任务的排队情况。通过系统视图可以查看每个分区的SSTable数量、合并任务的执行状态和等待队列长度。如果SSTable数量持续超过阈值且合并任务队列长期非空说明合并能力不足。处理合并积压有两条路径。第一条是提升合并能力增加合并线程数、提高合并并行度、或者将合并调度到资源更充裕的时段。第二条是降低增量数据的产生速度调整freeze_trigger_percentage减少转储频率、或者通过限流控制写入速率。两条路径的选择取决于系统的瓶颈是在合并侧还是在写入侧。合并积压的预防比处理更重要。在业务规划阶段应当根据预期的写入速率和查询延迟要求预先估算合并能力的需求并为合并预留足够的CPU和IO资源。等到积压出现再处理往往需要付出更大的代价。第二章 SQL层的调优2.1 执行计划的缓存与软解析一条SQL语句的执行经过编译和执行两个阶段。编译阶段优化器进行词法分析、语法分析、语法优化和代码生成生成执行计划后提交给执行器执行。这个过程称为硬解析消耗资源并增加SQL延迟。为了避免每次请求都进行硬解析执行计划被缓存在计划缓存中相同类型的后续SQL语句首先在计划缓存中查找匹配的计划找到后直接提交执行器执行这个过程称为软解析。计划缓存的存在意味着一条SQL在第一次执行时生成的计划可能在后续数千次执行中被反复复用。如果第一次生成的计划是非最优的那么这个非最优计划的影响会被放大数千倍。优化器生成非最优计划的原因包括缺少索引、统计信息过时和账号权限限制。非最优计划的直接代价是逻辑读次数高、扫描数据量大最终表现为延迟上升和吞吐下降。2.2 用SQL Audit定位需要优化的语句定位SQL层面的性能问题起点是V$OB_SQL_AUDIT视图。通过按平均执行时间乘以执行次数降序排列可以找出消耗租户资源最多的TopSQL语句。一个典型的查询会聚合每个SQL_ID的平均耗时、平均排队时间、平均逻辑读、执行次数、平均CPU时间和平均等待时间并按平均执行时间乘以执行次数排序取前十。逻辑读高的SQL是优化的重点因为逻辑读直接反映了查询在存储层扫描的数据量而扫描量是可以通过索引调整和SQL改写来控制的。找到可疑SQL之后下一步是查看它的执行计划。如果执行计划显示全表扫描、索引未命中或者连接顺序不合理就需要判断是统计信息过时、索引缺失还是SQL写法导致优化器做出了错误的选择。SQL Audit的价值不仅在于定位单条SQL还在于识别SQL的模式。如果多条SQL都因为相同的原因而性能不佳例如都缺少相同的索引或者都因为统计信息过时而选择了错误的连接顺序那么一次系统性的调整可能解决一批SQL的问题而不是逐个修复。2.3 统计信息的收集时机与方式统计信息是优化器决策的依据。OceanBase支持通过DBMS_STATS包或ANALYZE语句手动收集统计信息。V4.x版本支持主表和索引表的统计信息收集MySQL模式下可以使用dbms_stats.gather_table_stats或dbms_stats.gather_index_stats后者支持指定并行度。统计信息的收集时机需要与数据变化节奏匹配。对于写入量大的表统计信息在大量数据变更后会迅速过时导致优化器基于旧的分布信息估算行数进而选择错误的连接算法或索引。在批量导入、大规模数据迁移或分区变更之后应该主动收集相关表的统计信息而不是依赖自动收集的周期。统计信息的收集本身也消耗资源。全表扫描和直方图计算在高并发时段执行会与业务查询争抢IO和CPU。收集动作应该安排在业务低峰期或者针对变化显著的分区做增量收集而不是每次都对全表做完整的收集。统计信息的粒度也需要考虑。对于分区表可以收集全局统计信息和分区统计信息。全局统计信息用于跨分区的查询规划分区统计信息用于分区裁剪后的查询规划。如果只收集了全局统计信息而忽略了分区统计信息优化器在分区裁剪后可能低估了分区内的数据量选择了不适合的并行度或连接算法。2.4 Outline与计划绑定当统计信息调整之后优化器仍然选择非最优计划或者业务希望固定某个经过验证的最优计划时可以使用Outline进行计划绑定。CREATE OUTLINE语句将指定的执行计划绑定到特定的SQL语句上使该SQL的后续执行强制走绑定的计划。Outline的使用场景需要区分清楚。如果优化器选择一个索引是因为它在当前数据分布下确实更优那么强制绑定另一个索引可能在未来数据分布变化后变得次优。Outline适合的是那些优化器因为统计信息无法准确反映业务语义而做出错误选择的场景。例如某条查询的业务语义决定了它总是只返回极少数行但优化器因为统计信息中的平均值估算而认为它需要扫描大量数据此时绑定一个索引扫描的Outline是合理的。计划缓存的另一个问题是计划可能因为各种原因失效比如统计信息更新、Schema变更或者内存压力导致的缓存淘汰。Outline在计划失效后仍然能够将SQL重新引导到绑定的计划上这是它相对于单纯依赖计划缓存的价值。Outline的管理也需要流程化。绑定的计划需要被记录和定期审查当数据分布发生变化或者索引结构发生变化时原有的绑定计划可能不再是优计划。Outline的清理应当与索引变更和统计信息更新同步进行。2.5 并行执行的适配条件并行执行通过将一个查询拆分为多个子任务在多核或多节点上同时执行来加速大查询但它的收益依赖于一系列前提条件。分区策略是第一个前提。在大表JOIN的场景中如果参与JOIN的表使用相同的分区方式并绑定到同一个表组可以实现partition wise join这是性能最佳的并行JOIN形式。如果分区方式不匹配数据需要做重分布网络传输和内存开销会抵消并行度的收益。并行度与分区数的比例关系是第二个前提。并行度与分区数符合整比例关系时性能较好比如分区数为8并行度设为4、8或16。如果并行度远高于分区数多余的并行任务会因为无数据可处理而空转如果并行度远低于分区数单个并行任务需要处理多个分区的数据并行加速比受限。索引的合理性是第三个前提。创建合适的索引减少数据扫描量才能让并行执行的每个子任务处理更少的数据并行加速比才不会被单任务的IO瓶颈所限制。在并行执行中如果某个子任务因为缺少索引而退化为全表扫描整个查询的完成时间由最慢的子任务决定并行度再高也无法弥补这个短板。复制表是第四个可以借用的能力。V4.2及之后版本支持创建复制表复制表在每个节点上都有完整的数据副本JOIN时不需要做数据重分布可以提升并行执行的效率。复制表适合小维表与大事实表的JOIN场景不适合写入频繁的大表。2.6 SQL改写的常见模式除了索引和统计信息SQL的写法本身也直接影响执行计划的优劣。一些常见的改写模式可以在不改动数据模型的前提下获得显著的性能提升。子查询展开是最常见的改写之一。IN子查询和EXISTS子查询在某些场景下会被优化器转换为半连接或反连接但在另一些场景下可能被逐行执行。将子查询改写为JOIN或者在子查询中增加过滤条件以减少中间结果集可以避免逐行执行的开销。分页查询的深分页问题也是一个典型场景。LIMIT加OFFSET的写法在OFFSET很大时需要扫描并丢弃大量行将分页改写为基于上一页最后一条记录的主键范围查询可以避免无效扫描。聚合查询的预计算是另一个方向。对于频繁执行的聚合查询可以通过物化视图预先计算并存储聚合结果查询时直接读取物化视图而不是扫描原始数据。物化视图的刷新策略需要与数据的更新频率匹配过于频繁的刷新会抵消预计算的收益。第三章 锁与等待事件3.1 等待事件的分层等待事件是分析并发性能瓶颈的核心工具。在任何时间点OceanBase的工作线程要么在计算CPU利用率要么在等待资源例如IO资源、网络带宽或临界区。通过等待事件发生的位置和统计信息可以识别系统性能瓶颈尤其是高并发小查询的性能。OceanBase目前支持超过200个等待事件分为11个大类包括网络、调度、临界区、latch、集群、事务提交、用户IO和系统IO等。用户IO等待事件由用户请求直接触发系统IO等待事件由后台任务触发。典型的诊断场景有三类。第一类是CPU利用率持续偏低而系统负载上升这通常是因为并发瓶颈例如频繁访问的临界区出现资源争用通过定位等待时间最长的TopN等待事件可以找到争用的临界区。第二类是业务SQL的响应时间偶尔从不到1毫秒飙升到100毫秒以上这通常是因为SQL的执行在某处被卡住通过查看SQL执行期间记录的等待事件详情尤其是耗时最长的等待事件的细节可以定位卡住的位置。第三类是系统突然停止执行某条SQL这通常是因为会话被锁竞争或未知原因的死锁阻塞通过查看会话当前等待事件的详情可以定位原因。3.2 行锁冲突的排查路径行锁冲突是最常见的并发问题之一。排查的思路是把锁抽象为两个对象行锁的持有者和行锁的等待者两者都是事务的一部分因此在监控活跃事务的同时监控锁的持有者和等待者可以对锁冲突问题提供很大的帮助。OceanBase提供了三张核心的虚拟表。__all_virtual_trans_stat用于监控活跃事务包含事务的唯一标识、所在分区、参与者列表、创建时间、状态和锁冲突重试次数等信息。__all_virtual_trans_lock_stat记录当前集群中所有活跃事务持有行锁的信息包括事务标识、持有锁的行的rowkey和所属会话。__all_virtual_lock_wait_stat统计当前集群中所有等待行锁的请求或语句的信息包括等待的行的rowkey、开始等待的时间、绝对超时时间、重试加锁的次数、等待锁的会话ID和该行第一个等待事务的会话ID。一个典型的排查场景是一个会话中的未知长事务持有锁阻塞了其他事务的执行需要找到并停止这个长事务。方案是通过无法加锁的事务的session_id在__all_virtual_lock_wait_stat中找到它等待锁的行的rowkey然后通过rowkey在__all_virtual_trans_lock_stat中找到持有该行锁的事务和会话最终定位到阻塞源。3.3 锁的可观测性与死锁检测一个更棘手的问题是通过锁视图查到持有锁的会话ID之后在会话列表中看到该会话处于SLEEP状态无法判断这个会话是否可以安全终止也无法确定它属于哪个业务请求。这种场景需要把锁信息与SQL审计关联起来。GVOBLOCKS视图可以查到持有锁的会话GVOBL​OCKS视图可以查到持有锁的会话GVOB_SQL_AUDIT可以查到该会话最近执行的SQLGV$OB_TRANSACTION_PARTICIPANTS可以查到事务的参与者信息。通过事务ID把三者串联可以从锁的持有者追溯到上锁的具体SQL和业务请求。死锁的检测依赖于等待图。当事务持有的锁与其他事务冲突时会形成wait-for链系统定期检测这条链如果检测到闭环就判定为死锁并回滚其中一个事务。死锁检测的周期和重试次数可以在OCP层面通过deadlock_check_period和deadlock_retries参数调整。缩短检测间隔可以更快地发现和解除死锁但增加了检测本身的开销。锁的粒度也影响并发性能。行锁的并发度高于表锁但行锁的管理开销更大。OceanBase默认使用行锁在需要批量操作大量行时可以考虑使用LOCK TABLE语句显式加表锁减少行锁的管理开销。但这会降低并发度需要根据操作的规模和并发要求来权衡。3.4 临界区与latch的争用除了行锁临界区和latch的争用也是并发性能的常见瓶颈。临界区是内核中不允许并发访问的代码段latch是保护临界区的轻量级同步原语。当多个线程同时请求进入同一个临界区时只有一个线程能获得latch其他线程排队等待。临界区争用的表现是CPU利用率不高但QPS上不去等待事件中latch相关的等待占比高。诊断的方法是查看等待事件的TopN找到争用最严重的latch类型然后分析该latch保护的数据结构和访问模式。缓解临界区争用的方法包括减少临界区的持有时间、拆分热点数据结构、以及通过调优减少对热点数据结构的访问频率。在某些情况下增加并发度反而会加剧争用此时降低并发度、让请求排队反而能获得更高的吞吐。这需要根据实际的等待事件数据来判断而不是凭直觉调整。第四章 资源隔离与路由策略4.1 集群级内存的分配逻辑内存是OceanBase调优中最关键的资源因为内存的分配直接决定了写入缓冲、合并触发和查询缓存的可用空间。memory_limit配置OBServer最大可使用的总内存大小当前该值无上限但如果设置的上限超过实际物理内存总和会导致系统中其他按总内存计算的值偏大。memory_limit需要与memory_limit_percentage配合设置。system_memory设置系统预留给非实体租户的内存容量默认值为30G。这部分内存不分配给业务租户用于系统内部的元数据管理、RootService运行和内部保留。rootservice_memory_limit设置RootService的最大内存容量限制默认值为2G。memstore_limit_percentage设置租户使用MemStore的内存占其总可用内存的百分比默认值为50。这个参数决定了租户内存中用于写入缓冲的比例。写入密集的业务需要更高的MemStore比例来缓冲写入峰值但过高的比例会挤压查询缓存和Block Cache的空间影响读取性能。内存分配的一个常见误区是只关注总量而忽略分配比例。总内存足够但MemStore比例过低时写入峰值会触发频繁的冻结和转储影响写入性能。MemStore比例足够但查询缓存比例过低时高频查询会因为缓存命中率下降而退化。内存分配需要根据业务的读写比例来校准而不是简单地增加总量。4.2 CPU配额与并发控制cpu_quota_concurrency设置租户的每个CPU配额所允许的最大并发数默认值为4取值范围为1到10。这个参数影响租户内工作线程的并发度。对于以短查询为主的高并发业务适当的并发度可以提升CPU利用率对于以长查询为主的业务过高的并发度可能导致线程争用和上下文切换开销。租户资源的定义还包括IOPS和会话数限制。在资源配置时MAX_CPU必须大于或等于MIN_CPU单元配置的CPU、内存和日志盘容量在修改后必须大于或等于租户已使用的容量。资源超配是租户创建失败的常见原因CPU或内存的超额分配会导致创建过程在资源检查阶段被拒绝。obdiag的巡检能力可以快速发现租户资源配置的问题例如租户线程利用率超过95%、副本数不一致、宏块利用率过低等。这些指标如果不在上线前检查可能在业务高峰期暴露为不可用或性能骤降。4.3 读写分离与LDC路由读写分离是通过将弱一致性读请求路由到备副本或只读副本来分担主副本的读取压力。OceanBase的读写分离依赖于OBProxy的路由策略。弱一致性读有两种路由策略读写分离和LDC路由。LDC路由的优先级高于读写分离ODP在选择副本时先检查LDC路由再走读写分离路由。proxy_route_policy参数控制ODP的弱读请求路由策略默认策略允许弱读请求被路由到备副本和只读副本备副本的类型由route_target_replica_type决定。LDC路由的核心是利用proxy_idc_name配置指定ODP在逻辑数据中心中的位置使弱读请求只路由到特定区域的副本。这种方式适合跨地域部署的场景可以避免弱读请求跨地域访问带来的延迟。读写分离策略则通过ob_route_policy系统变量控制默认取值为readonly_zone_first即优先读只读型副本。读写分离的前提是业务能够容忍弱一致性读的数据延迟。对于需要读到最新数据的查询比如刚写入后立即读取的场景弱读会导致读不到刚提交的数据。这类查询应该走强一致性读路由到主副本而不是为了分担压力而牺牲一致性。4.4 资源组与租户隔离OceanBase支持通过资源组和租户来隔离不同业务的资源。租户是资源隔离的基本单位每个租户拥有独立的CPU、内存和存储资源。资源组用于在租户内部进一步细分资源的分配。租户隔离的价值在于防止业务之间的相互干扰。一个写入密集的业务不会因为其合并操作而影响另一个读密集业务的查询延迟因为合并操作消耗的是写入密集租户的资源。租户隔离的代价是资源利用率可能下降因为每个租户的资源配置需要按照其峰值需求来预留而不同租户的峰值可能不在同一时间出现。租户隔离的粒度需要权衡。过细的租户划分会导致资源碎片化和配置管理的复杂度上升过粗的划分则可能让不同业务在同一租户内相互干扰。通常的建议是按照业务线或按照SLA等级来划分租户同一业务线内共享租户不同SLA等级的业务分开租户。第五章 诊断工具链5.1 obdiag的定位与部署obdiag是专为OceanBase设计的黑屏诊断工具功能覆盖对日志、SQL审计以及进程堆栈等信息的扫描、收集和分析。它的价值在于把原本分散在多个视图、日志文件和命令行工具中的诊断动作收敛为一条命令的采集和分析。obdiag支持独立部署和通过obd部署两种方式。独立部署时通过yum安装oceanbase-diagnostic-tool包执行init.sh初始化环境。部署完成后通过obdiag config命令配置待诊断集群的连接信息。obdiag采用集中式收集只需要部署在任意一台能连接到集群各节点的服务器上不需要在每台OBServer节点上部署。5.2 一键收集SQL诊断信息慢查询诊断的核心是trace_id。可以通过GV$OB_SQL_AUDIT按query_sql模糊匹配获取近期SQL的trace_id也可以在当前会话执行SELECT last_trace_id获取前一条SQL的trace_id。获取trace_id之后执行obdiag gather plan_monitor命令一键收集该SQL的执行计划、运行时统计、等待事件和相关日志。这个命令把原本需要手工在多个视图之间比对的信息聚合为一个结构化的诊断包使问题定位从凭经验猜变成按证据查。perf_sql场景是另一个针对慢查询的采集能力通过trace_id一键采集所有相关诊断信息适合SQL超时这类业务最常见的问题。5.3 系统巡检与配置核查性能问题的一个常见根源是系统配置与硬件规格不匹配或者多个参数之间存在未被察觉的冲突。obdiag的system巡检项可以一次性检查所有关键配置包括操作系统层面的参数和OceanBase层面的参数找出可能导致瓶颈的配置项。巡检的价值在于把上线前的检查从人工清单变成自动化流程。人工检查容易遗漏尤其是在集群规模大、参数多的情况下。自动化巡检能够以一致的标准覆盖所有配置项并在发现异常时给出具体的修改建议。租户资源配置的巡检可以检查线程利用率、副本一致性和宏块利用率等指标这些指标如果在上线前被忽略会在业务运行中以难以定位的性能异常形式暴露。巡检的另一个用途是变更前后的对比。在参数调整或扩容操作之前执行一次巡检操作之后再执行一次对比两次巡检的差异可以快速确认变更是否达到了预期效果是否引入了新的配置问题。这种对比比单纯看某个指标的变化更全面因为它覆盖了所有关键配置项而不是只看被调整的那一个。第六章 调优的判断框架6.1 从症状到层次的映射调优的起点是症状但症状本身不指向具体的层次。同样表现为响应时间上升可能是SQL执行计划变差可能是合并积压导致读放大可能是锁等待也可能是租户内存不足导致频繁冻结。有效的诊断需要把症状映射到最可能出问题的层次。如果延迟上升同时伴随逻辑读显著增加优先看SQL层。逻辑读的增加通常意味着执行计划选择了扫描更多数据的路径原因可能是统计信息过时或索引失效。如果延迟上升伴随CPU利用率下降、IO等待上升优先看存储层。合并积压或转储不及时会导致MemTable中的数据在查询时需要与更多的SSTable归并读放大上升。检查freeze_trigger_percentage、minor_freeze_times和合并线程的积压情况。如果延迟上升表现为间歇性的尖峰且尖峰时间与某些操作的时间窗口重合优先看并发层。锁等待或latch争用会造成间歇性的阻塞通过等待事件的TopN视图可以定位争用的临界区。如果延迟上升在特定租户上表现明显而其他租户正常优先看资源层。租户的CPU配额、内存配额或连接数可能已经接近上限检查租户的资源使用率与配额的比例。6.2 调优动作的传导路径任何一个调优动作都会沿着依赖链传导在解决一个问题的同时可能引入新的问题。在调整参数之前理解这个参数的传导路径比参数本身更重要。调小freeze_trigger_percentage会减少MemTable的内存占用但转储频率上升转储消耗的IO和CPU增加。如果系统的IO已经接近饱和这个调整可能缓解内存压力却加剧IO争用。调大minor_freeze_times会降低合并频率写入吞吐可能提升但查询时需要归并的SSTable数量增加读放大上升。如果业务以读为主这个调整会让查询延迟恶化。增加并行度可以加速大查询但如果并行度超过分区数的合理比例或者数据分布不均匀并行度的收益会被空转和跨节点数据传输所抵消。增加索引可以改善特定查询的执行计划但每个新增索引都会增加写入路径的维护开销和合并时的索引重建成本。索引的收益应该在查询性能的提升和写入路径的代价之间做权衡。6.3 调优的验证方法调优动作的效果需要通过数据来验证而不是通过主观感受来判断。SQL层的调优验证指标是目标SQL的平均执行时间、逻辑读次数和CPU时间的变化。V$OB_SQL_AUDIT视图可以按SQL_ID做前后对比。存储层的调优验证指标是合并的完成时间、增量SSTable的数量、MemTable的冻结频率和查询的平均IO等待时间。如果合并的完成时间没有缩短或者读放大没有下降调整可能没有触及真正的瓶颈。资源层的调优验证指标是租户的CPU和内存使用率是否回到了配额的安全区间以及租户内查询的排队时间是否下降。验证的周期需要覆盖业务负载的完整波动。在低谷期表现良好的调整可能在高峰期因为并发上升而失效。调整后的观察窗口应该至少覆盖一个完整的业务峰值。第七章 典型故障场景的调优实录7.1 场景一 合并积压导致的查询延迟飙升某交易系统的查询延迟在业务高峰期从平均五毫秒上升到八十毫秒CPU利用率没有明显变化但IO等待显著上升。诊断路径查看增量SSTable的数量发现多个核心分区的增量SSTable数量超过阈值合并任务队列积压。查询这些分区的执行计划发现原本使用索引扫描的查询因为需要归并过多SSTable而退化。根因分析业务的写入速率在高峰期显著上升freeze_trigger_percentage的默认值导致MemTable频繁冻结增量SSTable快速积累。合并线程数不足以处理积压读放大持续恶化。调优动作增加merge_thread_count将合并线程数从四增加到八。将minor_freeze_times从五调整为三让合并更频繁地触发避免增量SSTable过度积累。同时将合并窗口的调度调整到业务低谷期避免合并与业务争抢IO。效果验证调整后增量SSTable的数量回到阈值以下查询延迟恢复到六毫秒IO等待下降到可接受水平。7.2 场景二 执行计划退化导致的单条SQL性能骤降某报表查询的执行时间从两百毫秒突然上升到十五秒执行计划从索引范围扫描变为全表扫描。诊断路径通过V$OB_SQL_AUDIT定位到该SQL的trace_id执行obdiag gather plan_monitor获取执行计划的详细信息。对比历史执行计划发现优化器选择的索引发生了变化。根因分析表的统计信息在最近一次批量导入后没有更新优化器基于过时的统计信息估算行数错误地判断索引扫描的成本高于全表扫描。调优动作执行DBMS_STATS.GATHER_TABLE_STATS更新统计信息重新执行查询执行计划恢复为索引范围扫描。同时为该SQL创建Outline固定索引范围扫描的计划防止未来统计信息波动导致计划漂移。效果验证SQL执行时间恢复到两百毫秒以内在后续两周的观察中没有再次出现计划漂移。7.3 场景三 租户内存不足导致的写入阻塞某写入密集的业务在高峰期出现写入超时租户的内存使用率持续接近百分之百。诊断路径查看租户的内存分配MemStore的使用率在高峰期达到上限触发频繁的冻结和转储。转储速度跟不上写入速度MemTable的内存无法及时释放写入请求排队等待。根因分析memstore_limit_percentage设置为百分之五十在写入高峰期不足以缓冲峰值。租户的总内存配额也偏低无法通过调整分配比例来解决。调优动作将租户的内存配额从三十二GB增加到六十四GB将memstore_limit_percentage从百分之五十调整到百分之六十。同时将freeze_trigger_percentage从七十调整到六十让冻结更早发生避免MemTable逼近上限时才触发。效果验证租户内存使用率在高峰期回到百分之八十五的安全区间写入超时消失转储和合并的节奏恢复到正常水平。结语OceanBase的高效运维本质上是在LSM-Tree的写入节奏、SQL优化器的决策质量、并发控制的争用水平和资源分配的隔离程度之间持续寻找与业务负载匹配的平衡点。合并频率、计划稳定性、锁争用和内存配额这四个变量相互牵制任何一个的调整都会在另外三个上产生回响。一个可复现的调优流程应该遵循这样的顺序先用等待事件和SQL审计定位症状所在的层次再理解该层次的机制与参数之间的传导关系然后以最小的变更幅度做调整最后用数据验证效果并观察传导路径上的副作用。这个流程不依赖于对某个特定版本默认值的记忆而依赖于对机制的理解和对验证的坚持。当合并节奏与写入负载匹配时读放大被控制在可接受的范围当执行计划在统计信息更新后保持稳定时查询性能不会因为一次数据变化而突然劣化当锁争用被可观测性工具定位到具体的会话和SQL时并发问题从不可捉摸变为可处理当资源配额与业务峰值对齐时租户之间不会因为资源争抢而互相拖累。这些状态的达成是调优工作真正的成果也是运维从被动救火走向主动治理的标志。