Redis学习04–对事务的处理

Redis中的事务
  1. redis对事务的支持目前还比较简单,redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令,由于redis是单线程来处理所有client的请求的所以做到这点是很容易的;
  2. 一般情况下redis在接受到一个client发来的命令后会立即处理并返回处理结果,但是当一个client在一个连接中发出multi命令时,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中,当此连接收到exec命令后,redis会顺序的执行队列中的所有命令,并将所有命令的运行结果打包到一起返回给client,然后此连接就结束事务上下文;
  3. 命令解释:
    1. MULTI:Marks the start of a transaction block. Subsequent commands will be queued for atomic execution using EXEC,开启一个事务,相当于begin transaction;
    2. EXEC:Executes all previously queued commands in a transaction and restores the connection state to normal,When using WATCH, EXEC will execute commands only if the watched keys were not modified,执行事务队列里面的命令,相当于commit;
    3. DISCARD:Flushes all previously queued commands in a transaction and restores the connection state to normal,If WATCH was used, DISCARD unwatches all keys,取消事务队列里面的命令,相当于rollback;
    4. WATCH:Marks the given keys to be watched for conditional execution of a transaction,命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败;也可以调用watch多次监视多个key,这样就可以对指定的key加乐观锁了;
    5. 注意watch的key是对整个连接有效的,事务也一样,如果连接断开,监视和事务都会被自动清除,当然了exec, discard, unwatch命令都会清除连接中的所有监视;
  4. 使用multi命令的例子;
  5. 使用discard命令的例子;
  6. 使用watch命令的例子;
  7. Redis使用事务的bug:
    1. redis只能保证事务的每个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,命令类型不匹配的例子;
    2. 当事务的执行过程中,如果redis服务宕机了,只有部分命令执行了,后面的也就被丢弃了;当然如果我们使用的append-only file方式持久化,redis会用单个write操作写入整个事务内容,即使是这种方式还是有可能只部分写入了事务到磁盘;发生部分写入事务的情况下,redis重启时会检测到这种情况,然后失败退出,可以使用redis-check-aof工具进行修复,修复会删除部分写入的事务内容,修复完后就能够重新启动了;

————————- multi命令 ————————-

— 1.可以看到incr a ,incr b命令发出后并没执行而是被放到了队列中;调用exec后两个命令被连续的执行,最后返回的是两条命令执行后的结果;
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr b
QUEUED
redis 127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1

————————- multi命令 ————————-
————————- discard命令 ————————-
— 1.可以发现incr a incr b都没被执行;discard命令其实就是清空事务的命令队列并退出事务上下文;
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr b
QUEUED
redis 127.0.0.1:6379> discard
OK
redis 127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
redis 127.0.0.1:6379> get a
“1”
redis 127.0.0.1:6379> get b
“1”
————————- discard命令 ————————-
————————- watch命令 ————————-
— 1.常规情况,设置a的值;如果是一个incr a的操作,使用以下命令去实现,由于get a和set a两个命令并不能保证是连续执行的(get操作不在事务上下文中),很可能有两个client同时做这个操作,结果期望是加两次a从原来的1变成3,但是很有可能两个client的get a,取到都是1,造成最终加两次结果却是2,主要问题我们没有对共享资源a的访问进行任何的同步;
也就是说redis没提供任何的加锁机制来同步对a的访问;
redis 127.0.0.1:6379> get a
“1”
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> set a 2
QUEUED
redis 127.0.0.1:6379> exec
1) OK
redis 127.0.0.1:6379> get a
“2”
— 2.使用watch命令对变量加锁;
redis 127.0.0.1:6379> watch a
OK
redis 127.0.0.1:6379> get a
“2”
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> set a 3
QUEUED
redis 127.0.0.1:6379> exec
1) OK
redis 127.0.0.1:6379> get a
“3”
— 3.被watch的key如果在事务在exec之前被修改了,则返回错误;
————————- watch命令 ————————-
————————- Redis中事务的bugs ————————-
— 1.数据类型不一致导致事务失败;
redis 127.0.0.1:6379> set a 1
OK
redis 127.0.0.1:6379> lpush list1 5
(integer) 1
redis 127.0.0.1:6379> set c 1
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr list1
QUEUED
redis 127.0.0.1:6379> incr c
QUEUED
redis 127.0.0.1:6379> exec
1) (integer) 2
2) (error) ERR Operation against a key holding the wrong kind of value
3) (integer) 2
— 2.当事务的执行过程中,如果redis服务宕机了,只有部分命令执行了,后面的也就被丢弃了;
————————- Redis中事务的bugs ————————-

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注