架构师带你玩转分布式锁

  

  大多数互联网系统都是分布式部署的,分布式部署确实能带来性能和效率上的提升,但为此,我们就需要多解决一个分布式环境下,数据一致性的问题。   

  

  当某个资源在多系统之间,具有共享性的时候,为了保证大家访问这个资源数据是一致的,那么就必须要求在同一时刻只能被一个客户端处理,不能并发的执行,否者就会出现同一时刻有人写有人读,大家访问到的数据就不一致了。   

  
  1,我们为什么需要分布式锁吗?   
  

  在单机时代,虽然不需要分布式锁,但也面临过类似的问题,只不过在单机的情况下,如果有多个线程要同时访问某个共享资源的时候,我们可以采用线程间加锁的机制,即当某个线程获取到这个资源后,就立即对这个资源进行加锁,当使用完资源之后,再解锁,其它线程就可以接着使用了。例如,在JAVA中,甚至专门提供了一些处理锁机制的一些API(同步/锁等)。   

  

  但是到了分布式系统的时代,这种线程之间的锁机制,就没作用了,系统可能会有多份并且部署在不同的机器上,这些资源已经不是在线程之间共享了,而是属于进程之间共享的资源。   

  

  因此,为了解决这个问题,我们就必须引入“分布式锁”。   

  

  分布式锁,是指在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问。   

  

  分布式锁要满足哪些要求呢?   

  

  
  

     <李>   

  排他性:在同一时间只会有一个客户端能获取到锁,其它客户端无法同时获取   

  李   <李>   

  避免死锁,这把锁在一段有限的时间之后,一定会被释放(正常释放或异常释放)   

  李   <李>   

  高可用:获取或释放锁的机制必须高可用且性能佳   

  李      

  讲完了背景和理论,那我们接下来再看一下分布式锁的具体分类和实际运用。   

  
  二、分布式锁的实现方式有哪些吗?   
  

  目前主流的有三种,从实现的复杂度上来看,从上往下难度依次增加:   

     <李>   

  基于数据库实现   

  李   <李>   

  基于复述,实现   

  李   <李>   

  基于管理员实现   

  李      

  无论哪种方式,其实都不完美,依旧要根据咱们业务的实际场景来选择。   

  

  1. 基于数据库实现:   

  

  基于数据库来做分布式锁的话,通常有两种做法:   

     <李>   

  基于数据库的乐观锁   

  李   <李>   

  基于数据库的悲观锁   

  李      

  我们先来看一下如何基于“乐观锁”来实现:   

  

  乐观锁机制其实就是在数据库表中引入一个版本号(版本)字段来实现的。   

  

  当我们要从数据库中读取数据的时候,同时把这个版本字段也读出来,如果要对读出来的数据进行更新后写回数据库,则需要将版本加1,同时将新的数据与新的版本更新到数据表中,且必须在更新的时候同时检查目前数据库里版本值是不是之前的那个版本,如果是,则正常更新。如果不是,则更新失败,说明在这个过程中有其它的进程去更新过数据了。   

  

  下面找图举例:   

  

  架构师带你玩转分布式锁”>
  <br/>
  (图片来源网络)
  </p>
  <p>
  <br/>
  </p>
  <p>
  如图,假设同一个账户,用户一个和用户B都要去进行取款操作,账户的原始余额是2000,用户一个要去取1500,用户B要去取1000,如果没有锁机制的话,在并发的情况下,可能会出现余额同时被扣1500年和1000年,导致最终余额的不正确甚至是负数。但如果这里用到乐观锁机制,当两个用户去数据库中读取余额的时候,除了读取到2000余额以外,还读取了当前的版本号版本=1,等用户一个或用户B去修改数据库余额的时候,无论谁先操作,都会将版本号加1,即版本=2,那么另外一个用户去更新的时候就发现版本号不对,已经变成2了,不是当初读出来时候的1,那么本次更新失败,就得重新去读取最新的数据库余额。
  <h2 class=架构师带你玩转分布式锁