事务
事务
在Redis中,事务是通过MULTI、EXEC、DISCARD和WATCH四个命令来实现的,它们构成了一个队列,通过EXEC命令来一次性、顺序、无中断地执行队列中所有命令。在执行EXEC之前,如果有任何错误(比如命令的语法错误或命令用错),Redis将立即停止并返回错误。但是,如果EXEC命令执行后,在执行队列中的命令时出现错误,它并不会回滚之前已经执行成功的命令,而是继续执行队列中的其他命令。所以Redis的事务没有所谓的“回滚”机制。
事务队列
当客户端发送 MULTI
命令后,Redis开始记录后续的命令,并将这些命令放入一个队列中。当遇到 EXEC
命令时,Redis会按照FIFO(先进先出)的顺序执行队列中的所有命令。
错误处理
在事务执行过程中,可能会遇到命令执行失败的情况。对于错误的处理,Redis采用的策略是:即使某个命令执行失败,事务中的其他命令仍然会继续执行。然而,整个事务的返回结果会包含错误信息,以便客户端了解事务执行过程中发生的错误。
WATCH命令与乐观锁
WATCH
命令允许客户端监视一个或多个Key,以确保在事务执行期间,这些Key的值没有发生变化。这种机制被称为乐观锁(Optimistic Locking)。如果在事务执行之前,有其他客户端修改了这些被监视的Key,那么事务将被中断,并返回一个错误。乐观锁可以在一定程度上解决并发场景下的数据一致性问题。
ua脚本优势
那lua脚本相比起内置事务,就有以下几大优势:
1、更强的灵活性:redis事务无法依赖于前一个命令的结果来进行后续的处理,但Lua脚本提供了更强的灵活性。通过编写自己的脚本,开发者可以设计出更符合业务需求的事务处理方案。
2、提供原子性操作:虽然Redis的事务可以保证一组命令的原子性执行,但并不能保证在执行过程中数据不被其他客户端修改。而Lua脚本则可以避免这种情况,所有命令在同一Lua脚本中执行,从而保证数据在执行过程中的一致性。
3、减轻压力:使用Redis内置的事务功能,每个命令都需要进行网络通信,这会增加服务器的压力。而将命令放入Lua脚本执行,只需要一次通信,既减轻了服务器压力又提高了执行效率。
代码实践示例
假设,我们有一个需求,库存扣减,这是一个典型的事务应用场景。
首先,我们来看Redis事务的一个使用的例子:
在上面的代码中,我们必须注意其他客户端可能在事务过程中改变accountA的值。写起来比较复杂,而且理解起来也需要一点时间。
然后,我们来看看在这种场景下如何使用Lua脚本:
redis Eval 命令基本语法如下:
redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]
参数说明:
- script: 参数是一段 Lua 5.1 脚本程序。脚本不必(也不应该)定义为一个 Lua 函数。
- numkeys: 用于指定键名参数的个数。
- key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
- arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
在这个Lua脚本的示例中,我们只需在服务器端就可以完成所有的任务,而不需要担心数据在执行过程中被其他客户端修改。这个版本的代码更简洁易懂,性能也更好。
因此,若要保证数据的一致性和原子性,绝大多数开发者会选择使用Lua脚本完成Redis的事务操作。