分布式锁
锁的本质是一个所有线程或进程都可以看见的共享变量,这个共享变量具有互斥性,当一个线程或进程拥有这个变量之后,其他线程或进程就只能阻塞,直到变量被释放,再重新去竞争。拥有了共享变量即获取了锁,只有获取锁之后才能执行互斥区中的代码。
对于单机应用来说,代表锁的共享变量只要存在于单机内存中即可保证所有的线程和进程可见。例如java synchronize实现中基于对象头中的mark字段,lock接口中是使用int值来存储锁状态的变更,使用cas来更新状态,并使用volatile来保证int值更新后的可见性。操作系统中进程中则是使用信号量等内存数据来进行互斥锁的实现。
但是对于分布式应用来说,代表锁的变量必须能够对运行在所有机器上的服务进程可见。所以提供锁服务必须运行在独立的集群中,例如数据库、缓存和zk等。
分布式锁需要考虑的问题
使用锁最基本的两个操作就是加锁和释放锁。对于这两个操作,我们希望它是原子性操作,否则在多线程竞争锁时就容易出现安全问题。在分布式环境下,还需要考虑到网络延迟和服务宕机带来的影响,需要考虑到一个线程宕机之后,它所拥有的锁如何释放。锁应该是可重入的,一个锁如果不可重入,则获取锁的进程在未释放锁的情况下,自身也无法再次获取锁,假如互斥区代码存在循环调用的话,就会出现死锁。在实际应用中,可能还需要考虑到锁是否需要阻塞,是否需要公平。
分布式锁的实现
数据库锁
使用数据库实现,本质上还是利用数据库的行锁或表锁。当我们使用select for update或insert时,会对指定行加上行锁。当多个线程同时对同一行数据加