Redis也支持事务(如果这种真的能称之为是事务的话),类似于begin、commit、rollback,Redis中用multi、exec和discard来表示事务的开始,执行,和抛弃。
Redis事务怎么实现的
当服务器收到multi指令后,会把后续的指令放入到缓存队列中;收到exec之后,会按顺序执行 enqueue的指令。所有enqueue的指令,服务器端会返回“QUEUED”,用于表示服务器端收到指令并入队。
真的是事务吗
数据库的原子性需要满足ACID,其中最重要的应该是原子性。
原子性?如果enqueue的指令出现问题的话,其他的指令并不会出现回滚,而是会继续执行,如果和数据库的事务(数据库会通过undo、redo日志等保证)相比的话,这种并不能说是满足原子性;但是确实是所有指令都执行了,或者所有指令都没执行。
一致性?原子性都不太满足,谈一致性好像有点过分
隔离性?这点Redis可以相对自豪些,至少一个事务不会被另一个事务的过程影响(通过Redis的单线程和事务的实现方式);但是,指令本身不会对数据、记录上锁(mysql等的实现方式,可以参考博客中mysql锁部分),不能说完全隔离。
持久化?如果rdb和aof算的话
优化
Redis事务中每条指令到服务器缓存都是通过网络读写,当一个事务内部的指令较多时,需要的网络 IO 时间也会线性增长。所以通常 Redis 的客户端在执行事务时都会结合 pipeline 一起使用,这样可以将多次 IO 操作压缩为单次 IO 操作。
Watch
针对并发操作,Redis中可以使用分布式锁,但其本身是悲观锁,性能一般而言偏差,可通过Watch 变量 ,上乐观锁,Redis 会检查关键变量自 watch 之后,是否被修改了 (包括当前事务所在的客户端)。如果关键变量被人动过了,exec 指令就会返回 null 回复告知客户端事务执行失败,这个时候客户端一般会选择重试。
Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。
Redis事务为什么支持回滚
官方说法如下:
- Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
- 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
数据库的操作日志是先写日志,后操作,而Redis中是先写操作,后写AOF(如果AOF算是操作日志的话),Redis这样做保证了性能、高效的同时,放弃了UNDO、REDO日志的完整性,因此不能基于这些记录进行回滚。