为平衡ROI、保证服务稳定性,部门整体进行去O;在数据库选型上,平衡存储、查询等等性能后,合规中心选择迁移到TiDB运行,在迁移过程中,AP场景的迁移遇到了诸多挑战。
0 背景
0.1 业务背景
合规中心设计与实现:《网易支付合规中心数据应用体系建设》
为平衡ROI、保证服务稳定性,部门整体进行去O;
合规中心为面向合规上报、检查等场景建设,使用关系型数据库oracle搭建,因此需要对数据和服务进行迁移。
0.2 数据库选型
监管对第三方支付机构的数据流入流出有严格要求,因此网易支付无法选用其他(集团内或集团外)服务商提供的云服务/分布式服务,必须使用自有环境。
需要满足下面几个使用场景:
支持有数可视化连接
按主键查询/更新
支持10T级别存储和KW-10KW量级大表存储
支持like查询
join分页查询RT要求秒级
支持多字段group by,单表group by RT要求秒级,join group by RT要求小时级
选型对象有3个:ClickHouse,TiDB,Hbase
| 数据库 | ClickHouse | TiDB | Hbase |
|---|---|---|---|
| 存储格式 | 列存储 | 行存储、列存储 | 列族存储 |
| 事务 | 不支持 | 支持 | 行级事务 |
| join | 分布式Jion支持差 | 支持(性能预估mysql一半,太多表关联不建议) | 支持差(需插件支撑,且性能差) |
| 行删除 | 支持,不擅长 | 支持 | 支持,性能低 |
| 行更新 | 支持,不擅长 | 支持 | 支持,性能低 |
| 不支持特性 | 不支持事务;不支持真正的删除/更新;几乎不支持分布式Join(ClickHouse的定位是 分析性数据库,而不是严格的关系型数据库;在分析模型上仅支持大宽表模式;) 没有强类型校验,数据可能被内部机制进行截取,造成数据不一致;对高基数列进行精确去重操作时,受限于单点聚合的处理方式,性能瓶颈明显; | 不支持存储过程、视图、触发器、 自定义函数、外键约束、全文索引、非 UTF8 字符集 | HBase不适用于有join,多级索引,表关系复杂的数据模型;读性能低下,百毫秒级(一次范围查询可能会涉及多个分片、多块缓存甚至多个数据存储文件;); |
| 运维支撑 | 支持(支付DBA无经验,可支持) | 支持 | 支持 |
| 有数对接 | 支持 | 支持 | 支持(需集成Phoenix) |
| 建议场景 | 日志分析、宽表分析,OLAP | OLTP 同时兼顾OLAP(OLAP较弱) | OLTP(非结构化数据,noSql) |
| 优势 | 简单查询高吞吐量 | 数据更新、删除支持较好,且支持动态资源伸缩 | 写入高吞吐量(瞬时大量写入,逻辑写入) |
合规库存在逻辑关联、多字段Group By、偶尔有like情况;初步选型TiDB,基于账务数据参考,千万级数据做关联查询时间秒级,千万级数据做多字段GroupBy耗时10+秒,符合需求;
1 待迁移任务
功能应用主要使用kafka消息落表,迁移主要将kafka消息落oracle表切换到落tidb表。
统计应用主要使用python通过jdbc连接向数据库抛出sql脚本并执行,共计31个脚本;迁移的工作量和难度都比较大。
最终资源沟通、任务对接、31个脚本的改写/拆分/新增/合并、运行测试、数据校验共花费了12人日(包含超出预期的3人日,用于以批处理替代持久化问题的讨论、设计、实现、测试和验证)。
2 TP场景迁移
2.1 表迁移前件
具备以下2个条件,可以使用内部工具NDC进行数据迁移传输:
oracle 表必须要有主键或者唯一键
oracle 表不含lob字段
其他场景需要新增历史数据传输+增量搬运merge
2.2 表迁移校验
表的校验沿用了数仓的经验,即条数校验、枚举校验、随机明细对跑。
底层表的迁移可靠性传输工具(NDC)、开发监控(kafka报警),人工校验(前面3板斧),在本次TiDB迁移中是足够用的。
3 AP场景迁移
在TP场景,KW到10KW数据级上,不管是聚合还是单层关联,TiDB的表现与oracle相差无几;由于作者主要负责统计应用(也就是AP场景应用)的迁移,遇到的问题和瓶颈也主要集中在AP场景,因此着重介绍AP场景的迁移和问题处理。
3.1 调度结构
依然保持使用虚拟机crontab部署定时任务(python脚本)的主要形式。
但由于实测tidb在AP场景下使用不如oracle可靠(试运行阶段就出现过因为其他user起了长事务,导致整个服务器宕机重启),因此使用一个py脚本外包了一层,强制保证所有的任务都是串行执行。
3.2 语法变更
TiDB的SQL语法与MySQL基本一致,其实相当于Oracle SQL语法变更为MySQL SQL语法。
主要涉及的部分包括时间格式转换函数(to_char、to_date),取当前的变量(sysdate)、时间日期加减法(add_months等)和更新语法(merge)。
好消息是本次使用的TiDB版本与MySQL 8基本一致,支持CTE(公共表达式),因此原先oracle中大量采取的CTE写法不必做出太大改动。
坏消息是不支持create as 的持久化语法,这点在3.3中会提到。
3.3 持久化策略
在长事务优化中,持久化是相当有效的一种形式。
通过把存放在内存里面的临时计算结果写入硬盘,将一个复杂事务拆解为若干层的简单事务,在空间和计算中做权衡。
如果觉得上面那段话太过晦涩,不妨等价为create as语法创建的临时表。目前的TiDB版本均不支持这一语法。这意味着所有的计算都必须一把梭哈,没算出来就GG。
迁移实践中出现过这样的场景,4个CTE中有2个涉及实时变更的巨(10KW级)宽表,1个涉及数据量比较大(10KW级)的日志表,且根据中间结果进行二次判断后聚合,就完全无法跑通。
而且还收到了DBA无数的谆谆劝导,不要提交太多长事务
3.4 批处理策略
由于不支持持久化策略,因此必须在依赖表和产出结果中间人工搭建一层中间表,且通过某个时间字段触发小批量写入和更新。
在oracle中也有一小部分任务存在这种设计,但oracle中触发200W的更新,基本秒级可以完成;在TiDB中,超过50W的更新就会导致跑批失败。
批的大小其实是可以通过参数控制的,只是出于运维稳定性考虑,这个参数不会给的太大
这种想法其实就非常接近离线数仓“分而治之”的理念了,把大活拆成小活,把小活拆成小小活。只是在离线数仓中,负责实现的是更底层的设计,在TiDB中要自己手工实现。
最终是将2个统计脚本拆解为3个小批量明细更新脚本 + 2个统计脚本。
3.4.1 批处理带来的问题
当然,批处理也带来了一些问题。
写脚本更新数据这事儿更接近工程而非统计,作为分析师,作者的手未免伸得长了些。
实现设计和运行测试、数据校验虽然通过了,但并没有一个稳妥的死任务拉起机制——如果有任务失败了且没有重新拉起,就永远有一拨数据是错的。
为了解决这个问题,新设计了一个脚本用于记录调起任务的结果、耗时和报错;同时又新增了一个巡检通知脚本,用于检查日志中是否存在错误,捕获到错误记录就触发邮件通知。
由于合规中心的上报时效在T+1和T+30之间浮动(月初才上报),且作者的工程能力并不能很好地处理并行调度中的冲突,因此触发通知之后不做任何处理。
等上班来check邮件,发现抛出了异常,人工拉起当天所有调度的重跑 :-)
3.5 最终运行效果
迁移前31个脚本在oracle运行花费约45min;
迁移后进行了部分脚本的简并、重构、新增,也对部分脚本进行了拆分,共计33个脚本在tidb串行执行,需要约3小时。
4 总结
4.1 关于TiDB
离线分布式数仓大规模采用 hive/hadoop 的解决方案不是没有道理的。
在TP场景,TiDB的使用基本满足所有需求;
但是在AP场景则一言难尽:作为一个主要在AP场景应用的分析师,必然涉及到大量复杂关联和计算,没有办法按照TP场景的要求只提交小事务;且这类需求时效要求基本都在天级别,先交给工程开发新表再进行统计也不大现实。
因此,如果没有类似hive这样成熟的方案,而且出现了KW以上的3层以上关联,也没有比较完备的数仓基础建设(指AP场景需求完全可以通过2张及以下的宽表关联聚合统计完成),同时对交付时效还有较高的要求,是不太建议在AP场景上TiDB的。
4.2 关于迁移时效
12人日内将合规中心数据应用全部迁移去O工作完结,个人觉得时效还是不错的。
能做到比较快的迁移,主要由于脚本具备以下特点:
实现方式均为python以字符串形式拉取sql format,进行参数填充后通过jdbc的形式抛给数据库执行,实际这部分只需要改改连接并调通即可
设计中留下了比较清晰的设计文档和逻辑依赖关系图,可以从底向上地一层层进行迁移,上一层脚本数据验证后,下一层脚本的测试数据自然就准备好了
主要采用varchar2/double/int三个类型的字段,在迁移过程中没有因为字段类型不兼容(比如时间字段)导致的各种问题
4.3 技术支持参考
【官方文档】https://docs.pingcap.com/zh/tidb/stable
【DBA文档】http://doc.hz.netease.com/pages/viewpage.action?pageId=272851046
-- End --
点击下方的公众号入口,关注「技术对话」微信公众号,可查看历史文章,投稿请在公众号后台回复:投稿




