背景:数仓接入binlog类型的业务数据,由于业务数据同一个id可能会有不同的操作,如:insert
update
delete
,所以数仓接入过来的binlog数据,同一个id会存在多条数据的情况,为了保证跟业务数据库的数据状态相同,我们需要拿到id的最新数据
-
旧版的binlog数据处理方式:
- 每条binlog里会有一个字段
position
用来记录这条数据在文件里的位置,同一个id,通过对position
降序排序,取到rank=1的数据即为最新的数据(一般情况下最新的数据,其position
值最大)
- 每条binlog里会有一个字段
-
遇到的问题
- 数仓接入的一个订单相关数据,有用户反馈数仓相关表存在订单状态异常的问题,业务数据库里某条订单状态为【已完成】,但数仓里这个订单的状态是【已下单】
-
排查问题
- 顺着当前表往上游查询每张表里的订单状态,都显示【已下单】
- 怀疑binlog丢失或者计算逻辑有误
- 在ods层查看是否存在日志丢失问题,发现binlog日志没有丢失、
- 检查计算逻辑,用现有逻辑去查询当前order_id,对于同一条order_id、存在多条数据,最新的数据不是
position
最大的那条数据;假设order_status分别为1、2、3,最新的状态应该为3,但是用现有逻辑查询出来的结果为order_status=2时的那条数据最大。所以在这一步我们拿到的数据不是最新的,导致跟业务数据不一致
- 跟中间件的同学进行了沟通,得到反馈是因为集群做迁移导致的文件顺序错位,所以会存在最新数据的
position
不是最大的情况,并且我们不应该拿position
做排序取最新数据,这个不准确
-
解决方案
- 给出的解决方案是binlog数据传过来时每条数据中携带一个全局唯一事务id【gtid】,刚开始我以为是通过redis的Incr操作实现的原子计数器,后来请教了一下中间件同学,【gtid】答复是mysql自带的功能,会对每个事务附上一个唯一id,这个id会随着事务的操作递增,能保证最新的数据其【gtid】最大
- 数仓同学在接入binlog数据时,可以拿每个【gitd】做排序,取rank=1的数据