当前位置: 首页 > 原理解释

redis setifabsent实现原理-实现原理:Redi Set If Absent

Redis SETIFABSENT 到底是如何回事儿? 提起 Redis 的原子操作,大家脑海里第一反应肯定是 SET 要么 INCR。但真正让你头疼的往往不是这些基础功能,而是那些带点“坑”要么“哲学”的指令。
比如 SETIFABSENT,这个指令乍一看听着挺像,但在实际代码里能直接写出来的概率极低。
为啥?出于它背后藏着一种“只要我不存有,我就能变出来”的魔法,这种魔法对数据库来说忒悬了。 起初,我们要搞清楚 SETIFABSENT 到底干啥。它的核心逻辑就四个字:要是不存有,就改成;要是已经存有,直接报错。 听起来好办,好办引发的后果是严重的。
要是服务器正在读取某个热点数据,假设那个数据正好被一个正在执行的 SETIFABSENT 操作给覆盖了,结局本来想查旧值,却查出了新值,那整个业务逻辑可能就崩了。
故此,要不就你确实确定“不存有的值”比“存有的值”关键,否则千万别碰这个指令。 那它的底层原理是啥呢?从内存角度看,SETIFABSENT 本质上是个单线程的操作,但它比一般/平平的 SET 更“智慧”。
一般/平平的 SET 是二选一:要么成功写入新值,要么回滚旧值并提示毛病。而 SETIFABSENT 的思维是:既然我知道这个 key 当前不存有,那我只管把新的值扔进去,彻底不需求管旧值有没有被删,也不管其他操作在并发下会不会掉链子。
这就好比你在开盲盒,要是你一眼就看出盒子里没有东西,你就不需求去检查盒子里是不是空的,直接掏出一个新的玩具放进去就行。 Redis 是如何实现这种“单线程保险”的呢?实际上挺好办,它利用的是 Redis 的 Single-Threaded 特性。在 Redis 里,所有线程都是跑在一个主线程里的,没有真正的并发,只有指令执行顺序的难题。当 SETIFABSENT 需求访问一个 key 时,它先去读缓存(Cache),要是读缓存里找不到,那它就默认 key 不存有。紧接着,它直接写回内存。在这个过程中,Redis 的锁机制闲时是开着的,忙时是闭着的。出于 Redis 本身就是单线程模型,所有操作都是串行执行的,故此不需求像死锁那样去争论哪位先哪位后。它不需求自旋锁,也不需求复杂的重试逻辑,就连不需求其他线程来帮忙。 举个具体的例子。假设 key "user:123" 的当前值是 "active"。目前有一个脚本想把它改成 "pending",并且要求只有在 "active" 不存有时才改。脚本里写了 if (Redis.exists("user:123")) return 1; 这一行代码要是直接运行,Redis 会认定 "user:123" 存有,脚本直接回毛病,根本不会去设置 "pending"。
这时候,Redis 实际上并没有做任何操作,只是验证了一个不存有的事实。
只有当脚本拿到回值 0 后,才会进入“要是不存有,则设置;要是存有,则报错”的逻辑分支,把内存里的值改成 "pending"。 还有一点特别值得注意,就是 SETIFABSENT 对读操作的限制。Redis 规定,SETIFABSENT 在设置一个新值之前,要是读缓存里已经找到了这个 key,那么它务必把读到的旧值存下来作为“新值”,然后持续执行设置操作。
这样做是为了保证原子性。
比方说,脚本想设置值为 "modified",但读缓存里有个值 "old_value"(就算这行代码还没执行完)。Redis 会把 "old_value" 当作文本里的新值放进去,把真正的值改成 "modified",然后把 "old_value" 原封不动地保留下来。
也就是说,这个指令在设置一个新值的时候,具有“只读”的特性,不能覆盖掉缓存里的旧值。
要是脚本不想保存旧值,那它就不能用 SETIFABSENT。 最终,我们要谈谈这个指令的“生态位”。目前的 Redis 开发人员根本都不推荐用它了。
为啥?出于在高并发场景下,SETIFABSENT 无法解决死锁难题。出于它是单线程的,线程一执行完这个操作,锁就释放了,下一个线程进来就能够竞争这个 key 了。
要是两个线程与此同时执行 SETIFABSENT,其中一个设置了新值,另一个出于缓存里有旧值而黄了了,但这在单线程模型里实际上不会造成真正的死锁,只是表现成一个线程等待锁,另一个持续执行。
不过,更严重的难题是,在分布式环境下,要是不同实例之间的缓存不一致如何办?要是 instance A 认定 key 不存有,instance B 认定 key 存有,两个实例会与此同时尝试操作,要么其中一个操作成功另一个黄了,数据就会慢慢对不齐。 故此,总结来说,SETIFABSENT 是个挺好的测试工具,用来验证一个 key 到底全不,要么验证脚本逻辑是否确实在检查存有性。但在造线上,要不就你能 100% 保证那个 key 绝对不存有,否则不要用它。它的保险性牺牲了,变成了“自当作保险”,这才是最好办埋雷的地方。
相关标签:

猜你喜欢

热门阅读

  • 赖柴尔定理-赖柴尔定理
  • 迪拜哪个国家的城市?-迪拜在哪国城市
  • 李毅吧番号及出处-李毅吧番号及出处
  • 贴春联的由来简介50字-春联由来简述
  • 思乡的名言和出处-思乡名言及出处

其他分站