6月23日,由 OceanBase 社区主办的 「深入浅出 OceanBase 第五期」直播专场已落下帷幕,感谢大家同我们度过了干货满满的一堂课!本期直播,我们邀请到了携程高级 DBA 台枫,为大家带来《携程 OceanBase 拟真压测系统深度解析》,以下为演讲整理内容。
嘉宾介绍:台枫,现就职于携程数据库团队,主要负责 MySQL 和分布式数据库,专注于数据库运维和工具开发。
在携程数据库系统从MySQL 转向 OceanBase 的过程中,为了将部分应用从 MySQL 平滑迁移到 OceanBase ,我们需要提前验证 SQL 兼容性和 SQL 执行性能。为此,我们设计了一套拟真压测工具, 模拟 MySQL 系统中的真实压力,对 OceanBase 进行压测。本文将深度解析携程业务的拟真压测工具,希望通过讲述携程将数据库系统从MySQL平滑迁移到 OceanBase 的实践,给大家提供参考和借鉴价值。
背景:大家都知道携程的数据库规模是比较庞大的,MySQL 占据了绝大多数 OLTP 场景的数据库。要想从 MySQL 转移到 OceanBase ,开发成本和运维成本都很高,对我们来说有很大的挑战。为了节省成本,我们需要考虑如何平滑地将应用从 MySQL 迁移到 OceanBase,考察重点是:评估当前业务在平滑迁之后是否存在兼容性风险和性能风险?
- 兼容性风险如语法的兼容,以及存储过程或触发器等其他对象的兼容。
- 性能风险主要是应用的响应时间,比如高并发场景下的数据库稳定性等。
以上是我们在做数据库拟真压测的背景。接着我来介绍一下具体的实现。
为什么要设计 SQL 拟真压测工具?
原因1:评测当前业务的 SQL 行为是否适合迁移到 OceanBase。
上文提到携程之前使用的是 MySQL 数据库,大部分线上应用不符合 OceanBase 数据库的特性。通过拟真压测工具,我们可以判断当前业务的行为是否适合OceanBase,是否有同样水平的 QPS、TPS 及响应时间。
原因2:为业务设计 OceanBase 分区方案提供依据。
大家都知道 OceanBase 能否用好的关键在于 OceanBase 的分区特性。当然,如何去用好这个特性,我们也正在摸索,而 SQL 拟真压测工具会设计一套分区方案,为我们提供判断依据和评测结果。通过对比并分析分区前后的具体性能,同时经过多轮的拟真压测,我们可以得到分区设计是否合理、分区方案是否可行、是否还能再优化及提高等问题的结论。
原因3:对新上线的业务起到 review 的作用。
对于一些业务新上线的 SQL,由于我们的开发人员对 MySQL接触更多,可能对 OceanBase 的特性了解得并不彻底。拟真压测工具可以帮助开发人员把新的 SQL 在真实环境中去审查、验证一番,判断它是否适合 OceanBase 的应用场景,顺便也可以对新上业务的SQL状态产生一个预期。
SQL 拟真压测工具介绍
业务 SQL 的采集
携程在业务 SQL 采集流程中,开启了 performance_schema,我们的采集进程如下图左侧,通过 performance_schema下面的 events_statements_summary_by_digest 表来得到一个概况分析,再通过概况分析得到一个大概的业务行为,通过这个业务行为得到 SQL 的执行比例。比如 A类 SQL,它执行的比例是60%,B类 SQL 的执行的例是30%。就可以得到一个大概的业务模型。而下图右侧所显示的是, performance_schema 下面的 events_statements_history_long是一个记录全量 SQL 语句的表,我们可以把这个表当成「近似全量」的存在,就可以得到一批具体的 SQL。通过实际得到的语句,然后生成一个回放 SQL 池。
可能有的公司并没有开启 performance_schema ,所以我再和大家介绍一下采集 SQL 的其他可行方案。
方案一是开启 general log,general log 的优点在于它是一个最精准、最全面的 SQL 日志记录方式,我们执行的 SQL 都会被它记录下来,但它对服务器的负载和性能影响比较高。而我们采用的 events_statements_history_long 表,虽然也会对性会产生一定的小波动,但相对而言是可以接受的。不过,上文也提到这个方案是「近似全量」,当业务 QPS 比较高的时候,我们无法做到「完整全量」。因为这张表会在数据达到一定阈值之后将旧数据刷走。
方案二是从 App 层面入手,比如,你接入了特别的框架或接入了自己公司的框架,可以从 App 层面直接把 SQL 采集出来,这个方案也是我认为是最优秀的方案。因为它的部署和数据采集是最轻的,对数据库的影响也是最小的。
如何提高仿真率?
回放 SQL 池
我们认为一个业务上的某些 SQL ,都是可以用一个 digest 来标记的,MySQL 中也是如此。我们把同样 digest 下面的 SQL组合,从中取 100 条组成一个池,在后续的压测过程中,我们都是从这个池子里面把一个 digest 的一个 SQL 随机抽取出来。
这样做的目的有两个,其一是为了保证多样性,其二是考虑到查询缓存,并不一定总是在生产环境中命中。
对于查询缓存,需要说明一点:如果我们一直采用同样的 SQL 进行压测,我们的请求始终是打在内存上的,这很难说符合一个真实的线上场景。
替换 SQL 参数
我们还会替换一些 SQL 的参数。在做数据采集及向压测库进行同步的时候,是有时间差的,举个例子,在 SQL 中,会存在一些时间戳字段,如果通过采集时的时间戳进行查询,可能会出现两种情况:第一种情况是查不到这个数据;第二种情况是查到的数据并不符合线上业务应该取到的数据,可能数据会变多或变少。无论是哪一种情况,都会影响仿真效果,可能会让 SQL 原本的执行性能上升或下降,这都是不可控的。
在这里我们采用一个方式把时间戳进行替换:SQL 中的时间+(全量同步时间-采集时间),在全量同步时间和采集时间中存在一个时间差,这个差值就是我们替换时间戳的一个参数。
计算 SQL 权重
计算 SQL 权重也是一个非常关键的存在。因为我们在压测时,是对整个业务行为进行模拟,而不是对单条 SQL 语句的性能去排查。如果你要单独查询某一个 SQL 性能语句,其实用更加简单的工具就能够完成这个操作。在计算 SQL 权重后,我们会将(A SQL 占比60%,B SQL占比30%) SQL 一起放置到 OceanBase 或 MySQL 的 Server 上。这样的压测过程是比较真实、还原的。
过滤 MySQL 独有的一些语句
MySQL 会有一些系统表,而这些在 OceanBase 中是不存在的,或者在查询的时候它比较慢(在 OceanBase 中兼容 MySQL 的系统表一般都是 OceanBase 系统库下面的一些映射,这或许是查询比较慢的原因),再或者说不太会运用到这些表情况下。我们需要把这些这些语句剔除出我们的压测环境,它是不真实存在的一种场景。
SQL 兼容性评估
兼容性评估流程相对简单。选取 SQL 池中任意一个 SQL,将其放到回放 Server 上,在 MySQL 侧和 OceanBase 侧分别回放。回放以后就可以得到一个返回的结果,通过比较这两侧的结果就可以知道这条 SQL 在 MySQL 和 OceanBase 是否兼容。
SQL 兼容性评估需要评估以下几点:
- 语法兼容性。包含函数是否支持、语法是否报错等;访问 OceanBase 不存在的一些系统变量,都有可能导致语法性报错。
- SQL 是否超时。在压测之前,会先对 SQL 进行超时评估,防止后续在压测过程中 SQL 影响到整体的性能。先把 SQL 挑出来,挑出来的目就是为了保证 SQL 在 MySQL 上能跑出来的,在 OceanBase 上也能跑出来。如果跑不出来,则说明业务场景可能需要优化,或者考虑 OceanBase 侧可能要对分区方案和索引进行一些优化改动来解决这个问题。
- 是否存在非标 SQL。这会和我们的业务场景更加耦合。以跨库查询为例,我们的目的是为了迁移 A-DB 到 OceanBase ,A-DB 发现它与 B-DB 耦合,有一些跨库查询,关联到了 A-DB 和 B-DB。在这个场景下,我们只迁移 A-DB ,只对 A-DB 场景进行评估,发现它是 OK 的。但在迁移评估完成后,发现 A-DB 和 B-DB 之间存在耦合,业务就报错了。因此,在一些特别的场景,我们也会在 SQL 兼容性评估中将它特别对待、特别分析。
SQL 压测评估
上图为压测评估的架构,第一层是 LOCUST 发压工具,发压工具把流量给到一个接入标准框架的 App。再将 SQL 请求发给 MySQL 侧和 OceanBase 侧。这个架构也存在一定的问题,当我们在具体的场景中,无论是应用、OceanBase、MySQL 都存在一个高可用方案,在高可用方案下很多应用可能是分机房部署的,MySQL 是有主从概念的, OceanBase 也会有多节点部署、多机房部署的概念。我们就要去考虑一些网络上的问题。在 MySQL 侧我们采用的方案是: 应用只访问 MySQL 的 master 节点。在 OceanBase 侧,采用了副本 leader 集中在单 zone 场景,比如,三个 OceanBase 有三个 zone,我们将 primary_zone 设置到同一个zone上。这样和 MySQL 侧也是完全对称的,在保证这样的一个迁移后,哪怕是 OceanBase 使用单节点,我们也能扛住业务流量。
在应用侧,也是分机房部署的,在应用测访问 MySQL 时,存在一侧本地访问,另一侧跨地域访问。但在 OceanBase 侧,线上的策略是就近访问,我们也需要在分机房的情况下,把 OBProxy 部署上去。通过就近访问本地的 Proxy 再路由到实际 OceanBase 的 primary zone 上,就比较符合我们的业务场景。
SQL 压测时如何按照等比例回放?
在 SQL 压测时如何按照等比例回放?我们采用了 LOCUST 发压工具 。LOCUST 是 Python 开源的压测框架。在这个框架中有两种方式来实现 SQL 等比例回放,第一种方案是在 LOCUST 启动时有个预热的过程,会把 user 数量一步步追加,每一个 user 提上去会有一个时间差。用一个简单的循环语句去循环编译这些压测的 SQL,让它按比例跑。通过 user 的时间差,可以仿真出一个等比例的回放方案。
第二种方案是: LOCUST 本身支持多任务,在多任务中支持权重配比,通过配置权重配比也可以实现这个过程,权重配比和方案对比而言,虽然它的仿真度更高,但可能需要一直重启 LOCUST 产生任务的脚本,这两个方案各有优劣,可以根据具体的场景来考虑使用哪一种方案。
LOCUST 部署场景
LOCUST 本身是支持分布式的,LOCUST Master 节点通过调度将压力下发至 Worker 机,Worker 机向下发压至 URL 层。本质是对 HTTP 协议的压测。 URL 层做了 SLB,在压测后 SLB 把请求分发给不同的实际的 App节点, App 节点再去访问底层 DataBase。
LOCUST 扩容:在 LOCUST 旁边直接加一个 Worker,把 Worker 指向 Master节点,那么它的扩容就完成了。当需要更大压力时,直接扩容一个节点就可以。
LOCUST 高可用:只需要采用一个 VIP,将 LOCUST Master 迁移走即可,始终指向这个VIP也是可以实现的。
SQL 压测指标
应用侧
应用侧最关心的是响应时间、QPS 等指标。
作为一个分布式数据库,除了数据库层面的耗时外,还需考虑网络层面的耗时。因此,我们要关注的响应时间是在 App 侧的总耗时,以此来评判数据库性能是否符合我们的预期。
QPS 和响应时间其实差不多,主要保证吞吐量,在同样的压力下 QPS 高则更优秀。
服务侧
在服务侧,我们检查的是 CPU 偏移量、IO 偏移量、网络流量。为什么采用偏移量,而不是一个绝对值呢?OceanBase 作为一个分布式数据库,下面会有很多线程去做其他的操作。比如 election 操作、leader switch 操作、 rebalance 操作等,它的底层会占用 CPU ,并不是一个绝对的量。IO 偏移量同样是这样的道理,通过和 MySQL 进行比较,来评价这个服务是否良好。网络流量也是一个关注重点, OceanBase 的网络流量相对于单机数据库 MySQL 来说是比较高的,网卡配置是否顶得住网络流量也是我们要考察的重点。
SQL层
在 SQL层,我们进行了 MySQL 和 OceanBase 的耗时对比。具体 SQL 耗时可以等效的认为是接口耗时的差值,在这中间将网络时间进行了剔除。SQL 执行的差值,如果达到了一定的阈值,就要把 SQL 摘出来进行具体分析,来看 SQL 是否存在一些问题。在 MySQL 和 OceanBase 上,为什么会存在这么大的差距,是不是写的方式不对,或者没有运用到 OceanBase 的一些特性。
接口的耗时比例也是我们关注的重点。比如一个 SQL 在 QPS 很高的场景下,它的耗时占比突然间上升了很多。那就需要好好关注一下为什么这些 SQL 在这种场景下会存在问题,这些 SQL 也会被单独拎出来进行具体的业务分析。
拟真压测的问题
Insert 语句携带业务主键
在 MySQL 中大部分的业务场景都使用自增键。因为 MySQL 是 B+ 树的存储结构,采用自增键是有好处的。但有一些场景采用的是业务主键,采用业务主键时,在压测时就会存在很多问题。如果我直接把这个语句拿过来,去跑实际的插入,就会出现主键冲突,在压测工具这一环就存在大量报错。
我们之前设计的一个方案是怎么解决呢?就是把业务主键替换成时间戳加 6 位随机数。但这个方案也有一定的问题,当 QPS 特别高的时候,还是会出现主键冲突,但是这个主键冲突我们暂时是可以接受的。
另外一种方案是替换成 replace,replace 具体的实现逻辑和Insert是有区别的,所以并不一定可以达到实际的测试效果。
update/delete 是否需要转换成 select
以下两种情况都会在压测周期中影响仿真效果。
- 如果我们 update 了一行数据,在下一次 update 的时候,可能跑的就是空的。
- 第一次 delete 完成,第二次 delete 的时候,对应的行可能已经被删除了。
我们也在考虑能否把 update 和 delete 转换成select,对于我们来说这并不是一个特别好的方案。OceanBase 的写操作始终是向内存追加的顺序写,MySQL 大部分情况是随机写,这两个写入方式的差异是我们把 select 替换成 update 和 delete 时测试不到的。
业务层使用 prepare statement
使用 prepare statement 在我们的数据库业务场景中,其实并不多见。但有的公司可能会用的特别多。我之前迁移过一个 C++ 的应用,也使用了 prepare statement 模式。在 prepare statement 模式下,其实是不支持拟真压测的。首先我们采集的 events_statements_summary_by_digest 这张表是不包含 prepare 模式的。其次,prepare statement 具体的实现和直接执行 SQL 也有区别,它是一个传参的模式。另外,这种模式在我们当前的使用场景下也不太常用。
总结
拟真压测工具在我们从 MySQL 迁移到 OceanBase 中扮演了很重要的角色。就兼容性结果而言,OceanBase 对 MySQL 语法的兼容度是相当不错的,这保证了迁移时的平滑进行,对代码的改动成本很低。
另外,在性能层面上,就目前我们的大部分应用场景来看,在集群50000qps的点查和小范围查询场景下 OceanBase 和 MySQL 具备相近的性能,这保证了迁移后的性能不容易出现波动。
附录:
携程经验分享:MySQL数据同步OceanBase时DDL遇到的问题
直播回放:携程 OceanBase 实践数据库发布和拟真压测
————————————————————————————————————————————
社区版官网论坛
社区版项目网站提 Issue
欢迎持续关注 OceanBase 技术社区,我们将不断输出技术干货内容,与千万技术人共同成长!!!
搜索🔍钉钉群(33254054),或扫描下方二维码,还可进入 OceanBase 技术答疑群,有任何技术问题在里面都能找到答案哦~