当前位置:首页 > 问答 > 正文

Redis事务到底啥时候用合适,什么时候又不太该用呢?

Redis事务,说白了就是把好几个命令打包,然后一起发送给Redis服务器去执行,它跟咱们平时理解的关系型数据库(比如MySQL)里那种要么全成功、要么全失败的事务不太一样,理解清楚这个差别,就知道该什么时候用它了。

什么时候用Redis事务比较合适?

  1. 需要连续执行多个命令,且不想被插队的时候。 这是Redis事务最核心、最合适的应用场景,因为Redis是单线程处理命令的,所以正常情况下命令都是一个接一个执行的,如果有多个客户端同时连接,你的一个命令发过去,服务器处理完,在等你发下一个命令的这个“空档期”,其他客户端的命令就可能被处理了,事务的作用就是,当你开启事务(MULTI)后,你把想要执行的命令一个个排好队(QUEUED),最后用EXEC命令告诉Redis:“把我刚才排队的这些命令,按顺序一口气执行掉,中间别处理别人的命令”,这保证了这一组命令在执行过程中是连续的,不会被其他操作打断,你想先给一个键counter加1,然后读取它的值,再用这个值去设置另一个键,如果你不用事务,可能在加1之后,读取值之前,别的客户端也修改了counter,你读到的就不是你刚加完1的那个值了,用事务就能确保这三个操作的原子性顺序执行。

  2. 对数据一致性有基本要求,但允许个别失败的非关键操作。 Redis的事务不支持“回滚”(Rollback),这意味着,在你用EXEC执行事务时,队列里的命令会一个接一个地执行,但如果执行到第3个命令时报错了,Redis不会自动取消掉前面已经执行成功的两个命令,它会继续执行剩下的命令,它不能保证所有命令100%成功,这种特性适合用在一些对一致性要求不是那么“性命攸关”的场景,你要更新一个用户的多个信息:昵称、签名、头像链接,即使更新头像链接的命令因为格式错误失败了,但昵称和签名成功更新了,这个结果通常也是可以接受的,你可以在应用层面记录下错误,稍后重试更新头像,而不需要因为头像更新失败就把昵称和签名的修改也撤销掉。

什么时候不太该用或者需要谨慎使用Redis事务?

  1. 事务中包含需要依赖前一个命令结果的后续命令时(即需要“乐观锁”时),直接用事务不行,得配合WATCH。 这是Redis事务一个非常关键的限制,在你用MULTI开启事务后,直到EXEC执行前,你放入队列的所有命令都只是被缓存起来,并没有实际执行,你无法在事务内部根据前一个命令的执行结果来决定下一个命令该是什么,你想先判断库存是否大于零,如果大于零再进行扣减库存的操作,如果你把判断和扣减都放在一个普通事务里,判断命令在EXEC时才会执行,它无法影响早已放入队列的扣减命令,这种情况下,正确的做法是使用WATCH命令,WATCH可以监视一个或多个键,如果在执行EXEC之前,有其他客户端修改了你WATCH的键,那么你整个事务都会执行失败,这样你就可以在失败后重试整个逻辑(包括判断和扣减),这被称为“乐观锁”机制,如果你的业务逻辑有这种“检查后修改”的需求,不能傻傻地用普通事务,必须结合WATCH。

  2. 追求严格的ACID事务特性,要求绝对的全部成功或全部失败时。 正如前面提到的,Redis事务在执行过程中即使某条命令失败,也不会回滚已成功的命令,如果你的业务场景要求极高的一致性,比如金融交易中的转账,必须保证扣款和入账同时成功或同时失败,那么Redis的原生事务就不适合作为实现方案,这种情况下,你应该使用关系型数据库的事务,或者寻求其他更强大的分布式事务解决方案。

  3. 事务过于庞大或耗时。 由于Redis是单线程模型,一个事务执行时会阻塞其他所有命令的处理,如果你打包了一个包含成千上万条命令的超大事务,那么在这个事务执行期间,整个Redis服务器将无法响应其他客户端的任何请求,这可能会导致服务超时甚至被判断为不可用,要确保事务内的命令数量和执行时间都在合理范围内,避免长时间阻塞。

  4. 可以用更高效的原子命令或Lua脚本替代时。 Redis本身提供了很多原子性的单条命令(比如INCR, HMSET等)或者复合命令(比如SETNX等),如果你的需求能用一两条这样的原子命令实现,那就没必要动用事务,对于更复杂的逻辑,Redis支持使用Lua脚本,Lua脚本在执行时也是原子性的(整个脚本在执行期间不会被其他命令打断),并且它比事务更强大,因为脚本内部可以包含逻辑判断、循环等,能够根据中间结果决定后续操作,相当于把WATCH和事务的功能整合在了一起,而且通常更高效,当逻辑复杂到需要WATCH和事务配合时,优先考虑是否能用Lua脚本来实现。

Redis事务最适合的场景是确保一串命令连续、不被中断地执行,但它不是万能的,尤其不适用于需要“回滚”或事务内逻辑判断的场景,理解其“打包执行”和“无回滚”的核心特点,结合WATCH命令或Lua脚本,才能更好地在合适的场景下发挥其作用。 综合参考了Redis官方文档关于Transactions的描述以及技术社区如Redis Labs博客、Stack Overflow上关于事务使用场景的常见讨论。)

Redis事务到底啥时候用合适,什么时候又不太该用呢?