分布式锁闲谈
前言
服务器单机情况下,要锁住某个资源,我们一般用到的是synchronized,lock等锁,这是java提供的,也确实能很有效的锁住资源。
但是在服务器集群的情况下,上面这些单机锁就不起作用了,你能锁住这台机器,但剩下的机器都没锁,就没有意义。
所以,一般解决这种问题,我们都用分布式锁。
分布式锁原理
分布式锁,主要原理就是新增了一个第三方存储介质,代替以前的synchronized。
假设服务器A对某个资源加锁后,就在这个第三方介质中记录,然后其他服务器再访问该资源的时候,就能知道这个资源被锁住了,效果就达到了。
所以也可以说,这个第三方资源,也可以理解成将服务器集群强行变成了一个单机服务器了。
一般情况下,能担当这个第三方介质的,有以下三种。
1.数据库
2.redis
3.zookeeper
1.数据库
不管集群有几个服务器,都会操作一张表
所以,想要加锁,只需在数据库中插入一条数据即可,记录这个资源的唯一标识,状态为已加锁。
再有加锁的请求,一查数据库已经加锁了,就是没有获取到锁。
一般基于数据库的分布式锁,都会考虑以下几个问题
锁的失效时间(一般做法就是job定时扫,删掉过期的锁,但多久才算过期,是一个很值得仔细研究的问题,一般都是因具体业务来定)
数据库的可靠性(这个倒不是主要问题,一般可以通过搭建集群等方式解决)
数据库的效率问题(毕竟数据库是读磁盘,肯定没有读内存快)
2.redis
这个应该是最常见的,redis分布式锁。
setNx方法估计很多人都用过,不多说了,set if not exist
旧版本的redis,加锁和设置过期时间,但这样是有问题的,比如加锁了,但没有来得及设置过期时间,服务挂了,那么这个锁就死锁了
新版redis,相当于将这两步合成一个方法了,支持多参数,这就感觉很完美啊,不过我没用过,可以试试,应该没问题。
一般基于redis的分布式锁,会考虑以下问题:
锁的失效问题(和数据库分布式锁一样,失效时间设置多少算最优,没有答案,只能job异步扫,一样的问题,所以不能算是一个优雅的解决方案)
锁是非阻塞的 (成功失败都会立即返回,这个要看业务怎么操作了,也可以在业务上手动阻塞住,这不是问题,但要知道)
3.zookeeper
大数据高并发,一般用这个
使用临时有序节点,服务器A的方法加锁就在对应方法节点上创建一个临时有序节点,算是获得锁,因为第一个创建的,序号最小,所以后面再有其他服务器想要加锁时,一查自己的节点的序号不是最小,所以就算没有获得锁。
因为zookeeper的临时有序节点,当加锁的这个线程断掉后,这个节点也自动消失,那么锁也就自动释放了,是不是感觉特别合适做分布式锁,上面两个关于锁的失效问题,用zookeeper得到了优雅的解决
一般需要注意的是:
zookeeper相对复杂(zookeeper算是比较重量级的,甚至有些小项目都不会用到zookeeper,所以也没有必要为了一个简单的分布式锁的问题,强行使用zookeeper)
性能问题(一般和redis比性能,都是弟弟)
总结
在性能方面,redis > zookeeper > 数据库
在实现的复杂度方面,zookeeper > redis > 数据库
对于新手而言,数据库 > redis > zookeeper
可靠性方面,zookeeper > redis > 数据库
所以,一般项目用redis分布式锁就可以了,
如果对可靠性要求很高,那么建议考虑zookeeper分布式锁,
几乎很少用数据库分布式锁,主要还是性能问题。
补充
其实分布式锁就是一个集群环境如何共享资源的问题,我们看问题的角度不应该仅仅停留在分布式锁这里。很多问题的解决方案,其实都是大同小异的。
比如shiro的集群session共享,一般做法也是放在redis中,其实和这个redis的分布式锁原理一样。
还有很多类似场景,欢迎评论补充。