转载请联系后台Q公众号。什么是分布式锁?介绍分布式锁首先要提到线程锁和进程锁对应的是分布式锁。线程锁:主要用于锁定方法和代码块。当一个方法或代码使用锁时,一次只有一个线程执行该方法或代码段。线程锁只在同一个JVM中有效,因为线程锁的实现从根本上是通过线程间共享内存来实现的。比如synchronized是一个共享对象头,显示锁Lock是一个共享变量(状态)。进程锁:为了控制同一个操作系统中的多个进程访问一个共享资源,由于进程是独立的,每个进程不能访问其他进程的资源,所以进程锁不能通过synchronized等线程锁来实现。分布式锁上的问题监听:当多个进程不在同一个系统中时,使用分布式锁来控制多个进程对资源的访问。有这样一种情况,线程A和线程B都共享一个变量X,如果是分布式的话,线程A和线程B很可能不在同一个对象中。每个client在释放锁的时候,都是一个删除操作,并没有检查锁是否还是自己的,所以释放会出现被别人加锁的风险。解决方法:当客户端加锁时,设置一个只有自己知道的唯一标识符。比如可以是自己的线程ID,也可以是UUID(随机唯一),这里以UUID为例://锁的值设置为UUID127.0.0.1:6379>SETlock$uuidEX20NXOK这里,假设操作共享时间为20s是完全够用的,不管锁自动过期。之后在释放锁的时候,首先要判断锁是否还属于你。伪代码可以这样写://锁是自己的,然后释放ifredis.get("lock")==$uuid:redis。del("lock")在这里,GET+DEL两个命令用于释放锁。这时候就会遇到前面提到的原子性问题。客户端1执行GET,判断锁是自己的。客户端2执行SET命令,强行获取锁(虽然发生的概率比较低,但需要严格考虑锁的安全模型)。Client1执行了DEL,但是releases可以看出这两个命令必须是原子执行的。如何原子地执行它?Lua脚本。我们可以把这个逻辑写成Lua脚本,让Redis去执行。因为Redis在单线程中处理每个请求,所以在执行一个Lua脚本时,其他请求必须等到Lua脚本处理完毕,这样GET+DEL之间就不会插入其他命令了。安全释放锁的Lua脚本如下://判断锁是自己的,释放ifredis.call("GET",KEYS[1])==ARGV[1]thenreturnredis.call("DEL",KEYS[1])elsereturn0end就可以了,这个沿途的优化会让整个加锁和解锁的过程更加严谨。这里我们先总结一下,基于Redis实现的分布式锁,一个严谨的流程如下:加锁:SETlock_key$unique_idEX$expire_timeNX操作共享资源释放锁:Lua脚本,先GET判断锁是否属于自己,以及然后DEL释放锁
