分布式,多线程(MQ多消费者)的情况下,关于redis以及数据库出现的数据重复问题

多个消费者,其实就是开启了多线程同时执行多个消费者(这个可以通过日志打印每个线程名称可以看到)

最初的场景:为了加快消息的消费,所以开启了多线程的模式对消息进行消费,并且一开始我是在自己本地(单服务部署)的情况下调试。

由于这个需求是把工业领域的监控视频RTSP流逐帧拆成图片传输给AI模型分析里面的信息,帧与帧之间的时间差距很小,如果全部插入数据库的话,数据量几分钟就有几万条,运行一天数据很庞大,需要把数据重复的,数据产出时间间隔很小的冗余数据丢弃掉,只插入代表性的数据。

遇到的问题:涉及数据库的数据插入,所以整一块都添加了@Transactional这个事务注解,并且由于是多线程,为了排除冗余数据,我采取的数据是取出数据库中 “同类型上一条的数据插入的数据”,只有产出时间time与当前插入的数据的时间相差5分钟才被认为是有代表性数据并进行数据库的插入。执行一次,去数据库检查数据的时候发现由于数据库的IO执行需要时间,某些数据在相差0.1毫秒0.2毫秒的情况下同时插入了,即使我修改了用redis作缓存(与数据库类似,只不过读取的是redis中的同类型数据key-value),每个操作都基于上一个缓存—-即是否命中redis的key,命中则跳过,否则则进行后续的插入,但是依然有重复插入的问题.

@Transactional(rollbackFor = Exception.class)
public void comsumeMessage(){
	....redis操作(IO内存需要时间);

	....数据库MySql操作(IO磁盘需要时间);
}

原因很明显就是不管是redis还是Mysql,IO都需要时间,由于消费速度快,两个线程基本是同时操作,在A线程查询完缓存后,还未进行插入的时候,B线程刚好查询缓存,在A完成插入之前,B已经查询并且判断了缓存不存在同类型数据所以进行了插入操作,导致AB两线程的数据重复了.

最初解决:后面给整一块的redis缓存操作都加入了同步锁,使时候类型的数据在redis操作中变为串行运行,至此单机问题结束。


public void comsumeMessage(){
	synchronized (this){
		....redis操作(IO内存需要时间);
	}
	updateDB();
}

@Transactional(rollbackFor = Exception.class)
public void updateDB(){
	....redis操作(IO内存需要时间)

	....数据库MySql操作(IO磁盘需要时间)
}

至于为啥这么写,主要是考略到@Transactional与synchronized之间的一些冲突,因为synchronized 锁的是这个方法,@Transactional的事物是Spring的AOP开启的。进入这个方法前,AOP会先开启事物,然后进入方法后, 此时会加锁,当方法结束后,锁释放,然后才会提交事物。在释放锁和提交事物之间有其他线程请求,读取到的是A线程还未提交事务的数据 , 这种情况会导致程序不安全。所以应该在开启事物前加锁(我们的redis也加了事务机制)。

后面的问题:后面更新上了开发环境(多个服务部署),查看了日志,发现有好几条报错是在我那块的,仔细一看是重复主键的报错,我当时想会不会是多服务的缘故,因为在redis中用redission新增分布式锁。过了一天,发现还是有原来的重复主键问题,继续排查,我们的服务用的是雪花算法,雪花算法会有取上一个时间戳的这么一个变量,仔细查看,发现是时间戳的问题,可是我看到生存算法是加了同步锁的,按道理不应该呀。

	public static int lastTimeStamp = -1;

问题排查:我用过thread.sleep调用该方法,发现锁失效了,仔细一看,静态变量时间戳emmmm,那就尝试吧static去掉,虽然时间戳维持着默认值-1,但是锁生效了!!

猜想:经过查找了一些资料,说是锁的变化,导致了加锁失效(仍待验证)

	public static void synchronized  snowflack(){
		.....
	}

解决方法:那既然变量锁不住,那我直接对调用这个雪花算法的对象进行加锁,虽然消费速度确实会出现明显的下降,但是解决线程安全问题还是更重要的.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值