分布式锁的实现方式:
- 基于数据库的实现方式
- 建一个表,当使用某个方法的时候,向表中插入一条数据,记录该方法名,机器id,线程id,表示获取到锁,当方法执行完毕后删除对应的数据
- 缺点就是基于数据库,数据库的性能会直接影响分布式系统的性能。而且如果获取到锁的线程如果挂掉了,那么这段数据永远无法删除,其他线程也就无法执行这个方法,因此需要再设置一个过期时间,并且需要定时线程来定期删除过期的数据
- 使用redis实现
- 通过redis设置nx、ex来设置锁,分布式某个节点先判断redis中是否存在key,如果存在者等待,如果不存在则设置key,并将值value一个随机值,以及过期时间,表示它已经获取到锁
- 设置过期时间的原因是防止节点失效后锁无法释放;设置随机值的原因是,在任务执行完毕节点释放锁的时候,先判断key对应的值是否跟自己设置的值一致,防止错误释放其他线程的锁(当前线程阻塞一段时间,锁超时释放,其他线程获取到锁,当前线程阻塞返回,执行删除锁的操作,此时将其他线程设置的锁给删了)
- 缺点是在高可用的redis集群中,无法保证锁的唯一性,因为redis集群的数据主从复制是异步方式的
- 使用zookeeper实现
- zookeeper内部是一个分层的树状目录结构,每一层的目录不允许重名。分布式节点的线程A可以在mylock文件夹下寻找比它小的兄弟节点,如果不存在,则说明它最小,则创建节点表示获取到锁,任务执行完毕后删除自身节点;如果存在则表示锁已经被获取,则创建节点加入并且监听比自己小的相邻节点,如果相邻节点变更,B监听到了变更则判断自己是不是最小的节点,如果是则获取到锁,否则等待
- 缺点是频繁的创建和删除节点,性能上不如redis