{"content":{"title":"不可不知的Redis秘籍：事务命令全攻略！","body":"在数据处理的世界里，事务（Transaction）是一个不可或缺的概念。它们确保了在一系列操作中，要么所有的操作都成功执行，要么都不执行。这就像是一个“全有或全无”的规则，保证了数据的一致性和完整性。\r\n\r\n今天，我们就来聊聊Redis事务的使用，看看如何通过它来提升我们的数据操作效率和安全性。\r\n\r\n## 一、Redis事务的概念\r\n\r\nRedis 事务的本质是一组命令的集合。事务支持一次执行多个命令，一个事务中所有命令都会被序列化。在事务执行过程，会按照顺序串行化执行队列中的命令，其他客户端提交的命令请求不会插入到事务执行命令序列中。\r\n\r\n![Description](https://ydcqoss.ydcode.cn/ydyx/bbs/1708660784-vXIN1H.png)\r\n\r\n**总结来说：** redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。\r\n\r\n**Redis事务没有隔离级别的概念**\r\n\r\n批量操作在发送 EXEC 命令前被放入队列缓存，并不会被实际执行，也就不存在事务内的查询要看到事务里的更新，事务外查询不能看到。\r\n\r\n**Redis不保证原子性**\r\n\r\nRedis中，单条命令是原子性执行的，但事务不保证原子性，且没有回滚。事务中任意命令执行失败，其余的命令仍会被执行。\r\n\r\n**redis事务的执行阶段**\r\n\r\n* 开始事务(multi)。\r\n* 命令入队。\r\n* 执行事务（exec）\r\n\r\n![Description](https://ydcqoss.ydcode.cn/ydyx/bbs/1708660976-ym9DHq.png)\r\n\r\n## 二、Redis事务优缺点\r\n\r\n对于Redis事务的概念我们已经有了基本的了解，下面我们再来看看它都有哪些优缺点。\r\n\r\n**优点：**\r\n\r\n* 一次性按顺序执行多个Redis命令，不受其他客户端命令请求影响；\r\n* 事务中的命令要么都执行(命令间执行失败互相不影响)，要么都不执行(比如中间有命令语法错误)；\r\n\r\n**缺点：**\r\n\r\n* 事务执行时，不能保证原子性；\r\n* 命令入队每次都需要和服务器进行交互，增加带宽；\r\n\r\n**注意：**\r\n\r\n* 当事务中命令语法使用错误时，最终会导致事务执行不成功，即事务内所有命令都不执行；\r\n* 当事务中命令知识逻辑错误，就比如给字符串做加减乘除操作时，只能在执行过程中发现错误，这种事务执行中失败的命令不影响其他命令的执行。\r\n\r\n## 三、Redis事务相关命令\r\n\r\nRedis事务可以通过一系列命令来执行多个操作，并确保这些操作可以原子性地执行。以下是Redis事务的相关命令及其作用：\r\n\r\n**MULTI：** 开启一个事务。在调用此命令后，Redis 会将后续的命令逐个放入队列中，直到接收到 EXEC 命令为止。\r\n\r\n**EXEC：** 执行事务中的所有操作命令。一旦调用 EXEC 命令，Redis 会原子性地执行队列中的所有命令。\r\n\r\n**DISCARD：** 取消事务，放弃执行事务块中的所有命令。如果不想继续执行事务中的操作，可以使用 DISCARD 命令来清除当前事务队列。\r\n\r\n**WATCH：** 监视一个或多个键，如果在事务执行之前这些键被其他命令所改动，那么事务将会被打断。\r\n\r\n**UNWATCH：** 取消所有由 WATCH 命令监视的键。如果不想继续监视某些键，可以使用 UNWATCH 命令来取消监视。\r\n\r\n需要注意的是，在事务执行过程中，其他客户端提交的命令请求不会插入到事务执行命令序列中，这保证了事务的隔离性。同时，Redis 事务提供了批量操作缓存的功能，即在发送 EXEC 命令前，所有操作都会被放入队列缓存。\r\n\r\n>你还在苦恼找不到真正免费的编程学习平台吗?可以试试【云端源想】！课程视频、知识库、微实战、云实验室、一对一咨询……你想要的全部学习资源这里都有，重点是现在还是免费的！[点这里即可查看！](https://www.ydcode.cn/?sourceId=732)\r\n\r\n## 四、Redis事务的使用\r\n\r\n使用Redis事务的步骤如下：\r\n\r\n* 使用MULTI命令开启一个事务。\r\n* 在事务中执行需要的命令，如SET、GET等。\r\n* 使用EXEC命令提交事务，将事务中的命令一次性发送给Redis服务器执行。\r\n* 如果需要取消事务，可以使用DISCARD命令。\r\n\r\n![Description](https://ydcqoss.ydcode.cn/ydyx/bbs/1708661162-nrc6TB.png)\r\n\r\n下面通过一些示例来讲解一下这些命令的使用方法：\r\n\r\n### 1、正常执行\r\n\r\n```\r\n192.168.xxx.21:6379> multi\r\nOK\r\n192.168.xxx.21:6379> set aa AA\r\nQUEUED\r\n192.168.xxx.21:6379> set bb BB\r\nQUEUED\r\n192.168.xxx.21:6379> set cc CC\r\nQUEUED\r\n192.168.xxx.21:6379> set dd DD\r\nQUEUED\r\n192.168.xxx.21:6379> exec\r\n1) OK\r\n2) OK\r\n3) OK\r\n4) OK\r\n192.168.xxx.21:6379> get aa\r\n\"AA\"\r\n```\r\n\r\n首先，通过执行multi命令开始一个事务块。然后，依次执行了四个set命令，将键\"aa\"、“bb”、“cc\"和\"dd\"分别设置为对应的值\"AA”、“BB”、“CC\"和\"DD”。\r\n\r\n每个set命令执行后返回的结果为\"QUEUED\"，表示该命令已被加入到事务队列中等待执行。\r\n\r\n接下来，通过执行exec命令来提交事务，一次性执行事务队列中的所有命令。执行结果为每个命令的返回值，即\"OK\"。最后，通过执行get aa命令获取键\"aa\"的值，返回结果为\"AA\"。\r\n\r\n### 2、取消事务\r\n\r\n```\r\n192.168.xxx.21:6379> multi\r\nOK\r\n192.168.xxx.21:6379> set aa 11\r\nQUEUED\r\n192.168.xxx.21:6379> set ee EE\r\nQUEUED\r\n192.168.xxx.21:6379> discard\r\nOK\r\n192.168.xxx.21:6379> get aa\r\n\"AA\"\r\n192.168.xxx.21:6379> get ee\r\n(nil)\r\n192.168.xxx.21:6379>\r\n```\r\n\r\n示例代码中，首先，通过执行multi命令开始一个事务块。然后，依次执行了两个set命令，将键\"aa\"设置为值\"11\"，将键\"ee\"设置为值\"EE\"。每个set命令执行后返回的结果为\"QUEUED\"，表示该命令已被加入到事务队列中等待执行。\r\n\r\n接下来，通过执行discard命令来取消事务，放弃执行事务块内的所有命令。执行结果为\"OK\"。\r\n\r\n最后，通过执行get aa命令获取键\"aa\"的值，返回结果为\"AA\"。而执行get ee命令获取键\"ee\"的值时，由于之前已经取消了事务，所以返回结果为\"(nil)\"，表示该键不存在。\r\n\r\n### 3、事务队列中存在命令错误\r\n\r\n如果在事务队列中存在命令性错误（类似于java编译性错误），则执行EXEC命令时，所有命令都不会执行\r\n\r\n```\r\n192.168.xxx.21:6379> multi\r\nOK\r\n192.168.xxx.21:6379> set aa 22\r\nQUEUED\r\n192.168.xxx.21:6379> set bb 33\r\nQUEUED\r\n192.168.xxx.21:6379> setq cc 44\r\n(error) ERR unknown command 'setq'\r\n192.168.xxx.21:6379> set ff FF\r\nQUEUED\r\n192.168.xxx.21:6379> exec\r\n(error) EXECABORT Transaction discarded because of previous errors.\r\n192.168.xxx.21:6379> get ff\r\n(nil)\r\n192.168.xxx.21:6379> get bb\r\n\"BB\"\r\n192.168.xxx.21:6379>\r\n```\r\n\r\n首先，通过执行multi命令开始一个事务块。然后，依次执行了三个set命令，将键\"aa\"设置为值\"22\"，将键\"bb\"设置为值\"33\"，将键\"cc\"设置为值\"44\"。每个set命令执行后返回的结果为\"QUEUED\"，表示该命令已被加入到事务队列中等待执行。\r\n\r\n然而，在执行第三个set命令时，出现了错误。因为Redis中并没有名为\"setq\"的命令，所以返回结果为\"(error) ERR unknown command ‘setq’\"。\r\n\r\n接下来，通过执行exec命令来提交事务，一次性执行事务队列中的所有命令。由于之前已经出现了错误，导致事务被中断，所以执行结果为\"(error) EXECABORT Transaction discarded because of previous errors.\"。\r\n\r\n最后，通过执行get ff命令获取键\"ff\"的值时，由于事务被中断，所以返回结果为\"(nil)“，表示该键不存在。而执行get bb命令获取键\"bb\"的值时，由于事务被中断，所以返回结果为\"BB”。\r\n\r\n### 4、事务队列中存在语法错误\r\n\r\n如果在事务队列中存在语法性错误（类似于java的1/0的运行时异常），则执行EXEC命令时，其他正确命令会被执行，错误命令抛出异常。\r\n\r\n```\r\n192.168.xxx.21:6379> multi\r\nOK\r\n192.168.xxx.21:6379> incr aa\r\nQUEUED\r\n192.168.xxx.21:6379> set ff FF\r\nQUEUED\r\n192.168.xxx.21:6379> set bb 22\r\nQUEUED\r\n192.168.xxx.21:6379> exec\r\n1) (error) ERR value is not an integer or out of range\r\n2) OK\r\n3) OK\r\n192.168.xxx.21:6379> get bb\r\n\"22\"\r\n192.168.xxx.21:6379> get ff\r\n\"FF\"\r\n192.168.xxx.21:6379>\r\n```\r\n\r\n\r\n错误原因：字符串不能累加1\r\n\r\n### 5、watch监控\r\n\r\nwatch 命令可以监控一个或多个键，一旦有其中一个键被修改（被删除），后面的事务就不会执行了。监控一直持续到 EXEC 命令（事务中的命令是在exec之后才执行的，所以在multi命令后可以修改watch监控的键值）\r\n\r\n假设我们通过watch命令在事务执行之前监控了多个Keys，倘若在watch之后有任何Key的值发生了变化，exec命令执行的事务都将被放弃，同时返回Null multi-bulk应答以通知调用者事务执行失败。\r\n\r\n**(1)、执行watch，不执行multi、exec**\r\n\r\n```\r\n192.168.xxx.21:6379> get aa\r\n\"AA\"\r\n192.168.xxx.21:6379> watch aa\r\nOK\r\n192.168.xxx.21:6379> set aa 11\r\nOK\r\n192.168.xxx.21:6379> get aa\r\n\"11\"\r\n192.168.xxx.21:6379>\r\n```\r\n\r\n\r\n\r\n**(2)、执行 watch 命令，通知执行 MULTI、exec**\r\n\r\n```\r\n192.168.xxx.21:6379> set aa Aa\r\nOK\r\n192.168.xxx.21:6379> get aa\r\n\"Aa\"\r\n192.168.xxx.21:6379> multi\r\nOK\r\n192.168.xxx.21:6379> set aa 11\r\nQUEUED\r\n192.168.xxx.21:6379> exec\r\n(nil)\r\n192.168.xxx.21:6379> get aa\r\n\"Aa\"\r\n192.168.xxx.21:6379>\r\n```\r\n\r\n\r\n\r\n**(3)、exec 执行之后，会自动执行 UNWatch 命令，撤销监听操作**\r\n\r\n```\r\n192.168.xxx.21:6379> set aa Aa\r\nOK\r\n192.168.xxx.21:6379> get aa\r\n\"Aa\"\r\n192.168.xxx.21:6379> multi\r\nOK\r\n192.168.xxx.21:6379> set aa 11\r\nQUEUED\r\n192.168.xxx.21:6379> exec\r\n(nil)\r\n192.168.xxx.21:6379> get aa\r\n\"Aa\"\r\n192.168.xxx.21:6379> set aa 11\r\nOK\r\n192.168.xxx.21:6379> get aa\r\n\"11\"\r\n192.168.xxx.21:6379>\r\n```\r\n\r\n\r\n\r\n**(4) 、unwatch撤销监听**\r\n\r\n```\r\n192.168.xxx.21:6379> get bb\r\n\"BBB\"\r\n192.168.xxx.21:6379> watch bb\r\nOK\r\n192.168.xxx.21:6379> multi\r\nOK\r\n192.168.xxx.21:6379> unwatch\r\nQUEUED\r\n192.168.xxx.21:6379> set bb 222\r\nQUEUED\r\n192.168.xxx.21:6379> exec\r\n1) OK\r\n2) OK\r\n192.168.xxx.21:6379> get bb\r\n\"222\"\r\n192.168.xxx.21:6379>\r\n```\r\n\r\n\r\n以上就是Redis事务的概念及相关命令的使用，Redis事务是一个非常强大的工具，它可以帮助我们在处理数据的时候保持数据的一致性和完整性。通过使用Redis事务，可以让我们的数据操作更高效、更安全。\r\n\r\n希望这篇文章能够帮助你更好地理解和使用Redis事务！"},"author":{"user":"https://learnblockchain.cn/people/17279","address":null},"history":null,"timestamp":1708684143,"version":1}