本文介绍一种redis锁的应用场景,主要解决需要根据数据库多个字段来确认数据唯一性的问题

背景介绍

最近到工作的过程中遇到一种这样的场景,设计一个新的数据库表用来存储数据,但是该表存储的数据可以根据主键加另外三个字段property1、property2、property3来唯一确定一条数据,即非主键的三个字段能唯一确认一条数据,但是数据库表的主外键依赖不建议设置的太过复杂,否则难以维护,这里可以使用redis的单线程锁在应用层进行控制,前提是能确认整个insert方法事务执行的最大耗时时间maxExcuteValue

场景简介

由于新表只有一个唯一索引键,但property1、property2和property3是可以重复,如果需要保证三个字段唯一确认一条数据,首先想到的一种解决方式是,我们可以在insert数据的时候,先根据这个3个条件进行数据库的查询,如果查询到没有才将数据入库,如果已经有了,则返回入库失败提示,但是这种方式在并发的时候会出现问题,例如在同一个唯一索引(用户id)的情况下,如果出现了并发的操作,会出现这样一种间隙,两个线程带着相同的三个字段值进来insert的方法,线程1首先查询到数据库没有该记录,准备进行insert语句,在线程1还没将数据入库的时候,此时线程2也完成了数据库的查询,也发现数据库没有该数据,就有可能出现property1+property2+property3出现重复的数据,这样就不满足非主键的三个字段唯一确认一条记录的要求

利用Redis的单线程机制解决问题(思路)

因为redis是单线程的,我们可以考虑使用这样一种方式进行加锁,当进入新增的方法时,使用appName+property1+property2+property3组合作为redis key,并使用setifabsent锁住key,如果锁定成功,设置key的有效时长稍微大于整个事务的maxExcuteValue,并且开始进行数据库的数据查询,如果数据库中没有相应的数据,则insert,否则返回失败,这种处理方式即使有2个线程带着相同的property1+property2+property3进来,也只会有一个线程成功锁住30秒,并完成事务,可以从代码层面保证数据的唯一性(property1+property2+property3),另外锁住时间稍微大于整个方法执行事务的maxExcuteValue是为了防止查询时间过长事务没有结束却让另一个线程进入insert操作

代码的实现demo

小结

本文介绍了一种使用redis的单线程特性,解决一种业务场景数据一致性保证的需求,当然可能也会有更好的方案,解决问题的方式并不单一,解决问题应该朝着尽量简单的方向去考虑,提高代码的可读性和维护性

发表评论

电子邮件地址不会被公开。