今天,同事武杨发现使用精卫做一个商品排期表的数据同步有问题,问题表现在重新排期的商品在一个终搜查询系统里面查不到记录了。
1、分析精卫中间件的数据同步机制,它是分析mysql binlog ,解析成RowChangeEvent以后,包装成消息通过消息中间件Meta发送消息到消息网关
MetaServer,然后再由消息订阅者消费消息,解析消息中的RowChangeEvent以后,同步到搜索系统的。
2、而从业务逻辑上看,排期操作是先逻辑删除旧的排期记录,再插入一条新的排期记录,update和insert是在一个事务的。 示意如下:
set autocommit =0 ;
#这条update操作因为有 KEY `idx_iod_jid` (`ju_id`,`is_deleted`,`online_end_time`) 这个索引的存在保证了A商品的排期不会影响到B商品,
#即采用了行锁。
update item_online_detail set is_deleted=1 where is_deleted=0 and ju_id in (?)
insert into item_online_detail (?,?,...,?)
commit;
而武杨查看日志,发现insert 和update操作有乱序 。造成插入的记录,被错误的delete了。
3、进一步分析精卫中间件,结合源码发现精卫在提取binlog(DbExtractor) 和消费binlog时(HdfsApplier),可以配置多线程,而精卫通过把一行记录hash到一个线程,来保证一行记录的处理不会乱序。而消息中间件Meta,则在消息发送时一个线程的消息发送到一个分区,消息订阅时一个分组中的一台机器负责一个分区的消息,来保证顺序。
最后发现问题是在于在数据提取时(DbExtractor)配置了多线程,而对记录hash到binlog提取线程的操作使用了item_online_detail表的id,而不是ju_id,
而每次排期操作的id是会变的,这就导致对一个ju_id的操作会交给不同的线程处理而导致乱序。而且武杨在快速多次对同一商品进行排期时重现了这一问题。
总结原因:1、可能我们对系统的压力测试还要做充分一些,才能发现这种问题。2、对中间件的使用还要更慎重,少踩雷。