Redis事务的概念:
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
- Redis事务没有隔离级别的概念:
批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。
- Redis不保证原子性:
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
- Redis事务的三个阶段:
- 开始事务
- 命令入队
- 执行事务
Redis事务相关命令:
-
watch key1 key2 … :
监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 ) -
multi :
标记一个事务块的开始( queued ) -
exec :
执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 ) -
discard :
取消事务,放弃事务块中的所有命令 -
unwatch :
取消watch对所有key的监控
在命令行测试redis 事务
- 正常执行
127.0.0.1:6379> multi
OK
# 进入队列的数据,再查询 就会提交事务之后才会查到
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v2"
4) OK
- 放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> set k3 33
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get k2
"v2"
- 若在事务队列中存在命令性错误(注意啊 命令错误),则执行EXEC命令时,所有命令都不会执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3 # 这是一条错误命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k4 # 此时命令未被执行
(nil)
命令错误和语法错误有什么区别?
- 命令错误 指的是你 打错单词了
- 语法错误 值得是你 语法用错地方了
- 若在事务队列中存在语法性错误,则执行EXEC命令时,其他被执行,错误命令抛出异常。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 # 对k1 (value: v1)进行 +1
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> set k3 33
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> EXEC
1) (error) ERR value is not an integer or out of range # 第一条命令报错,其他的正确执行
2) OK
3) OK
4) "33"
- watch
使用watch检测balance,事务期间balance数据未变动,事务执行成功
127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> set debt 0 # 代表着欠额
OK
127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby balance 20 # 减去 20
QUEUED
127.0.0.1:6379> incrby debt 20 # 加 20,因为之前减去了20,所以在欠额上要 + 20,保持数据的统一性
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 80
2) (integer) 20
-
使用 watch监听 balance,在开启事务后
-
在新窗口执行中的操作,更改balance的值,模拟其他客户端在事务执行期间更改 watch监控的数据
-
然后再执行标注 1后命令,之心 EXEC后 事务未成功执行
时间 | 客户端A | 客户端B |
---|---|---|
T1 | set balance 100 watch balance | |
T2 | get balance set balance 800 | |
T3 | decrby balance 20 exec 事务失败 | |
T4 | get balance balance: 800 |
- 一旦执行 EXEC之后,无论事务使用执行成功,watch对变量的监控都将被取消。
- 当事务执行失败后,需重新执行 watch命令对变量进行监控,并开启新的事务进行操作。
总结:
watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。
实质:
WATCH 只会在数据被其他客户端抢先修改了的情况下通知执行命令的这个客户端(通过 WatchError 异常)但不会阻止其他客户端对数据的修改
-
watch其实就是redis提供的一种乐观锁,可以解决并发修改问题
-
watch会在事物开始前盯住一个或多个关键变量,当服务器收到exec指令要顺序执行缓存中的事物队列时,redis会检查关键变量自watch后是否被修改
-
WATCH 只会在数据被其他客户端抢先修改了的情况下通知执行命令的这个客户端(通过 WatchError 异常)但不会阻止其他客户端对数据的修改
python测试 redis 事务
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import redis
r = redis.Redis(host='127.0.0.1')
pipe = r.pipeline()
pipe.multi() #开启事务
pipe.set('key2', 4) #存储子命令
pipe.execute() #执行事务
print(r.get('key2'))
使用python测试redis事物
mysql 与 redis的事务区别
- mysql:
begin #显式地开启一个事务
commit #提交事务,对数据库进行的所有写操作变为永久性的
rollback #结束用户的事务,并撤销正在进行的所有未提交的写操作
- redis支持简单的事务:
multi #标记事务的开始
exec #执行事务的commands队列
discard #结束事务,并清除commands队列
命令区别:
mysql | redis | |
---|---|---|
开启 | start transaction / begin | multi |
语句 | SQL | 命令 |
失败 | roolback回滚 | discart取消 |
成功 | commit提交 | exec执行事务的命令队列 |
取消事务(回滚)区别:
-
mysql回滚为sql全部成功才执行,一条sql失败则全部失败,执行rollback后所有语句造成的影响消失
-
redis的discard只是结束本次事务,正确命令造成的影响仍然还在.
- 如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
- 如果在一个事务中出现运行错误,那么正确的命令会被执行。
乐观锁,悲观锁区别:
MySQL:
- 悲观的想法:有人和我抢,给ticket加锁,只有我能操作。(悲观锁)
- 乐观的想法:我只需要注意有没有人更改ticket的值就可以了。(乐观锁)
Redis:
redis的事务中,启动的是乐观锁,只负责监测key有没有被改动
Redis Watch 命令用于监视一个(或多个) key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断 (也是乐观锁)
事务隔离性 区别:
-
MySQL:
四大隔离级别 -
Redis:
开启事务将,任务(数据)放入队列,没有隔离级别这一思想。