一、MySQL事务回顾
1.1 MySQL 事务的概念与特性
MySQL 事务(Transaction) 是一种用于管理数据库操作的机制,它确保一组相关的 SQL 操作要么全部成功执行,要么全部失败,从而维护数据的一致性 和 完整性。
事务(Transaction) 是数据库管理系统中的核心概念之一,它通常遵循 ACID 属性:
- 原子性(Atomicity):事务是一个原子操作,要么全部执行成功,要么全部失败。如果其中任何一部分操作失败,整个事务将被回滚,数据库状态将保持不变。
- 一致性(Consistency):事务的执行将数据库从一个一致的状态转移到另一个一致的状态。这意味着事务执行后,数据库必须满足事先定义的完整性约束。
- 隔离性(Isolation):隔离性定义了多个并发事务之间的相互影响程度。不同的隔离级别提供不同的隔离程度,包括读未提交、读已提交、可重复读和串行化等级。
- 持久性(Durability):一旦事务被提交,其结果将永久存储在数据库中,并在系统故障后保持不变。
1.2 MySQL 事务的管理
在MySQL中,可以使用以下关键字来管理事务:
- BEGIN:开始一个新的事务;
- COMMIT:提交事务,将更改保存到数据库;
- ROLLBACK:回滚事务,撤销未提交的更改;
- SAVEPOINT:创建一个保存点,可以在事务中的特定位置回滚;
- SET TRANSACTION:设置事务的属性,如隔离级别;
以下是一个简单的MySQL事务示例:
|
|
如果在事务执行期间发生错误或者需要取消事务,可以使用 ROLLBACK命令 来回滚事务,这将撤销所有在事务中的更改。
|
|
使用事务可以确保数据库的数据完整性,特别是在多个用户同时访问数据库时。根据具体的应用场景和需求,可以选择不同的隔离级别,以平衡并发性和一致性之间的需求。在实际应用中,正确使用事务是确保数据的一致性和可靠性的关键。
二、对 Redis 事务的认识
2.1 Redis 事务的概念
Redis事务(Transaction) 允许将一组 Redis 命令组合成一个单独的、不可中断的操作序列。这意味着,一旦开始了一个 Redis 事务,任何在事务执行期间的其它客户端的命令都不会中断这个事务,而是会排队等待执行,直到事务被提交或取消执行。
Redis 事务的核心概念涉及在 Redis 服务器上创建一个称为 事务队列 的缓冲区。当开启一个事务后,所有与该事务相关的命令都将按顺序排队进入该队列,只有当发送执行事务的命令时,队列中的命令才会以原子的方式依次执行,这确保了这组命令要么全部成功,要么全部失败。
未收到执行命令的事务在 Redis 服务器中处于排队等待状态,虽然将命令发送给了服务器,但并没有执行,同时也不会影响其它客户端命令的正常执行。
在事务队列中,可以包括任意数量的 Redis 命令,包括读取 和 写入操作,以便进行一系列复杂的操作。然而,需要注意的是,Redis 事务并不支持像传统数据库那样的隔离级别和回滚机制。因此,应用程序需要自行处理错误 和 回滚逻辑。
总之,Redis 事务提供了一种 将多个命令打包成一个原子操作的能力 ,这在需要 确保一系列操作的一致性时 非常有用。然而,它与传统数据库的事务有一些区别,特别是在隔离性和持久性方面。
Redis的一个事务从开始到执行会经历以下三个阶段:
- MULTI 命令开启事务;
- 命令入队;
- EXEC 命令按顺序执行队列里的所有命令;
Redis事务 可以一次执行多个命令,并且带有以下两个重要的保证:
- Redis事务是一个单独的隔离操作,事务中的所有命令都会序列化、按顺序地执行;
- Redis事务在执行的过程中,不会被其它客户端发送来的命令请求所打断;
Tips: 事务不会被其它 client连接打断,当一个 client在一个连接中发出 multi命令 后,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接收到 exec命令 后,redis会顺序的执行队列中的所有命令,并将所有命令的运行结果打包一起返回给client,然后此连接就结束事务上下文。
EXEC命令负责触发并执行事务中的所有命令:
- 如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行EXEC,那么事务中的所有命令都不会被执行;
- 如果客户端成功在开启事务之后执行 EXEC,那么事务中的所有命令都会被执行;
当使用AOF方式做持久化的时候,Redis会将事务中命令转化为单个命令后将其写入到磁盘中。 如果Redis服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能只有部分事务命令会被成功写入到磁盘中。 如果Redis在重新启动时发现AOF文件出了这样的问题,那么它会退出,并汇报一个错误。 使用redis-check-aof程序可以修复这一问题,它会移除AOF文件中不完整事务的信息,确保服务器可以顺利启动。
2.2 Redis 事务与 MySQL 事务的比较
虽然 Redis 和 MySQL 都涉及事务的概念,但在实现和应用方面存在一些关键差异。
1、事务的粒度不同
- Redis 事务通常以单个命令的粒度工作。多个 Redis 命令可以放入一个事务中,但每个命令本身是不可分割的,这允许执行一系列简单的操作,但不能跨多个 Redis 命令执行复杂的事务。
- MySQL 事务具有更广泛的粒度,允许跨多个 SQL 语句执行复杂的事务,包括事务的开始和结束。
2、ACID 属性支持度
- Redis 事务支持 原子性(Atomicity) 和 一致性(Consistency),确保一组命令要么全部成功,要么全部失败,并在事务执行后维护数据的一致性。但 Redis 不支持传统数据库中的 隔离性(Isolation) 和 持久性(Durability)。
- MySQL 事务支持完整的 ACID 属性,包括原子性、一致性、隔离性和持久性。这使得 MySQL 事务在需要严格事务完整性的应用中非常有用,如金融和电子商务系统。
3、并发控制差异
- Redis 事务是单线程的,一次只能执行一个事务。这限制了并发性,因为其它客户端的命令必须排队等待执行。同时 Redis 使用乐观锁定来控制并发。
- MySQL 具有强大的并发控制机制,支持多个事务同时执行,可以根据需要选择不同的隔离级别,从而平衡并发性和一致性。
4、错误处理和回滚
- Redis 事务提供了回滚操作的能力,可以使用 DISCARD 命令来取消当前事务并清除已排队的命令。但 Redis 不会自动回滚事务中的错误命令。
- MySQL事务支持显式的回滚操作,可以使用 ROLLBACK 来撤销已执行的事务中的命令,并恢复到事务开始之前的状态。
5、应用场景
- Redis 事务通常用于缓存、队列、计数器等特定用例,其中需要简单的原子操作,但不强调严格的隔离性 和 持久性。
- MySQL 事务广泛用于支持事务完整性的应用程序,如电子商务、金融系统等,这些应用需要强大的 ACID 支持。
总之,Redis事务和MySQL事务在粒度、ACID属性、并发控制、错误处理和应用场景等方面存在重要差异。选择使用哪种类型的事务取决于项目需求和性能要求。在某些情况下,Redis 和 MySQL可以共同使用,以满足不同类型的事务需求。
三、Redis 事务相关命令
Redis 事务是一种强大的功能、允许将一系列 Redis 命令打包在一起,以确保它们要么全部成功执行,要么全部失败 的机制。在 Redis 中,有一些关键的命令用于处理事务,包括 MULTI
、EXEC
、DISCARD
、WATCH
和 UNWATCH
,下面将详细介绍这些命令的作用和用法。
3.1 MULTI 命令
MULTI
命令用于开启一个事务(标记事务块的开始),在执行 MULTI 命令后,Redis 会进入事务模式,接下来的命令都会被逐个加入到事务队列中,等待用户输入 EXEC
命令 开启事务执行。如果 MULTI
命令执行成功,会返回 OK
等待输入要操作命令。
命令语法:
|
|
3.2 WATCH 和 UNWATCH 命令
1、WATCH 命令
WATCH
命令标记所有指定的 key 被监视起来,在事务中有条件的执行(乐观锁)。在开启事务时,如果某个事务中的键被其它客户端修改,可能会导致数据不一致的问题。WATCH
命令用于解决这个问题,它可以监控一组指定的键,如果这些键在事务执行前被其它客户端修改,事务将被取消。
|
|
WATCH 的原理可以简单概括为以下几个步骤:
- 客户端执行 WATCH 命令并指定要监视的键;
- Redis 在服务器端创建一个 监视器(Watcher) 来监视这些键,监视器会记录被监视键的当前状态,通常是版本号或时间戳;
- 客户端进入事务模式,可以执行多个命令,但这些命令不会立即执行,而是进入一个队列中等待执行指令;
- 在执行事务之前,客户端要求服务器检查监视器中被监视键的状态是否发生变化,这是通过比较监视器中的状态与当前键的状态来完成的;
- 如果监视器发现被监视键的状态在执行事务之前已经发生变化,它会通知客户端取消事务,这是乐观锁的核心思想:如果其它客户端在执行 WATCH 和 EXEC 之间修改了被监视的键,那么事务将失败;
- 如果监视器未发现变化,客户端可以执行事务中的命令,这些命令会按顺序执行;
总之,WATCH 的原理是通过在服务器端创建监视器来监控一组键的状态,并在执行事务前检查这些键的状态是否发生变化。如果有其它客户端修改了被监视键,事务将被取消,以确保数据的一致性。这使得 Redis 能够在并发环境中实现事务操作,而不会引发竞态条件等问题。
2、UNWATCH 命令
UNWATCH
命令用于取消对键的监控,是 WATCH
命令的逆操作。如果不再需要监控某些键,可以使用 UNWATCH
来取消监控。
|
|
3.3 EXEC 命令
EXEC
命令用于执行先前放入事务中的命令队列,。在执行 EXEC
命令时,Redis 会按照事务队列中命令的顺序,以原子的方式依次执行这些命令。如果事务中的所有命令都执行成功,EXEC
命令返回一个包含每个命令执行结果的数组;如果有命令执行失败,事务中的所有命令都会被取消。然后恢复正常的连接状态。
|
|
3.3 DISCARD 命令
DISCARD
命令用于放弃当前事务。执行 DISCARD
命令会清空事务队列中的所有命令,之前的操作都不会被真正执行。然后恢复正常的连接状态。这个命令的返回值是一个简单的字符串,总是OK。
如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。
|
|
四、Redis事务中的错误
4.1 Redis事务中的错误
在一个事务的运行期间,可能会遇到两种类型的命令错误:
- 事务在执行 EXEC之前,入队的命令可能会出错,比如说,命令可能会产生语法错误(参数数量错误,参数名错误等等),或者其它更严重的错误,比如内存不足(如果服务器使用maxmemory设置了最大内存限制的话)等;
- 命令可能在 EXEC调用之后失败,举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类;
对于发生在EXEC执行之前的错误,客户端以前的做法是检查命令入队所得的返回值,如果命令入队时返回QUEUED,那么入队成功;否则,就是入队失败;
如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。
不过,从Redis 2.6.5开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC命令时,拒绝执行并自动放弃这个事务。
在Redis 2.6.5以前,Redis只执行事务中那些入队成功的命令,而忽略那些入队失败的命令。而新的处理方式则使得在流水线(pipeline)中包含事务变得简单,因为发送事务和读取事务的回复都只需要和服务器进行一次通讯。至于那些在EXEC命令执行之后所产生的错误,并没有对它们进行特别处理,即使事务中有某个/某些命令在执行时产生了错误,事务中的其它命令仍然会继续执行。