使用redis分布式锁解决并发线程资源共享问题

众所周知, 在多线程中,由于共享全局变量,会致使资源修改结果不一致,因此须要加锁来解决这个问题,保证同一时间只有一个线程对资源进行操做mysql

可是在分布式架构中,咱们的服务可能会有n个实例,但线程锁只对同一个实例有效,就须要用到分布式锁----redis setnxweb

 

原理:

  修改某个资源时, 在redis中设置一个key,value根据实际状况自行决定如何表示redis

  咱们既然要经过检查key是否存在(存在表示有线程在修改资源,资源上锁,其余线程不可同时操做,若key不存在,表示资源未被线程占用,容许线程抢占,而后将经过setnx设置vlaue,表示资源上锁,其余线程不可同时操做)sql

  图示:数据库

  

分析:

  咱们的服务处于一个集群中,若是只是简单的的使用线程锁来解决以上问题,是存在问题的:由于线程是基于进程的,两个web server处于不一样的进程空间多线程

  也就是说,user1的请求发往web server1,那只能与web server1的其余请求进行锁的操做,而不能对web server2的请求产生影响架构

  上面的图中,user1发往web server1的请求负责处理的线程为Thread1,同理负责处理user2发往web server2的请求的线程thread2分布式

  在同一时刻1,两个线程都读取了mysql中residue_ticket的值为100,对应上图 (1)(2), 各自对100进行-1操做,更新到数据库,对应(3)(4)性能

  咱们预期的状况是residue_ticket值被减小了两次,应该为98,可是实际状况下,两个线程都作了100-1=99的操做,并都将mysql中的值改成了99, 的这就会致使最终数据不一致,因此就要用到分布式锁。spa

为何用redis?

  由于redis是单线程的,不存在多线程资源竞争,而且它真的很快

为何用setnx 而不是set?

  setnx表示只有在key不存在时才能设置成功,可是set会在key存在的状况下修改value

 

利用setnx的特性,咱们能够这样这样设计:

  伪代码:

  # 设置redis锁的
  redis key = 'residue_ticket_lock'

  # get_ticket是处理购票的逻辑
  def get_ticket():
    time_out = 5    # 为了防止线程过多,当前线程获取不到锁,长时间处于循环中而致使的性能影响,咱们设置一个超时时间,若是当前线程在超时时间内尚未抢占到分布式锁,就返回失败的结果
    while True:
       if redis.setnx('residue_ticket_lock''lock',5):
          # 若是setnx返回True, 表示此刻没有其余线程在操做数据库,当前线程能够上锁成功,注意不只设置了value=lock,还设置了过时时间,这是必要的,为了防止上锁的线程异常崩掉致使不能释放(删除key)而致使其余全部线程永远拿不到操做权
           residue_ticket = mysql.get('residue_ticket')     # 从mysql中获取当前剩余票数
           mysql.update('residue_ticket',residue_ticket-1)   # 订购成功,将票数-1,更新数据到mysql
          # 删除key,释放锁
          redis.del('residue_ticket')
           return True
       else:
          # 若是setnx返回False,表示有其余线程对在操做,当前线程等待0.01s,并继续循环
          time.sleep(0.01)
          time_out -= 0.01
          continue
    return False
相关文章
相关标签/搜索