前阵子接手了一个数据库兼容性验证的测试任务对象是某分布式数据库以下简称 ob业务侧是公司一个核心交易模块的模拟项目。整个测试周期接近三周从环境部署、功能验证、长时间稳定性压测到性能比对整个过程踩了不少坑也沉淀了一批问题记录。这篇就是把我在 ob 测试过程中的真实问题、排查路径和最终的解决方案整理出来给后面做同类数据库测试的兄弟们一个参考。先说清楚这篇文章适合谁看如果你正准备做数据库替换或兼容性验证或者你是 DBA 想了解测试阶段会遇到哪些典型问题又或者你只是刚开始接触分布式数据库、想提前避坑那这篇的内容应该能帮到你。我不讲教科书式的架构原理只讲测试现场的真实问题每个问题都按“现象-排查-根因-解决”的结构展开方便你照着定位。1. 测试环境与整体部署思路1.1 测试环境的搭建方式这次测试我选择的是三台物理机部署一套 ob 集群每台机器的配置是 16 核 32G 内存、SSD 磁盘操作系统是 CentOS 7.9。部署工具用的是 obd这也是目前 ob 官方推荐的方式整个部署过程比手工编辑配置文件要省心很多。关于副本数我一开始其实纠结过是单副本还是三副本。单副本部署简单、资源利用率高但很多分布式一致性问题测不出来三副本能还原生产环境但测试机器的内存会吃紧。考虑到这次测试的目标之一是验证数据一致性最终选择了三副本。这里有个经验如果只是功能验证单副本足够如果要做高可用或故障注入务必三副本起步。部署完成之后需要验证一下集群状态。使用 obd cluster list 查看集群列表再确认 observer 进程是否正常拉起。我在第一次部署时就遇到过 observer 进程反复退出后面会单独讲。1.2 测试对象与验证目标被测的业务系统是一个模拟订单系统包含用户、订单、支付流水三张核心表日常操作以订单写入和按用户维度查询为主。这个系统的 SQL 风格偏向传统关系型数据库用了不少自增列、多表 JOIN 和分页查询对兼容性测试来说覆盖度还算典型。测试目标分为四层功能兼容性原系统所有 SQL 能否在 ob 上正确执行返回结果与预期一致语法兼容性存储过程、触发器、序列等对象能否平滑迁移稳定性验证长时间运行下内存、CPU、磁盘是否有异常增长性能基线对比同结构数据在原数据库与 ob 上的关键 SQL 响应时间。明确了目标之后测试过程就围绕着这四条主线展开问题记录也按照这个分类归档。1.3 问题分类的整体思路测试周期内一共记录了 21 个问题我按阶段做了一个分类问题类型数量典型现象部署配置类4进程异常退出、端口冲突、资源单元不足SQL 兼容性类9隐式转换失败、分页报错、锁等待超时稳定性类5内存缓慢增长、租户内存不足报错性能异常类3慢 SQL、执行计划选择异常其中 SQL 兼容性类占比最高这也是分布式数据库替换场景中最容易踩坑的地方。后面几个章节我按这个分类逐个展开。2. 部署与初始化阶段的关键问题2.1 observer 进程反复退出的一次排查第一次部署完成后用 obd cluster status 查看observer 状态显示为 active但过了几分钟再看进程就退出了。日志目录下 observer.log 里大量报错关键的几行是网卡信息获取失败和端口绑定异常。排查思路是这样的先确认端口是否被占用。ob 默认使用 2881SQL 连接端口和 2882内部通信端口同时 observer 进程自身还会监听一个 RPC 端口范围默认是 2882 到 2884。我这次的问题就在于没有关闭防火墙导致 observer 监听端口失败进程反复重启。解决方法是把相关端口加入防火墙白名单或者直接关闭防火墙。这里补充一个容易忽略的点ob 检查端口占用用的不是简单的 bind 测试进程启动时会对所需端口做完整绑定校验。如果某个端口被占observer 不会降级运行而是直接退出。所以遇到“进程反复重启”的问题第一反应应该是去看日志里的 bind 相关报错而不是去调系统参数。2.2 租户资源单元规划的教训测试过程中遇到过一个非常典型的问题创建租户时报资源不足具体错误是 unit 数量不够或资源超卖。ob 的租户资源模型是这样的创建租户时必须指定 resource unit资源单元这个 unit 规定了租户能使用的 CPU、内存和磁盘上限。测试环境三台机器各 32G 内存但集群的 sys 租户默认占了一部分剩余可用内存被其他租户申请完后再创建新租户就会提示资源不足。我当时没有仔细查集群剩余资源直接复制了文档里的示例 unit 配置导致反复失败。后来通过查询 __all_resource_pool 视图确认了已有资源池的分配情况重新规划了内存配比才解决。实操命令参考-- 查看资源池分配情况 SELECT * FROM __all_resource_pool\G -- 查看 unit 配置 SELECT * FROM __all_unit_config\G规划资源时建议留出 20% 的内存余量不要全部分配给租户。ob 的合并操作、SQL 执行计划缓存、系统内部任务都需要额外内存如果分配过满后期很容易出现内存不足的错误。2.3 时间同步与参数初始化还有一个很低级但影响很大的问题是时间同步。分布式数据库对节点间的时钟偏差有要求如果时钟偏差超过阈值会导致选举异常、事务提交超时。我这次部署时只在一台机器上配了 NTP另外两台时间漂了十几秒结果执行简单的插入操作都出现超时重试的告警。排查方法很直接在三台机器上分别执行 date 命令对比时间偏差超过 100ms 就需要处理。建议在测试环境初始化时就把时间同步做成标准化步骤不要等出了问题再补救。参数初始化方面常用的一些调优参数可以提前设置。比如ALTER SYSTEM SET memory_limit_percentage80; ALTER SYSTEM SET freeze_trigger_percentage50; ALTER SYSTEM SET major_compact_trigger5;memory_limit_percentage 是 observer 进程可用的总内存占物理内存的比例测试环境我设为 80%留一部分给操作系统。freeze_trigger_percentage 控制内存中增量数据达到多大比例时触发转储这个参数对长稳测试影响很大后面稳定性部分会细说。3. SQL 兼容性测试中的典型报错3.1 字段类型与隐式转换的坑第一个接入业务的 SQL 就报错了场景是查询订单表时用了一个字符串条件和数字字段做比较原数据库可以正常执行但 ob 直接报错。定位到具体的 SQL 后发现原库对隐式转换的容忍度很高数字字段和字符串常量比较时能自动转换ob 在默认的 SQL 模式下对这类场景处理得更严格部分比较操作必须显式转换类型。比如原逻辑可能是SELECT * FROM orders WHERE order_status 1;如果 order_status 是数字类型ob 某些版本会直接报错或返回不了正确结果。这个问题不能只在 SQL 层面打补丁因为业务代码里可能有几十处类似写法。正确做法是梳理所有涉及类型比较的 SQL统一修正映射关系在应用层适配。这里给个建议测试前先用元数据对比工具把原库和 ob 的字段类型映射完整过一遍重点看字符类型、时间类型、数值精度这几个维度能提前拦截掉 80% 的类型兼容性问题。3.2 分页查询与行号处理的差异分页查询的兼容性问题也很有代表性。原系统很多报表查询用了 row_number 窗口函数来做分页和 MySQL 的 limit 语法混着用结果在 ob 上出现了结果集重复和分页错乱的现象。排查后发现问题出在 ORDER BY 字段没有唯一性约束上。分页查询如果排序字段有重复值分布式数据库在不同节点上返回的数据顺序是不确定的就导致翻页时出现重复或丢失。解决办法有两个一是给 ORDER BY 增加唯一字段比如在排序条件末尾加上主键二是改用更稳定的键集分页方式。如果业务侧对分页的绝对稳定性要求很高建议在 SQL 改造阶段就统一规范分页写法不要依赖数据库的默认行为。3.3 字符集与排序规则不一致导致索引失效这个问题的隐蔽性比较强。测试中发现某条查询走了全表扫描性能表现比预期差很多。查看执行计划后发现表字段的字符集是 utf8mb4但连接客户端的字符集设置是 gbk导致查询条件上的字符串比较无法走索引。ob 的规则是索引字段的比较要求字符集和排序规则一致不一致时优化器会放弃索引扫描。这个问题在功能层面没有任何报错只在性能层面暴露出来如果不做执行计划分析很容易被忽略。处理方式是在应用连接串中明确指定字符集jdbc:mysql://127.0.0.1:2881/order_db?useUnicodetruecharacterEncodingutf8同时也建议在测试开始前把库表、连接、客户端三层的字符集统一设置为 utf8mb4避免这种隐性性能损耗。3.4 事务隔离级别与锁等待超时订单系统的写入并发较高测试过程中开始出现锁等待超时报错错误码对应的是锁等待超时。排查的时候需要看两个关键信息一是当前活跃事务的持有锁情况二是等待锁的会话。通过查询内部视图拿到锁等待关系后发现是某个定时任务的事务长时间未提交导致业务更新同一行数据时一直等待。根因不是 ob 的锁机制有问题而是应用侧的事务控制不规范。排查后发现是存储过程里有一个游标循环循环内做了事务提交但没在异常分支做回滚异常时事务一直悬挂。这个问题在单机数据库上也可能出现但分布式数据库上锁等待的代价更高影响面更大。同步调整了锁等待超时参数ALTER SYSTEM SET ob_query_timeout10000000; ALTER SYSTEM SET ob_trx_idle_timeout120000000;不过这里也要提醒调大超时参数只是权宜之计根本解法是确保应用的事务能及时提交或回滚同时把大事务拆成小事务。事务粒度越大分布式数据库的协调成本越高锁冲突的概率也越大。3.5 自增列与序列行为的差异自增列的语义差异也值得记录。原系统依赖自增列作为订单号的一部分插入时使用了 insert 语句但没有显式指定自增值在 ob 上测试时发现高并发下自增值跳变很大而且部分场景下自增值顺序和插入顺序不完全一致。这是分布式数据库的常见行为为了保证全局唯一自增列的值分配机制和非分布式数据库不同无法保证严格连续只能保证唯一和递增。如果业务对自增值的连续性有强要求建议调整设计比如改用 UUID 或雪花算法生成业务主键把自增列只保留为数据库内部标识。在兼容性测试报告中这个差异需要明确标注为“行为差异”并评估对业务的实际影响。实际验证下来对于订单系统来说自增序列跳变并不会影响正常业务流程但会影响一部分依赖自增值做排序展示的逻辑需要应用代码做适配。4. 长时间稳定性测试与资源问题4.1 内存持续上涨与租户内存不足问题稳定性压测进行到第三天开始频繁出现内存不足的报错错误信息指向内存分配失败。查看监控后发现租户内存使用率已经接近 100%并且在整个压测周期内呈持续上涨趋势。这里先说明一下 ob 的内存模型。ob 的租户内存分为几个部分增量数据内存memstore、SQL 执行内存、计划缓存、以及其他系统开销。增量数据会随着写入持续增长达到 freeze_trigger_percentage 设置的阈值后触发转储把增量数据落盘释放内存。如果转储速度跟不上写入速度内存就会持续上涨直到触发内存保护机制。我这次出现内存持续上涨主要是两个原因造成的一是写入并发太高转储来不及释放二是转储阈值设置得不太合理导致增量数据长期占用高水位。处理方式分两步-- 查看转储和合并相关信息 SELECT * FROM __all_virtual_table_mgr\G -- 手动触发合并 ALTER SYSTEM MAJOR FREEZE;调整参数后观察内存水位能明显回落到安全区间。更重要的经验是在压测前就要结合预估写入速度设置合理的转储阈值不要等内存告警了再去处理。另外压测期间要关注每日合并是否正常完成合并异常是内存上涨最常见的隐形推手。4.2 合并与转储对测试结果的干扰另一个容易被忽视的问题是ob 的合并操作会对测试数据产生干扰。合并期间会占用大量 IO同时在合并过程中部分查询的响应时间会明显变长如果不记录合并时间窗口很容易把合并期的性能数据当成正常数据导致性能分析出现误判。我在测试过程中第一次做性能对比时就吃了这个亏。某天下午压测结果整体比前一天慢了很多怎么排查 SQL 和参数都找不到原因后来才发现是因为那个时段正好撞上了每日合并窗口。后来所有的压测脚本里都加了时间标记结合监控数据能准确识别合并窗口分析数据时单独排除。对于需要长期对比的性能测试建议记录下每次合并的时间点分析时打标处理。4.3 慢 SQL 与执行计划选择异常慢 SQL 问题在测试第四天集中暴露。有几条查询在数据量增长后性能急剧下降单条 SQL 的响应时间从几十毫秒涨到几秒。看执行计划后发现问题主要有两类一是统计信息不准确导致优化器选择了错误的 JOIN 顺序二是索引选择错误优化器没有走更优的二级索引而是走了主键扫描加回表过滤。ob 默认开启统计信息自动收集但有些场景下还是需要手动干预。我的处理方法是先分析慢 SQL拿到实际执行计划对比优化器估算的行数和实际行数对统计信息不准的表重新收集统计信息如果优化器仍然选错使用 outline 绑定执行计划。这里有一点值得强调分布式数据库的执行计划选错影响往往比单机数据库更大因为一个错误的分布式 JOIN 计划可能导致数据在节点间大量传输。测试阶段发现慢 SQL不要只盯着索引优化先看执行计划中的分布式算子是不是合理的。4.4 磁盘空间与日志膨胀日志磁盘被写满的问题在测试后期暴露了一次。ob 的日志盘默认保留了最近的 commit log在持续高并发写入场景下commit log 的膨胀速度非常快。我们的数据盘和日志盘是共用目录导致磁盘使用率快速攀升。最直接的解决方式是把数据目录和日志目录分盘存储在生产环境这是必须的。测试环境如果条件有限至少要做到磁盘容量预估时把日志空间单独算一笔账。另外observer.log 也会持续记录运行日志加上 trace 日志长时间运行后也能积累到 GB 级别。建议在测试环境中配置日志轮转策略避免日志文件无限制增长。我设置的日志归档策略是运行日志按天切分保留最近 7 天commit log 所在的日志盘预留 30% 的余量。这个问题的隐蔽之处在于它不直接报错而是表现为集群性能逐步下降直到磁盘满了之后写入全部失败。如果测试过程中没有磁盘监控排查起来会绕很多弯路。5. 问题定位方法论与监控建议5.1 先建立“问题快照”意识整个测试周期下来我最大的感受是数据库测试的问题排查最忌上来就调参数。正确流程应该是先做问题快照把当时的现场信息完整记录下来再开始分析。问题快照包含以下内容问题发生的时间点当前集群状态所有节点的 observer 状态相关租户的资源使用情况活跃 SQL 和慢 SQL 列表最近的告警日志和错误日志如果涉及性能问题还要记录当时的压测并发数和吞吐量。有了这些信息才能判断问题是偶发还是必现是单个节点问题还是集群问题。我这次排查的很多问题比如内存持续上涨和锁等待超时都是在拿到问题快照之后才快速定位到根因的。sys 租户的登录方式也很关键排查问题时需要切到 sys 租户下查看内部视图。连接命令参考obclient -h127.0.0.1 -P2881 -urootsys -p这里补充一个实操技巧obclient 是 ob 自带的命令行客户端用法和 MySQL 客户端相似但要注意默认字符集和 SQL 模式的差异。如果从 MySQL 客户端切过来有些查询结果展示的格式不太一样。5.2 常用视图与命令速查排查过程中经常用到的视图和命令我整理了一张速查表用途命令或视图查看集群状态obd cluster list / status查看所有节点资源__all_server查看租户信息__all_tenant查看资源池分配__all_resource_pool查看活跃 SQL__all_virtual_sql_audit查看慢 SQL__all_virtual_sql_audit WHERE elapsed_time 阈值查看锁等待__all_virtual_lock_wait_stat查看内存使用__all_virtual_memstore_info手动触发合并ALTER SYSTEM MAJOR FREEZE;查看分区分布__all_virtual_partition_location其中 __all_virtual_sql_audit 是排查慢 SQL 最常用的视图记录了每条 SQL 的执行时间、等待时间、返回行数、扫描行数等关键信息。我排查慢 SQL 时基本都会先看这个视图找到耗时最高的 SQL再结合执行计划分析。5.3 如何区分数据库问题和应用问题这个问题值得单独说一下。测试过程中遇到的报错并不都是数据库本身的问题有一部分是应用侧的 SQL 写法、事务控制或连接管理不规范导致的。区分的判断依据报错是偶发还是必现必现问题优先查 SQL 和参数偶发问题优先查连接、网络和资源报错集中在某个模块还是全局集中在某个模块多半是应用代码问题全局问题多半是集群或资源问题相同 SQL 单次执行正常、并发时报错大概率是资源竞争或锁问题重启 observer 后问题消失大概率是长期运行导致的状态累积比如内存碎片或日志膨胀。掌握这个判断框架后和业务团队沟通的时候会高效很多。有些问题如果一开始就往应用层推最后发现是数据库行为差异来回拉扯浪费的时间比排查本身还多。6. 回归验证清单与测试收尾6.1 回归验证的基线要求测试周期后半段进入了回归验证阶段。这里说的回归验证不是简单地执行一遍用例而是要确保之前修复的问题没有引入新的风险。我是按这个基线来的所有已确认问题均完成复测验证修复效果关闭问题后跑一轮全量功能用例关键业务路径的 SQL 执行计划与基线对比无劣化长时间压测下资源水位与修复前对比有明显改善。回归验证过程中我还会再跑一遍核心 SQL 的执行计划和第一阶段记录的基线做对比。这一步很有价值因为分布式数据库的执行计划有时会随着统计信息更新而变化如果不做对比很可能测试结束时性能和测试开始时不一致但没人发现。6.2 按模块拆分验证清单回归验证需要分模块做不能一把梭全量跑。建议按以下维度拆分功能兼容性全部核心 SQL 用例执行通过结果集与原库比对一致存储过程、函数、触发器的行为与原库对齐序列和自增列在并发场景下行为稳定。稳定性7 x 24 小时压测无新增报错内存水位在转储节奏正常的前提下保持在安全区间日志磁盘无异常增长。性能关键 SQL 响应时间与性能基线偏差在可接受范围内慢 SQL 数量无增长合并期间性能波动符合预期。按模块拆分验证清单的另一个好处是测试报告可以按模块输出结论方便业务团队针对性评审。6.3 测试结束标准与收尾工作测试结束的标准不只是“没有报错”而应该是一套可量化的判定条件。我这次总结的结束标准是核心 SQL 用例通过率 100%稳定性压测期间无单点故障性能基线达到业务预期所有已发现问题均有关闭结论。测试结束后的收尾工作也不能省导出一份完整的测试报告包含问题清单、影响分析、修复建议整理一份 SQL 兼容性改造建议交给应用团队备份测试环境的关键配置方便后续追溯清理测试过程中产生的临时表、测试数据避免污染后续测试。测试收尾其实还有一个容易忽略的点需要把测试过程中修改过的所有参数汇总成清单标注哪些是临时调整、哪些是建议长期保留。否则测试结束之后环境参数状态和文档对不上后面再有人接手就会很头大。最后再分享一点个人体会。数据库的测试工作最核心的能力不是会用多少工具而是面对一个报错时能快速判断问题的方向——是兼容性问题、资源问题还是应用问题。这种判断力只能靠实际踩坑积累。如果你刚开始做类似测试不用怕出问题尽量把所有现象都记录下来哪怕是暂时解释不了的现象。等到问题积累多了你会发现很多看起来独立的问题其实背后都有共同的规律。希望这篇测试问题记录能帮你少走一些弯路。
阅读完成 · 觉得有帮助?