Rueidis(Redis)单个Set操作是否需用DoMulti实现CAS事务?
关于Rueidis中单个Set操作实现CAS事务的疑问解答
问题背景
我在使用Rueidis客户端时,看到官方示例用DoMulti配合MULTI/EXEC实现CAS风格事务(多Set操作),但我只需要执行单个Set()操作,想确认:
- 是否必须将单个
Set()传入DoMulti()才能实现基于被监视key未变更的CAS逻辑? - 我自己写的这段不用
DoMulti()的代码是否可行?当被监视的key值变更或过期时,Set()会不会失败并返回错误?
我的代码示例:
client.Dedicated(func(c rueidis.DedicatedClient) error { // watch keys first c.Do(ctx, c.B().Watch().Key("k1").Build()) // perform read here c.Do(ctx, c.B().Get().Key("k1").Build()) // perform single write _, err := c.Do(ctx, c.B().Set().Key("k1").Value("1").Build()).ToAny() return err })
解答
核心结论
你的代码不可行,WATCH机制必须配合MULTI/EXEC事务才能实现CAS逻辑,单个Set()也必须通过DoMulti()包裹在事务中,否则WATCH的约束不会生效。
原因说明
Redis的WATCH命令本质是在EXEC阶段才会校验被监视的key是否被修改(包括被其他客户端修改、过期删除等):
- 如果不用
MULTI/EXEC,直接执行Set(),Redis不会触发WATCH的校验逻辑,不管被监视的key有没有变化,Set()都会直接覆盖值,完全起不到CAS的作用,也不会返回任何因key变更导致的错误。 - 只有当你把
Set()放在MULTI和EXEC之间,通过DoMulti()执行时,Redis才会在EXEC时检查WATCH的key:如果key已变更,整个事务会被取消,DoMulti()的返回结果会是nil,你可以通过这个结果判断CAS失败。
正确的单个Set操作CAS实现
即使是单个Set(),也需要用MULTI/EXEC包裹,示例代码如下:
import "errors" client.Dedicated(func(c rueidis.DedicatedClient) error { // 监视目标key if err := c.Do(ctx, c.B().Watch().Key("k1").Build()).Error(); err != nil { return err } // 读取key的值(如果业务需要) val, err := c.Do(ctx, c.B().Get().Key("k1").Build()).ToString() if err != nil { // 读取失败时取消WATCH,避免占用Redis资源 _ = c.Do(ctx, c.B().Unwatch().Build()).Error() return err } // 用MULTI/EXEC包裹单个Set操作,触发CAS校验 execRes, err := c.DoMulti( ctx, c.B().Multi().Build(), c.B().Set().Key("k1").Value("1").Build(), c.B().Exec().Build(), ).ToAny() if err != nil { return err } // EXEC返回nil,说明被监视的key已变更,CAS失败 if execRes == nil { return errors.New("CAS执行失败:被监视的key已被修改或过期") } return nil })
补充说明
- 当CAS失败时(EXEC返回nil),你可以根据业务需求选择重试逻辑,比如重新执行整个WATCH-READ-MULTI/EXEC流程。
- 执行WATCH后,如果不再需要事务,记得调用
Unwatch()释放监视,避免Redis一直维护这些监视的key。
内容的提问来源于stack exchange,提问作者gingerbreadboy
相关产品推荐
相关产品推荐

