前言
在上一节我们使用了陌陌开源框架来处理json中的单值校验,在这一篇文章里我们主要通过flink sql来处理更为复杂的风控场景。
业务场景
这一部分业务场景主要是校验企业当期需要报税的科目,相比企业往期报税的科目是否有增加或减少,以此来防止税务漏报。
我们的企业用户数量在百万级别,每个业务期间漏报率、误报率在千分之5左右,也就是会有几千家会有漏报的情况,而漏报会缴纳2000元的罚款,每个业务期间亏损大约在百万元级别,我们的目标是将误报率控制到万分之一以下。
数据量
在数据方面,当期的企业报税科目通过mq传送过来,以json的格式作为序列化方式,包含报税任务id,科目名称等。
往期的企业报税科目数据主要存储在线上mysql数据库中。
我们需要实时的从mq中消费出企业报税的报税科目,对比数据库中该企业上一个税期的报税科目,校验科目是否会有多或少的情况。
整个业务分为12个区域大区,mysql数据库按照区域进行了分库,存放在12个set中。
mq中每100毫秒会收到一条企业当期科目信息。
mysql中需要关联的维度表有四个:两个万级别数据量的科目码表,一张百万级别的任务-企业表,一张千万级别的企业往期报税科目。
技术调研
在项目中我们前期花费了大量的时间进行了业务需求分析与技术调研。在技术栈方面,我们使用到的工具主要有kafka、nifi、flink。
kafka主要缓存从mq中得到的消息、以及作为flink sql中的table数据来源。
nifi主要用于数据传输和数据清洗。数据传输包括从mq接受信息放入kafka中flink的源表topic、将kafka中flink处理好的结果表topic中数据备份到mysql并推到下游系统的mq中。数据清洗主要将json进行格式转变,适配flink 源表的schema格式。
flink主要使用到的组件是flink sql。以kafka中清洗好的mq信息作为源表1,不断的消费,去关联mysql中的历史信息。将关联结果sink到kafka结果表。
前期调研过程重点放在flink上,主要调研了flink cep与flink sql的高级api,在这个场景中我们决定使用flink sql尽量快速的实现关联校验的业务逻辑开发。
技术难点
技术难点主要在于两点:
- mysql中维度表数据量过大,flink如何与mysql进行交互。
- 使用dataset的table还是使用datastream的table。
mysql中维度表数据量过大
对于mysql维度表的关联,之前看flink的专家邪云写的例子,主要是通过sql-client在命令行中执行sql语句,通过flink里面的jdbc-connector jar包来连接数据库获得数据。
这种做法的优势是可以不用了解flink的各种API,直接通过sql快速完成业务开发。
而劣势是jdbc-connector中提供的sql语句不如写代码来的灵活,比如在这个场景中我们的百万级别和千万级别的mysql表数据量是相当大的,不能通过这样直接连接去查询。
因此我们选择使用jdbc-connector中的JDBCInputformat与JDBCLookUpFunction来处理大表。
通过业务分析我们发现千万级别的大表是历史表,我们只需通过拿出其中最近一个帐期的数据比较即可,这样就将千万级别的数据量降低到百万级别,同时由于历史表中的数据不会变动,因此我们使用JDBCInputformat一次将数据库中的数据以流的形式放入内存中,减少对数据库的访问。
对于百万级别的任务表,本身表中当期数据是变动的,因此我们需要使用JDBCLookUpFunction方法,配合CacheMaxSize与CacheExpireMs配置缓存,实时的查询数据库。而我们查询的数据都建立了索引,所以能够较好的应对查询压力。
是使用dataset的table还是使用datastream的table?
我们的业务逻辑目标是鉴别出从mq拿出的当期报文中科目数据,相对于mysql中近期科目数据是多了还是少了。简单来说就是求mq中报文与mysql中科目数据的一个差集。
一开始我们采用的是dataStream的方式,将每个报文中企业所有的科目数据进行了切分,放入kafka中作为数据源。也就是说flink的报文这边kafka源表的最细数据粒度是每条科目。这样只需要对比mysql中该企业的近期科目数据中有没有这条科目即可判断本期科目是否多于上期科目。
反过来,判断本期科目是否少于上期科目,我们是无法通过sql来查询实现的。因为必须要等流都流过才能确定报文中的本期科目确实少于上期科目,因为如果上一步是用流的一条数据join mysql数据库中找出差集,那么这里就是通过mysql数据库中数据求无限流的查集。对于无限的流求差集是无法做到的,因为我们必须要等到所有流都流完才能确定数据确实不存在于这个流中。
我们重新梳理了业务需求,我们的业务目标是一个企业的当期科目与这个企业的上期科目做校验,所以在这里流的数据粒度应该是企业,而不是科目。
因此应该把流中一个企业的所有科目看做是一个有限集合(DataSet),去对比该企业在mysql数据库中的上期科目,获得两个集合的差集,完成校验目的。
总结
目前项目仍然处于开发阶段,通过kafka、nifi、flink走通了业务校验流程,在这个过程我们对flink的核心api与高级api基本有了一个感性的认识。
在接下来的两周里面,我们会将业务扩展到所有mysql数据库,后续会继续对flink的性能方面进行测试。