【redis】事务

redis的事务一次执行多条命令,本质是一组命令的集合,包含开启事务、命令入队、执行事务三个阶段。

redis事务是原子性的,“原子性”仅指“一个事务中的一组操作要么都做(每个操作放入待执行队列成功,就全部操作),要么都不做(入队时出现某句语法错误,或者在watch某些key,而这些key被其他命令改动后,整个事务不执行)

redis事务不具有一致性(保证从一个一致性状态变到另一个一致性状态),因为入队成功后的操作失败,不影响其他操作。redis不支持回滚,原因是官方认为通常语法出错redis事务才会出错,回滚并不能避免语法错误。

mysql的一致性通过undo log(回滚日志)完成,不支持回滚可以让redis简化内部业务,速度更快

那万一事务没执行完服务器挂了怎么办呢?redis会在重启是检测这种坏事务,并以错误退出,可以用redis-check-aof –fix 修复(开启AOF还是很有必要的)。

redis事务支持监控指定key,如果这些key被其他命令更改过,事务会被放弃。即只要监测到“修改”这个动作,事务就被抛弃,并不只是对比最终值的变化(代码段二)。

redis这种监测到watch的key被修改的情况下通知客户端的锁叫乐观锁,它不同于关系型数据库的悲观锁(抢先上锁,让别人改不成),这样做是为了不必等待持有锁的客户端解锁即可执行自己的事务,失败了重试就行了。

redis这种先multi语句再一起exec后等待回复,实际上用到了pipeline,这会极大地提升性能。

支持主动丢弃事务,在执行前调用discard可以清空待执行事务。

watch的key在执行前被修改过:整个事务失败

/*本地初始化set a aa ,set b bb,set c cc.事务multi前watch了b*/
127.0.0.1:6379>watch b
OK

/*之后在另一个redis-cli进程中,set b 'bbb',watch只是监测,不能锁定这些值就不能被更改了,所以这个更改会成功。在此进程multi前、multi后更改结果是一样的,只要在exec前更改都能影响它*/

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 'new a'
QUEUED
127.0.0.1:6379> set b 'new b'
QUEUED
127.0.0.1:6379> set c 'new c'
QUEUED

/*此时执行不是逐一返回语句执行结果,直接是nil,是整个事务被放弃了*/
127.0.0.1:6379> exec
(nil)

/*获取到另一个进程对b的更改*/
127.0.0.1:6379> get b
"bbb"

/*set a语句在事务第一句,在被监测语句前,但是未执行。c在其后也未执行*/
127.0.0.1:6379> get a
"aa"
127.0.0.1:6379> get c
"cc"

watch的key被修改后又被改回原值:整个事务失败

/*客户端1 set a 'aaa'后,watch a*/
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> multi
OK

/*客户端2 更改a的值后,改回watch前的值*/
127.0.0.1:6379> set a 'a'
OK
127.0.0.1:6379> set a 'aaa'
OK

/*客户端1*/
127.0.0.1:6379> set a 'new a'
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get a
aaa

watch的实现是通过检查设置(CAS)实现乐观锁。

运行前就发现的错误:整个事务失败

127.0.0.1:6379> multi
OK

//给a赋值
127.0.0.1:6379> set a 'abc'
QUEUED

//set a 这句只写了一半,语法错误。不用执行就知道是错的,所以redis主动放弃了本次事务的所有操作
127.0.0.1:6379> set a
(error) ERR wrong number of arguments for 'set' command

//重新给a赋值
127.0.0.1:6379> set a 'abc'
QUEUED
127.0.0.1:6379> exec

//执行后返回:由于以前的错误,EXECABORT事务被丢弃。与上一个例子相比,它不再执行,逐个返回三个语句的返回结果,第二句出现语法错误,它就把整个事务抛弃了
(error) EXECABORT Transaction discarded because of previous errors.

//验证,果然获取不到
127.0.0.1:6379> get a
(nil)

运行时才发现的错误:本句失败

//向集合zz中增加元素 abc
127.0.0.1:6379> sadd zz "abc"
(integer) 1

//开启事务
127.0.0.1:6379> multi
OK

//把集合zz当做字符串来取值,这个语句在执行后就会发现会发现zz是集合,这么操作会出错,但是exec前不能知道它就是错的,所以它放入待执行队列成功了
127.0.0.1:6379> get zz
QUEUED

//继续向zz存元素hi
127.0.0.1:6379> sadd zz "hi"
QUEUED
127.0.0.1:6379> exec

//exec后得到回复get zz出错。但是接下来存hi成功了。
1) (error) WRONGTYPE Operation against a key holding the wrong kind of value
2) (integer) 1

//事务结束后获取集合zz中的元素,确认hi确实存成功了
127.0.0.1:6379>smembers zz
1) "abc"
2)  "hi"

综上,exec前能发现的错误可以导致事务不执行,exec时发现的只影响当前语句不影响其他语句。

 

 

 

 

 

 

官方认为以后(lua)脚本可能完全代替其事务,到那时官方会删除事务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值