用redis处理并发写的工作总结
公司运营部那边需要做一个营销活动-springrain活动,上午和team leader、同一组的同事和产品经理一同听取并讨论了活动的需求,以及一些细节性的东西。
大致需求是这样的,由于保密的原因,这里只列出大概的需求。就是每一个阶段都有一个浇水的滴数,达到这个浇水的滴数,app上的小数就成长一个阶段,以此类推。一共有5个阶段。
后面我和同事就开始进行数据库表的相关设计,以及涉及到提供给app前端的接口方法。这是每一次做后端接口的必要的工作步骤。api的接口方法,我和另一个同事进行了不同的分工,每人写几个接口方法,同时进行自测并和前端人员进行联调。
在设计到给树浇水的这个接口的时候,同事一开始是对于每一个浇水的人,都直接进行数据库操作,保存每一个会员的浇水记录,接口很快就写完并发布了。后续进行测试的时候,发现了问题。同事写了一个python的脚本进行多线程并发去测试该接口,出来的结果出人意料的出错了,有丢失更新的问题。或者说是并发导致了数据被覆盖了。所以,对于存在并发的这种操作,我们想到的方法就是加锁,或者同步。
加锁,这种操作我们一般分为业务加锁和数据库层面的锁。数据库层面的锁我们可以用悲观锁也可以用乐观锁,但通常这种问题我们不会再数据库层面进行处理。所以这种解决方案我们就忽略到,有对悲观锁和乐观锁不明白的童鞋,请自行google。
这里我们说一下业务层面的加锁也就是通常的同步操作,对于java层面的同步一般对于并发的性能支持的不好,如果并发量比较大,会造成排队甚至丢失连接的情况。所以这种方案对于我们也不可行。最后讨论的结果是使用基于生产者-消费者的模式解决并发问题,是并发从并发变为同步操作,从多线程变单线程。
这里我只写一下伪代码:
public void save(){ //组织数据 //将数据放入到队列,返回 jedisClient.lpop(xxxx) }
然后启动一个后台线程池从队列中取出需要保存的数据对象,进行数据库操作。类型的伪代码如下:
ScheduledExecutorService executor = Executors.newFixedThreadPool(6); executorService.execute(new Runnable() { @Override public void run() { //数据库操作 } }
从而解决上面的问题,这里面的队列是redis内部实现的一个简单的队列
上一篇: 用php如何解决大文件分片上传问题