一个超越主观意识的神奇问题。比如我在上一篇文章中使用Jedis实现了分布式锁的技术知识储备。本以为会很稳定,不会遇到什么问题,结果实际情况被打脸了。2.技术背景的同步为了照顾一些不喜欢看连续剧的同学,这里一定要贴上下文,不然内容会不连贯,看起来不流畅。如果你看过《分布式锁中-基于 Redis 的实现如何防重入》和《分布式锁实战-偶遇 etcd 后就想抛弃 Redis ?》,可以跳过本节【技术背景同步】,直接进入第三节【诊断流程】。2.1如何使用SET指令加锁我们使用SET指令来实现加锁逻辑,指令形式如下:SETkeyvalue[NX|XX][GET][EX秒|PX毫秒|EXATunix时间秒|PXATunix时间(毫秒)|hold]1)加锁成功逻辑如下:判断key是否存在;如果key不存在,则设置key指定key的过期时间如果存在,则返回SetParamsparams=SetParams.setParams().nx().ex(lockState.getLeaseTTL());Stringresult=client.set(lockState.getLockKey(),lockState.getLockValue(),params);上面的代码是之前《分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇》写的加锁逻辑,其中只用正常加锁的返回值来判断加锁是否成功,即结果是否为“OK”,但是key已经存在并且锁的返回值不成功。什么,我们应该怎么判断?2.2SET的返回值有哪些?官网上查看SET返回值的说明。为了您的方便,结果直接张贴在这里。很多同学应该没有看过这个描述。简单的字符串回复:OK如果SET正确执行。空回复:(nil)如果SET没有执行操作,因为用户指定了NX或XX选项但条件不满足。如果使用GET选项发出命令,则以上内容不适用。它会改为如下回复,而不管SET是否实际执行:批量字符串回复:密钥中存储的旧字符串值。空回复:(nil)如果密钥不存在。2.3SET命令加锁的结论从官网给出的描述可以知道,当前使用SET命令,只要返回不是“OK”,就说明锁已经存在,所以在《分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇》例子中tryLock的逻辑,只是增加了一个判断锁类型的逻辑,即如果锁key已经存在,并且锁是“一次性”锁,则立即返回,无需等待环形。2.4残酷的现实在使用Jedis客户端实现分布式锁功能时,我们发现并确认,从客户端用户的角度来看,SET指令的原子语义可能不一定得到保证。3、诊断过程1)根据用户反馈,偶尔会出现反重入锁加锁失败的情况。从日志的结果来看,在这个key相关的加锁日志中,只有SET返回空,即key已经存在的信息。是否有其他程序也可以加锁,比如在Redis中手动设置key或者是否有其他实例在运行?已经确认没有手动设置key。整个程序在测试环境中只有一个实例,没有其他实例。并Tag记录几个疑惑:从用户请求到结束,多次执行lockSET命令,SET不成功时,返回结果是OK还是空。如果SET返回空,通过GET查询,记录它的值,可以判断是否和加锁时的值一致3)用户反馈,我又出现了:通过TraceId信息查看Trace,越多你不信,你越展示:只有一个有效的SET命令,SET返回一个空的GET结果,返回值就是SET指定的值。SET的耗时不算太长,是208ms4)SET命令不是官网说的效果吗,有什么坑?通过直观的Trace信息,我们不再怀疑上层的加锁逻辑和应用程序的逻辑,而是将Jedis客户端定位为最可疑的对象,但仍然缺乏对一个现象的研判依据,然后重现,找到规律。我什至怀疑是Reidsserver5)模式出现了,耗时问题又出现了。通过Trace信息,我们对比了有问题的SET和没有问题的SET。他们表现出什么不同,很快就发现了一个显着的特征,有问题的SET命令执行时间超过200ms,正常的SET命令执行时间不到20ms。6)什么是200ms?通过排查,发现Jedis客户端的几个超时设置为200ms。会不会是哪个链接超时导致的问题?7)Debugsourcecode从下面的调用栈中,你是不是也发现了一个可疑的词?没错runWithRetries,它会重试。execute:112,JedisCluster$2(redis.clients.jedis)execute:109,JedisCluster$2(redis.clients.jedis)runWithRetries:120,JedisClusterCommand(redis.clients.jedis)//”这里运行:31,JedisClusterCommand(redis.clients.jedis)set:109,JedisCluster(redis.clients.jedis)8)让我们看看超时是什么意思publicBinaryJedisCluster(Set
