当前位置: 首页 > redis, 分布式系统, 缓存系统 > 正文

redis事务介绍与应用

关键字:
1 星2 星3 星4 星5 星 (1 次投票, 评分: 5.00, 总分: 5)
Loading ... Loading ...
baidu_share

一.事务的使用
1.Redis事务通过MULTI命令开始。 这条命令总是返回OK。

2.然后用户可以执行多条指令,redis不会马上执行这些指令,还只是放入到队列中。

3.当执行exec指令时,所有的指令执行。

4.调用discard指令,将会flush事物队列,并且退出事物。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
redis 127.0.0.1:6379> multi
 
OK
 
redis 127.0.0.1:6379> set foo 1
 
QUEUED
 
redis 127.0.0.1:6379> incr foo
 
QUEUED
 
redis 127.0.0.1:6379> incr foo
 
QUEUED
 
redis 127.0.0.1:6379> exec
 
1) OK
 
2) (integer) 2
 
3) (integer) 3

从以上会话中能看到multi命令返回的回复是一个数组,每个元素即是事物中每条指令的回复,并且跟指令发布的顺序一样。当redis连接在multi请求下,所有的命令回复都是queued,除非这条指令的语句法不正确。而一些指令语法正确,但执行阶段出错也是允许的。

如以下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
redis 127.0.0.1:6379> multi
 
OK
 
redis 127.0.0.1:6379> set t 13
 
QUEUED
 
redis 127.0.0.1:6379> lpop t
 
QUEUED
 
redis 127.0.0.1:6379> exec
 
1) OK
 
2) (error) ERR Operation against a keyholding the wrong kind of value

对于这种err,需要客户端给予合理的提示。

需要注意的是,所有在队列中的指令都会被执行,redis不会终止指令的执行。

二.取消队列指令
Discard为取消命令队列。可以终断一个事物。不会有命令会被执行,并且连接的状态是正常的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> SET foo 1
 
OK
 
> MULTI
 
OK
 
> INCR foo
 
QUEUED
 
> DISCARD
 
OK
 
> GET foo
 
"1"

三.Optimistic locking using check-and-set(乐观锁)
watch指令在redis事物中提供了CAS的行为。为了检测被watch的keys在是否有多个clients改变时引起冲突,这些keys将会被监控。如果至少有一个watch的key在执行exec命令前被修改,整个事物将会被终止,并且执行exec会得到null的回复。

例如:一个key自增长(假设redis不提供incr的功能)

val = GET mykey

val = val + 1

SET mykey $val

以上指令执行,如果是单一的client,整个操作是没问题的。如果多个client在同一时间操作。如client A与 client B读取了老的值,假如是10,这个值在两个client将会被增长到11,最后set这个key值时,这个key最终是11还不是12.

watch能够很好的处理这种问题:

WATCH mykey

val = GET mykey

val = val + 1

MULTI

SET mykey $val

EXEC

使用以上代码,如果在执行watch与exec指令这段时间里有其它客户端修改此key值,此事物将执行失败。以上形式的锁被称为乐观锁。在大多数使用场合中,多并发将会处理不能的keys,因为冲突不太可能。(通常没有必要重复操作)

四. Watch 指令说明
watch指令是exec指令的执行条件:保证在执行redis事物操作时没有任何client修改被watched的keys.否则事物不会执行。(注意:如果watch一个不稳定的key并且key过期,exec仍然会执行这条指令),当exec指令被调用,所有的keys将是unwatched,无论事物是否被终止。当client连接关闭后,所有的keys也会变成unwatched.为了flush所有的watched keys,也可以使用unwatch指令.有时间使用乐观锁锁住一些keys是很有用的,因为可能需要选择的keys需要事物操作,但是在执行读取现有的keys的内容后发现不需要继续执行,这时只要使用unwatch指令,使此连接能够继续被使用做其它新的事物操作。

ZPOP(取sorted set中socre最低的元素)指令就是通过watch指令实现的

WATCH zset

element = ZRANGE zset 0 0

MULTI

ZREM zset element

EXEC

五.总结
1.redis事物实现,multi开始,所有指令会被放入到队列中。当调用exec后,队列中所有指令会依次被执行。

2.multi-exec中指令执行时,所有指令只要语法合理都会被写入队列中。队列执行时,指令有可能会执行失败,但不影响其它指令执行。

3.redis事物提供了乐观锁,通过watch指令可以实现CAS操作。watch–multi–exec操作

在给key加上乐观锁后,当在执行exec指令前,有其它client修改此key,此事物将执行失败。从而保证原子操作。

说明:对于redis事物的应用其实需要灵活使用,上面介绍的例子是从官网翻译而来。其实在实际中可以通过watch一些标记位来保证多线程下缓存与数据库数据库的一致性。(我们的系统是分布式缓存与数据库的结合使用,缓存需要跟据数据库的一致性很重要,下面举例我们应用中的一个场景:)

如一个service方法,serviceA,执行DAO方法(1),然后更新缓存(2),两个并发线程,线程一执行了方法1,此刻他需要把DAO相关的数据更新到缓存2中,多线程情况下,线程二在线程一执行1后,也同样执行1,2相关的操作,并且比线程一优先完成,这样将导致线程一在执行2时,将出现缓存数据与数据库不一致的现象。(以上是针对单帐号的多并发操作,发生的概率还是存在),对于以上问题我们的解决方案是:

1. 帐号为acc,为每个acc在缓存中增加一个tag标识.

2. 当线程一执行方法1前,设置标记位tag.

3. 当执行方法2时,将会watch tag,并且比较tag是否发生了修改,如果一旦发生修改,则此次缓存操作不将更新,并清空此acc缓存。

4. 如果tag值达到预期,则提交缓存更新,在提交缓存这段时间,如果tag发生变换,则redisexec提交时,会返回null ,这样,虽然缓存内容更新成功,但跟据返回结果,可以即时清除此acc的缓存,从而清空了缓存的脏数据。

5. 通过以上事物保证了缓存数据与数据库数据不一致性的时间很短,甚至可以忽略,因为基本上在MS级别上。

6. 我们的应用在缓存数据不存在acc的情况下,会尝试从数据库读取,而缓存的作用只是缓解我们系统数据库的压力,这样实现,很好的达到了我们的预期效果.

本文固定链接: http://www.chepoo.com/redis-transaction-apply.html | IT技术精华网

redis事务介绍与应用:目前有1 条留言

  1. 沙发
    :

    基于Redis 2.8的事务介绍:http://qifuguang.me/2015/09/30/Redis%E4%BA%8B%E5%8A%A1%E4%BB%8B%E7%BB%8D/

    [回复]

发表评论