在Redis操作的时候,有时为了保证数据的正确性,我们必须知道在多个客户端同时处理相同数据时,不谨慎的操作很容易导致数据出错,这里介绍一下基于Redis事务来防止数据出错的方法,以及在某些情况下,使用事务来提升性能的方法。
Redis的事务和传统关系数据库的事务并不相同。在关系数据库中,用户首先向数据库服务器发送BEGIN,然后执行各个相互一致( consistent )的写操作和读操作,最后,用户可以选择.发送COMMIT来确认之前所做的修改,或者发送BOLLBACK来放弃那些修改。
Redis延迟序列:
延迟执行事务有助于提升性能 因为Redis在执行事务的过程中,会延迟执行已入队的命令直到客户端发送EXEC命令为止。因此,包括本书使用的Python客户端在内的很多Redis客户端都会等到事务包含的所有命令都出现了之后,才一次性地将MULTI命令、要在事务中执行的一系列命令,·以及EXEC命令全部发送给Redis,然后等待直到接收到所有命令的回复为止。这种“一次性发送多个命令,然后等待所有回复出现”的做法通常被称为流水线(pipelining ),它可以通过减少客户端与Redis服务器之间的网络通信次数来提升Redis在执行多个命令时的性能。
Redis事务相关命令
1. MULTI
用于标记事务块的开始。Redis会将后续的命令逐个放入队列中,使用EXEC命令原子化地执行这个命令队伍。
2. EXEC
执行事务队列并结束。在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态。
当使用WATCH命令时,只有当受监控的键没有被修改时,EXEC命令才会执行事务中的命令。
3. DISCARD
清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。
如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。
4. WATCH
当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的。
5. UNWATCH
清除所有先前为一个事务监控的键。
如果你调用了EXEC或DISCARD命令,那么就不需要手动调用UNWATCH命令
- 全部执行成功
MyRedis:0>multi
"OK"
MyRedis:0>set name start
"QUEUED"
MyRedis:0>set age 20
"QUEUED"
MyRedis:0>set address end
"QUEUED"
MyRedis:0>exec
1) "OK"
2) "OK"
3) "OK"
但是Redis只支持简单的事务,将执行命令放入队列缓存,当程序中有异常或命令出错,执行DISCARD清空缓存队列不执行队列中命令,其事务过程有以下特点:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个泛原子操作(这里我以泛原子称呼,在某些情况redis的事务不是原子性的,后续会说明):事务中的命令要么全部被执行,要么全部都不执行。(这里的全部不执行不等于mysql 全部回滚,当执行命令过程中,命令没有显示的报错(例如LSET操作设置一个不存在的list),而是在EXEC调用时候某个命令出错,那么在这之前已经执行的命令将不会回滚,所以严格说来,redis并不支持原子性(意思是在exec后,事务某一行有报错(比如操作的key不存在,比如给字符串做算术运算等),那么忽略改行,其他的继续运行))
例如:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set name java
QUEUED
127.0.0.1:6379> set nameList 0 100
QUEUED
127.0.0.1:6379> set name end
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR syntax error
3) OK
127.0.0.1:6379>
这里可以看到中间我们执行了一个失败的语句,但是在命令没有错误时,在exec后就算执行错误,也不会影响其他语句的执行。
我们都知道在mysql中有锁的概念是为了防止数据被多个请求同时修改而产生数据不一致的问题,在redis事务也同样存在这个问题,同一个key被多个客户端同时修改也会产生这个问题,而解决这个问题的方法就是WATCH命令。
注意:watch命令需求在multi命令之前执行。
127.0.0.1:6379> WATCH list
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set list 2
QUEUED
127.0.0.1:6379> set list 3
QUEUED
127.0.0.1:6379> set list 4
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> SADD list 123
(integer) 1
当我们在监听一个key时 在执行事务的时候这个key发生过改变,事务执行会失败。