redis悲观锁的使用


文件脏读脏写

在程序设计中,为了提高数据吞吐量往往会采用多线程的方式进行设计。在部署的时候,为了提高并发访问量,往往会使用负载均衡多点部署。而在有些功能需求设计中,我们在并发时需要特别注意多线程造成的脏读脏写问题。例如现在有一个文件,我们需要根据传来的参数处理这批文件。如果此时使用多线程去处理,势必造成对该文件的脏读脏写。为此我们就需要引入锁机制。

锁机制

程序设计中的锁分为悲观锁和乐观锁。悲观锁是表示没拿到锁之前,程序没有处理的权限,无论读写都需要等待;与之相对的是乐观锁,乐观锁是读取不需要等待,而在写入的时候,需要判断是否有冲突。而针对脏读脏写的问题,我们需要使用到悲观锁。

redis悲观锁的初步实现

起初设想的是直接在redis中存入一个字符串key,并把key的值设为ture。每次线程处理的时候都要先从redis中获取key,判断key的值是否为true,如果为true,则表示获取到锁,并同时把key的值设为false,表示此时已经有线程开始处理,该锁对其他线程处于上锁状态。当线程处理逻辑结束后,把key值重新设为true用来释放锁。以此来完成线程锁的应用。

遇到的问题:

这样设计,初步想也是挺符合悲观锁的逻辑的。但在实际测试中,当并发量很大的时候,还是会出现错误。也就是说当一个线程获取锁之后,修改锁状态前的这段时间内,其他线程也是可以获取到锁的。这就又会导致脏读问题的出现。显然初步设计还不是太完美,需要进一步完善。

redis悲观锁的进一步优化

当上面的初步实现满足不了使用要求后,通过进一步查找资料,发现可以使用redis的setnx方法,解决上述的问题。原理就是redis的setnx方法是一个设置字符串key-value值的方法,当这个key值已经存在redis中时,运行setnx会返回0。但是当redis中没有key值的时候就会设置成功,返回1。运用setnx原理,我们可以把上面的初步实现中获取锁,改为setnx方法。获取到锁就会返回1,获取不到就会返回0。这样获取锁的操作就具有了原子性。而当释放锁的时候,只需要删除redis中的这个值就可以了。

实操代码


def  threadfunc():	#一个线程方法
	while not redis.setnx(key,"1",expire_times):      #如果获取不到锁,就等待0.1秒
		time.sleep(0.1)

	some_deal_with_func()	#具体的程序业务逻辑

	redis.del(key)	#从redis中删除key值,释放锁 

上面的伪代码就是redis悲观锁的大概设计思路,其中some_deal_with_func是具体的业务逻辑,也就是线程内需要运行的方法。其中在setnx方法时设置了过期时间,就是为了防止,some_deal_with_func方法内有业务错误,导致不能释放锁,所以设置过期时间,以达到释放锁的目的。

完结撒花,如果你觉的写得不错,请给我点个赞吧