数据并发批处理的控制

本文探讨了在分布式服务环境中处理数据库数据的两种策略:悲观锁和预更新+lock_key方式。悲观锁可能导致效率低下,而预更新策略通过添加lock_key字段实现了无锁处理,提高了并发效率。文中还提出了一种未采用的Redis ZSet方案,虽能完全避免竞争问题,但因内存限制未能实施。总结了各种策略的优缺点,为实现高可靠性的任务处理服务提供了思路。
摘要由CSDN通过智能技术生成

背景:

我们有一批数据在数据库里,要进行到期处理并推送到mq,处理并推送小于等于当前时间的数据.

需求:

1.我们要求任务处理服务要做到高可靠性,因此需要搞成多机分布式服务,即使一个服务挂了,还有别的服务可以使用可以处理

2.保障各个服务均可以同时拿到数据进行高效处理

3.数据不能重复处理

 

方式一.悲观锁形式:事务+select for update

本文先提供一个没有采用的方式--采用事务加select for update的形式

  • 1.开启事务

  • 2.从task表里selce for update 锁定一批数据

  • 3.处理数据

  • 4.更新这批task

  • 5.提交事务

这么做呢就有个非常严重的问题,---同一时刻只有一个有效服务如果A系统拿到了数据,开始了事务但是没提交,那么B系统同样的条件也会查到同一批还没处理好提交的数据,此时B系统该查询线程就会阻塞等待A提交事务.这么看问题就来了,这里虽然保障了同一时刻只有一个服务可以拿到并处理一批数据,但是也导致了效率特别低,而且后面无论扩展多少服务应用都没啥用。

方式二.预更新+lock_key方式 (无锁,我们目前采用的方式) 

 

 

步骤解释:

  • 0.数据库里增加一个字段lockkey

  • 1.先去数据库拿小于等于当前时间的一批status为1(待发送)的数据

  • 2.根据这批数据的id ids更新这批数据为status=2(发送中),lockKey=一个唯一数(防止两个服务拿到同一批更新的数据,我们用的是redis的一个自增id)

  • 3.根据ids和locaKey查询出本批次哪些数据被本轮处理函数更新了(这就是好处,如果我们没用加lock_key,那么第二个系统更新的数据条数不等于拿到的数据数据条数只能回滚从新拿,而我们这里加了lockkey之后更新了多少就代表本批次有效数据是多少,我们只处理有效数据即可,也不用回滚)

  • 4.把这部分数据发到MQ上

  • 5.异常捕捉

    如果次数大于5,将数据置为status=4,记录操作日志(基本不会出现,一般只有1,mq连不上,2,数据有问题)

    如果我们成功发送数据到MQ,将数据库该条数据状态置为3(成功发送)

    如果过程中出现异常,redis里记录该key的失败次数,如果次数小于5,将数据重新置为status=1,下次查询的时候可以重新拿到。

 方式三.redis zset 双写服务--强烈推荐

 

 

  • 1.向mysql写任务的时候同步的向redis zset里写一份,并设置score为自己的搜索条件,比如我这里是发送时间

  • 2.所有的服务消费数据直接从redis zet直接消费,获取小于等于当前时间的一个批次数据(比如100条).

  • 3.利用从redis拿的数据去mysql里拿数据

这种方式是我觉得最好的方案了,完全保障了每个服务每次处理mysql的数据都是互不相同的数据,完全避免了竞争问题.

但是我们目前没有用这种方案,原因是...目前我们redis内存只申请到一个比较小的内存,而zset采用的跳跃表结构虽然保障了数据查询非常快速,但是也非常占用内存,预估了一下按照我们的数据量起码要存储300万数据,用到的内存量是3~4G之间,好家伙直接把我们所有内存都用了,其他服务还用个屁...而且这玩意为了保障数据安全,不进行数据淘汰起码还要留个1G空闲安全空间....那肯定就用不了了. 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值