《Redis实战》—读书笔记—第3章

《Redis实战》—读书笔记—第3章

2017年10月04日00:50:03

3.1 字符串

字符串可以存储3种类型的值。①字节串②整数③浮点数

命令 用例和描述
incr incr key-name —将键存储的值加上1
decr decr key-name — 将键存储的值减去1
incrby incrby key-name amount — 将键存储的值加上整数amount
decrby decrby key-name amount — 将键存储的值减去整数amount
incrbyfloat incrbyfloat key-name amount — 将键存储的值加上浮点数amount,这个命令在redis2.6或以上的版本可用

​ 当用户将一个值存储到Redis字符串里面的时候,如果这个值可以被解释(interpret)为十进制整数或者浮点数,那么Redis会察觉到这一点,并允许用户对这个字符串执行各种Incr和decr操作。

​ 如果用户对一个不存在的键或者一个保存了空串的键执行自增或者自减操作,那么Redis在执行操作时会将这个键的值当作是0来处理。

​ 如果用户尝试对一个值无法被解释为整数或者浮点数的字符串键支持性自增或者自减操作,那么Redis将向用户返回一个错误。

交互展示了Redis的incr和decr操作

>>> conn = redis.Redis()
>>> conn.get('key')
>>> conn.incr('key')
1
>>> conn.incr('key', 15)
16
>>> conn.decr('key', 5)
11
>>> conn.get('key')
'11'
>>> conn.set('key', '13')
True
>>> conn.incr('key')
14

供Redis处理子串和二进制位的命令

命令 用例和描述
append Append key-name value — 将值value追加到给定键key-name当前存储的值的末尾
getrange getrange key-name start end — 获取一个由偏移量start至偏移量end范围内所有字符组成的子串,包括start和end在内
setrange setrange key-name offset value — 将从start偏移量开始的子串设置为给定值
getbit getbit key-name offset — 将字节串看作是二进制位串(bit string), 并返回位串中偏移量为offset的二进制位的值
setbit setbit key-name offset value — 将字符串看作是二进制位串,并将位串中偏移量为offset的二进制位的值设置为value
bitcount bitcount key-name [start end] — 统计二进制位串里面值位1的二进制位的数量,如果给定了可选的start偏移量和end偏移量,name只对偏移量指定范围内的二进制位进行统计
bitop bitop operation dest-key key-name [key-name …] — 对一个或多个二进制位串执行包括并(AND)、或(OR)、异或(XOR)、非(NOT)在内的任意一种按位运算操作,并将计算得出的结果保存在dest-key键里面

getrange和substr Redis现在的getrange命令是由以前的substr命令改名而来的,因此,python客户端至今仍然可以使用substr()方法来获取子串,但如果读者使用的是2.6或以上版本的Redis,name最好还是使用getrange()方法来获取子串。

这个交互示例展示了Redis的子串操作和二进制位操作

>>> conn.append('new-string-key', 'hello ')
6L
>>> conn.append('new-string-key', 'world!')
12L
>>> conn.substr('new-string-key', 3, 7)
'lo wo'
>>> conn.setrange('new-string-key', 0, 'H')
12
>>> conn.setrange('new-string-key', 6, 'W')
12
>>> conn.get('new-string-key')
'Hello World!'
>>> conn.setrange('new-string-key', 11, ', how are you?')
25
>>> conn.get('new-string-key')
'Hello World, how are you?'
>>> conn.setbit('another-key', 2, 1)
0   //setbit命令会返回二进制位被设置之前的值
>>> conn.setbit('another-key', 7, 1)
0
>>> conn.get('another-key') 
'!'         // 0100 001 从高位到低位解释,即为33

3.2 列表

允许用户从序列的两端推入或者弹出元素,获得列表元素,以及执行各种常见的列表操作。除此之外,列表还可以用来存储任务信息、最近浏览过的文章或者常用联系人信息。

一些常用的列表命令

命令 用例和描述
rpush rpush key-name value [value …] — 将一个或多个值推入列表的右端
lpush lpush key-name value [value …] — 将一个或多个值推入列表的左端
rpop rpop key-name — 移除并返回列表最右端的元素
lpop lpop key-name — 移除并返回列表最左端的元素
lindex lindex key-name offset — 返回列表中偏移量为offset的元素
lrange lrange key-name start end — 返回列表从start偏移量到end偏移量范围内的所有元素,其中偏移量为start和偏移量为end的元素也会包含在被返回的元素之内
ltrim ltrim key-name start end — 对列表进行修剪,只保留从start偏移量到end偏移量范围内的元素,其中偏移量为start何偏移量为end的元素也会被保留

阻塞式的列表弹出命令以及在列表之间移动元素的命令

命令 用例和描述
blpop blpop key-name [key-name …] timeout — 从第一个非空列表中弹出位于最左端的元素,或者在timeout秒之内阻塞并等待可弹出的元素出现
brpop brpop key-name [key-name …] timeout — 从第一个非空列表中弹出位于最右端的元素,或者在timeout秒之内阻塞并等待可弹出的元素出现
rpoplpush rpoplpush source-key dest-key — 从source-key列表中弹出位于最右端的元素,然后将这个元素推入dest-key列表 的最左端,并向用户返回这个元素
brpoplpush lpoprpush source-key dest-key timeout — 从source-key列表中弹出位于最右端的元素,然后将这个元素推入dest-key列表 的最左端,并向用户返回这个元素;如果source-key为空,name在timeout秒之内阻塞并等待可弹出的元素出现

展示了redis列表的阻塞弹出命令以及元素移动命令

>>> conn.rpush('list', 'item1')
1
>>> conn.rpush('list', 'item2')
2
>>> conn.rpush('list2', 'item3')
1
>>> conn.brpoplpush('list2', 'list', 1)
'item3'
>>> conn.brpoplpush('list2', 'list', 1)
>>> conn.lrange('list', 0, -1)
['item3', 'item1', 'item2']
>>> conn.brpoplpush('list', 'list2', 1)
'item2'
>>> conn.blpop(['list', 'list2'], 1)
('list', 'item3')       // 弹出了list的item3元素
>>> conn.blpop(['list', 'list2'], 1)
('list', 'item1')       // 弹出了list的item1元素
>>> conn.blpop(['list', 'list2'], 1)
('list2', 'item2')      // 弹出了list2的item2元素
>>> conn.blpop(['list', 'list2'], 1)
>>> 

3.3 集合

Redis的集合以无序的方式来存储多个各不相同的元素,用户可以快速地对集合执行添加元素操作、移除元素操作以及检查一个元素是否存在于集合里。

对常用的集合命令介绍,包括插入命令、移除命令、将元素从一个集合移动到另一个集合的命令,以及对多个集合执行交集运算、并集运算和差集运算的命令。

一些常用的集合命令

命令 用例和描述
sadd sadd key-name item [item …] — 将一个或多个元素添加到集合里面,并返回被添加元素当中原本并不存在于集合里面的元素数量
srem srem key-name item [item …] — 从集合里面移除一个或多个元素,并返回被移除元素的数量
sismember sismember key-name item — 检查元素item是否存在于集合key-name
scard scard key-name — 返回集合包含的元素的数量
smembers smembers key-name — 返回集合包含的元素的数量
srandmember srandmember key-name [count] — 从集合里面随即地返回一个或多个元素。当count为正数时,命令返回随机元素不会重复;当count为负数时,命令返回的随机元素可能会出现重复
spop spop key-name — 随机地移除集合中的一个元素,并返回被移除的元素
smove smove source-key dest-key item — 如果集合source-key包含元素item,name从集合source-key里面移除元素item,并将元素item添加到集合dest-key中;如果item被成功移除,那么命令返回1,否则返回0

Python交互式的展示了一些命令的使用

>>> conn.sadd('set-key', 'a','b','c')
3
>>> conn.srem('set-key', 'c', 'd')
True    // screm函数在元素被成功移除时返回True,移除失败时返回False;注意这是Python客户端的一个bug,实际上redis的srem命令返回的是被移除元素的数量,而不是布尔值
>>> conn.srem('set-key', 'c', 'd')
False
>>> conn.scard('set-key')
2
>>> conn.smembers('set-key')
set(['a', 'b'])
>>> conn.smove('set-key', 'set-key2', 'a')
True
>>> conn.smove('set-key', 'set-key2', 'c')
False
>>> conn.smembers('set-key2')
set(['a'])

用于组合和处理多个集合的Redis命令

命令 用例和描述
sdiff sdiff key-name [key-name …] — 返回那些存在于第一个集合、但不存在于其他集合中的元素(数学上的差集运算)
sdiffstore sdiffstore dest-key key-name [key-name …] — 将那些存在于第一个集合但并不存在于其他集合中的元素(数学上的差集运算)存储到dest-key键里面
sinterstore sinterstore dest-key key-name [key-name …] — 将那些同时存在于所有集合的元素(数学上的交集运算)存储到dest-key键里面
sunion sunion key-name [key-name …] — 返回那些至少存在于一个集合中的元素(数学中的并集计算)
sunionstore sunionstore dest-key key-name [key-name …] — 将那些至少存在于一个集合中的元素(数学上的并集运算)存储到dest-key键里面

Redis的差集运算、交集运算以及并集运算

>>> conn.sadd('skey1', 'a', 'b', 'c', 'd')
4
>>> conn.sadd('skey2', 'c', 'd', 'e', 'f')
4
>>> conn.sdiff('skey1', 'skey2')
set(['a', 'b'])
>>> conn.sinter('skey1', 'skey2')
set(['c', 'd'])
>>> conn.sunion('skey1', 'skey2')
set(['a', 'c', 'b', 'e', 'd', 'f'])

3.4 散列

Redis的散列可以让用户将多个键值对存储到一个Redis键里面。从功能上来说,Redis为散列值提供了一些与字符串值相同的特性,使得散列非常适用于将一些相关的数据存储在一起。我们可以把这个数据聚集看作是关系数据库中的行,或者文档数据库中的文档。

对常用的散列命令进行介绍:添加和删除键值对的命令、获取所有键值对的命令,以及对键值对的值进行自增或者自减得命令。

命令 用例和描述
hmget hmget key-name key [key …] — 从散列里面获取一个或多个键的值
hmset hmset key-name key value [key value …] — 为散列里面的一个或多个值设置值
hdel hdel key-name key [key …] — 删除散列里面的一个或多个键值对,返回成功找到并删除的键值对数量
hlen hlen key-name — 返回散列包含的键值对数量

hdel命令已经在第1章中介绍过了,而hlen命令以及用于一次读取或者设置多个键的hmget和hmset则是新出现的命令。像hmget和hmset这种批量处理多个键的命令既可以给用户带来方便,又可以通过减少命令的调用次数以及客户端与Redis之间的通信往返次数来提升Redis性能。

>>> conn.hmset('hash-key', {'k1':'v1', 'k2':'v2', 'k3':'v3'})
True
>>> conn.hmget('hash-key', ['k2', 'k3'])
['v2','v3']
>>> conn.hlen('hash-key')
3
>>> conn.hdel('hash-key', 'k1', 'k3')
True

散列的其他几个批量操作命令,以及一些和字符串操作类似的散列命令。

命令 用例和描述
hexists hexists key-name key — 检查给定键是否存在于散列中
hkeys hkeys key-name — 获取散列包含的所有键
hvals hvals key-name — 获取散列包含的所有值
hgetall hgetall key-name — 获取散列包含的所有键值对
hincrby hincrby key-name key increment — 将键key存储的值加上整数increment
hincrbyfloat hincrbyfloat key-name key increment — 将键key存储的值加上浮点数increment

尽管有hgetall存在,但hkeys和hvalues也是非常有用的:如果散列包含的值非常大,name用户可以先使用hkeys取出散列包含的所有键,然后再使用hget一个接一个地取出键的值,从而避免因为一次获取多个大体积的值而导致服务器阻塞。

hincrby和hincrbyfloat会让我们想起incrby和incrbyfloat,这两对命令拥有相同的语义,它们的不同在于hincrby和hincrbyfloat处理的是散列,而不是字符串。

>>> conn.hmset('hash-key2', {'short':'hello', 'long': 1000*'1'})
True
>>> conn.hkeys('hash-key2')
['long', 'short']
>>> conn.hexists('hash-key2', 'num')
False
>>> conn.hincrby('hash-key2', 'num')
1L
>>> conn.hexists('hash-key2', 'num')
True

3.5 有序集合

​ 和散列存储着键与值之间的映射类似,有序集合也存储着成员与分值之间的映射,并且提供了分值处理命令,以及根据分值大小有序地获取(fetch)或扫描(scan)成员和分值的命令。本书曾在第1章使用有序集合实现过基于发表时间排序的文章列表和基于投票数量排序的文章列表,还在第2章使用有序集合存储cookie的过期时间。

​ 对有序集合进行介绍,其中包括向有序集合添加新元素的命令、更新已有元素的命令,以及对有序集合进行交集运算和并集运算的命令。

命令 用例和描述
zadd zadd key-name score member [score member …] — 将带有给定分值的成员添加到有序集合里面
zrem zrem key-name member [member …] — 从有序集合里面移除给定的成员,并返回被移除成员的数量
zcard zcard key-name — 返回有序集合包含的成员数量
zincrby zincrby key-name increment member — 将member成员的分值加长increment
zcount zcount key-name min max — 返回分值介于min和max之间的成员数量
zrank zrank key-name member — 返回成员member在有序集合中的排名
zscore zscore key-name member — 返回成员member的分值
zrange zrange key-name start stop [WITHSCORES] — 返回有序集合中排名介于start和stop之间的成员,如果给定了可选的WITHSCORES选项,name命令会将成员的分值也一并返回

交互展示用例

>>> conn.zadd('zset-key', 'a', 3, 'b', 2, 'c', 1)
3
>>> conn.zcard('zset-key')
3
>>> conn.zincrby('zset-key', 'c', 3)
4.0
>>> conn.zscore('zset-key', 'b')
2.0
>>> conn.zrank('zset-key', 'c')
2   // 此为排名不是分值
>>> conn.zcount('zset-key', 0, 3)
2L
>>> conn.zrem('zset-key', 'b')
True
>>> conn.zrange('zset-key', 0, -1, withscores=True)
[('a', 3.0), ('c', 4.0)]

有序集合的范围型数据获取命令和范围性数据删除命令,以及并集命令和交集命令

命令 用例和描述
ZRevRank zrevrank key-name member — 返回有序集合里成员member的排名,成员按照分值从大到小排列
ZRevRange zrevrange key-name start stop [WITHSCORES] — 返回有序集合给定排名范围内的成员,成员按照分值从大到小排列
ZRangeByScore zrangebyscore key min max [WITHSCORES] [LIMIT offset count] — 返回有序集合中,分值介于min和max之间的所有成员
ZRevRangeByScore ZRevRangeByScore key max min [WITHSCORES] [LIMIT offset count] — 获取有序集合中分值介于min和max之间的所有成员,并按照分值从大到小的顺序来返回他们
ZRemRangeByRank ZRemRangeByRank key-name start stop — 移除有序集合中排名介于start和stop之间的所有成员
ZRemRangeByScore ZRemRangeByScore key-name min max — 移除有序集合中分值介于min和max之间的所有成员
ZInterStore ZInterStore dest-key key-count key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] — 对给定的有序集合执行类似于集合的交集运算
ZUnionStore ZUnionStore dest-key key-count key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] — 对给定的有序集合执行类似于集合的并集运算
>>> conn.zadd('zset-1', 'a', 1, 'b', 2, 'c', 3)
3
>>> conn.zadd('zset-2', 'b', 4, 'c', 1, 'd', 0)
3
>>> conn.zinterstore('zset-i', ['set-1', 'set-2'])
2L
>>> conn.zrange('zset-i', 0, -1, withscores=True)
[('c', 4.0), ('b', 6.0)]
>>> conn.zunionstore('zset-u', ['zset-1', 'zset-2'], aggregate = 'min')
4L
>>> conn.zrange('zset-u', 0, -1, winthscores=True)
[('d', 0.0), ('a', 1.0), ('c', 1.0), ('b', 2.0)]
>>> conn.sadd('set-1', 'a', 'd')
2
>>> conn.zunionstore('zset-u2', ['zset-1', 'zset-2', 'set-1'])
4L
>>> conn.zrange('zset-u2', 0, -1, withscores=True)
[('d', 1.0), ('a', 2.0), ('c', 4.0),('b', 6.0)]

3.6 发布与订阅

​ 一般来说,发布与订阅(又称pub/sub)的特点是订阅者(listener)负责订阅频道(channel),发送者(publisher)负责向频道发送二进制字符串消息(binary string message)。每当有消息被发送至给定频道时,频道的所有订阅者都会受到消息。我们也可以把频道看作是电台,其中订阅者可以同时收听多个电台,而发送者可以在任何电台发送消息。

Redis提供的5个发布与订阅命令

命令 用例和描述
subscribe subscribe channel [channel …] — 订阅给定的一个或多个频道
unsubscribe unsubscribe [channel [channel …]] — 退订给定的一个或多个频道,如果执行时没有给定任何频道,name退订所有频道
publish publish channel message — 向给定频道发送消息
psubscribe psubscribe pattern [pattern …] — 订阅与给定模式相匹配的所有频道
punsubscribe punsubscribe [pattern [pattern …]] — 退订给定的模式,如果执行时没有给定任何模式,name退订所有模式

​ 考虑到publish命令和subscribe命令在Python客户端的实现方式,一个比较简单的演示发布与订阅的方法,就是像代码清单3-11那样使用辅助线程(helper thread)来执行publish命令。

这个交互示例展示了如何使用Redis中的publish命令以及subscribe命令

>>> def publisher(n):
        time.sleep(1)
        for i in xrange(n):
            conn.publish('channel', i)  // 在发布消息之后进行短暂的休眠,让消息可以一条接一条的出现
            time.sleep(i)
>>> def run_punsub():
        threading.Thread(target=publisher, args(3,)).start()
        pubsub = conn.pubsub()
        pubsub.subscribe(['channel'])
        count = 0
        for item in pubsub.listen():
            print item  // 打印接收到的每条消息
            count += 1
            if count == 4:  // 在接受到一条订阅反馈消息和三条发布者发送的消息之后,执行退订操作,停止监听新消息
                pubsub.unsubscribe()
            if count == 5:  // 客户端在接收到退订反馈消息之后就不再接收消息
                break
>>> run_punsub()    //在刚开始订阅一个频道的时候,客户端会接收到一条关于被订阅频道的反馈消息。
{'pattern': None, 'type': 'subscribe', 'channel': 'channel', 'data': 1L}
{'pattern': None, 'type': 'message', 'channel': 'channel', 'data': '0'}
{'pattern': None, 'type': 'message', 'channel': 'channel', 'data': '1'}
{'pattern': None, 'type': 'message', 'channel': 'channel', 'data': '2'}
{'pattern': None, 'type': 'unsubscribe', 'channel': 'channel', 'data': 0L}

​ 虽然Redis的发布与订阅模式非常有用,但不常使用的原因有两个。

​ 第一个原因和Redis系统的稳定性有关。对于旧版Redis来说,如果一个客户端订阅了某个或某些频道,但它读取消息的速度却不够快的话,那么不断挤压的消息就会使得Redis输出缓冲区的体积变得越来越大,这可能会导致Redis的速度变慢,甚至直接崩溃。也可能导致Redis被操作系统强制杀死,甚至导致操作系统本身不可用。新版的Redis不会出现这种问题,因为它会自动断开不符合client-output-buffer-limit pubsub配置选项要求的订阅客户端(本书第8章将对这个选项做更详细的介绍)。

​ 第二个原因和数据传输的可靠性有关。任何网络系统在执行操作时都可能会遇上断线情况,而断线产生的连接错误通常会使得网络连接两端中的其中一端进行重新连接。本书使用的Python语言的Redis客户端会在连接失效时自动进行重新连接,也会自动处理连接池(connection pool),诸如此类。但是,如果客户端在执行订阅操作的过程中断线,那么客户端将丢失在断线期间发送的所有消息,因此依靠频道来接收消息的用户可能会对Redis提供的publish命令和subscribe命令的语义感到失望。

​ 基于以上两个原因,本书在第6章编写了两个不同的方法来实现可靠的消息传递操作,这两个除了可以处理网络断线之外,还可以防止Redis因为消息积压而耗费过多内存(这个方法即使对于旧版Redis也是有效的)。

​ 如果你喜欢简单易用的PUBLISH命令和SUBSCRIBE命令,并且能够承担可能会丢失一小部分数据的风险,那么你也可以继续使用Redis提供的发布与订阅特性,而不是8.5节中提供的实现,只要记得先把client-output-buffer-limit pubsub选项设置好就行了。

​ 到目前为止,本书介绍的大多数命令都是与特定数据类型相关的。接下来的一节要介绍的命令你可能也会用到,但它们既不属于Redis提供的5种数据结构,也不属于发布与订阅特性。

3.7 其他命令

​ 到目前为止,本章介绍了Redis介绍了Redis提供的5种结构以及Redis的发布与订阅模式。本节将要介绍的命令则可以用于处理多种类型的数据:首先要介绍的是可以同时处理字符串、集合、列表和散列的sort命令;之后要介绍是用于实现基本事务的MULTI命令和EXEC命令,这两个命令可以让用户将多个命令当作一个命令来执行;最后要介绍的是几个不同的自动过期命令,它们可以自动删除无用数据。

​ 阅读本章有助于读者更好地理解如何同时组合和操作多种数据类型。

3.7.1 排序

Redis的排序操作和其他编程语言的排序操作一样,都可以根据某种比较规则对一系列元素进行有序的排列。负责执行排序操作的SORT命令可以根据字符串、列表、集合、有序集合、散列这5种键里面存储着的数据,对列表、集合以及有序集合进行排序。可以将sort看作是SQL语言里的order by子句。

命令 用例和描述
SORT SORT source-key [by pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC|DESC] [ALPHA] [STORE dest-key] — 根据给定的选项,对输入列表、集合或者有序集合进行排序,然后返回或者存储排序的结果

​ 使用SORT命令提供的选项可以实现以下功能:根据降序而不是默认的升序来排序元素;将元素看作是数字来进行排序,或者将元素看作是二进制字符串来进行排序(比如排序字符串'110'和'12'的结果就跟排序数字110和12的结果不一样);使用被排序元素之外的其他值作为权重来进行排序,甚至还可以从输入的列表、集合、有序集合以外的其他地方进行取值。

>>> conn.rpush('sort-input', 23, 15, 110, 7)
4
>>> conn.sort('sort-input')
['7', '15', '23', '110']
>>> conn.sort('sort-input', alpha=True)
['110', '15', '23', '7']
>>> conn.hset('d-7', 'field', 5)
1L
>>> conn.hset('d-15', 'field', 1)
1L
>>> conn.hset('d-23', 'field', 9)
1L
>>> conn.hset('d-110', 'field', 3)
1L
>>> conn.sort('sort-input', by= 'd-*->field')
['15', '110', '7', '23']
>>> conn.sort('sort-input', by='d-*->field', get='d-*->field')
['1', '3', '5', '9']

3.7.2 基本的Redis事务

​ 有时候为了同时处理多个数据结构,我们需要向Redis发送多个命令。尽管Redis有几个可以在两个键之间复制或者移动元素的命令,但却没有那种可以在两个不同类型之间移动元素的命令。为了对相同或者不同类型的多个键执行操作,Redis有5个命令可以让用户在不被打断(interruption)的情况下对多个键执行操作,它们分别是watchmultiexecunwatchdiscard

​ 这一节只介绍最基本的Redis事务用法,其中只会用到multi命令和exec命令。

什么是Redis的基本事务

​ Redis的基本事务(basic transaction)需要用到multi命令和exec命令,这种事务可以让一个客户端在不被其他客户端打断的情况下执行多个命令。和关系数据库那种可以在执行的过程中进行回滚的事务不同,在Redis里面,被multi命令和exec命令包围的所有命令会一个接一个地执行,直到所有命令都执行完毕为止。当一个事务执行完毕之后,Redis才会处理其他客户端的命令。

​ 要在Redis里面执行事务,我们首先需要执行multi命令,然后输入那些我们想要在事务里面执行的命令,最后再执行exec命令。当Redis从一个客户端那里接收到multi命令时,Redis会将这个客户端之后发送的所有命令都放入一个队列里面,直到这个客户端发送exec命令为止,然后Redis就会在不被打断的情况下,一个接一个地执行存储在队列里面的命令。从语义上来说,Redis事务在Python客户端上面是由流水线(pipeline)实现的:对连接对象调用pipeline()方法将创建一个事务,在一切正常的情况下,客户端会自动地使用multi和exec包裹起用户输入的多个命令。此外,为了减少Redis与客户端之间的通信往返次数,提升执行多个命令时的性能,Python的Redis客户端会存储起事务包含的多个命令,然后在事务执行时一次性地将所有命令都发送给Redis。

并行执行命令时,缺少事务可能会引发的问题

>>> def notrans():
        print conn.incr('notrans:')
        time.sleep(.1)
        conn.incr('notrans:', -1)
>>> if 1:
        for i in xrange(3):
            threading.Thread(target=notrans).start()
        time.sleep(.5)
1
2
3

因为没有保证事务,所以打印了其他的值

用Python使用事务

>>> def trans():
        pipeline = conn.pipeline()
        pipeline.incr('trans:')
        time.sleep(.1)
        pipeline.incr('trans:', -1)
        print pipeline.execute()[0]
>>> if 1:
        for i in xrange(3):
            threading.Thread(target=trans).start()
        time.sleep(.5)

​ 在使用Redis存储数据的时候,有些数据仅在一段很短的时间内有用,虽然我们可以在数据的有效期过了之后手动删除无用的数据,但更好的办法是使用Redis提供的键过期操作来自动删除无用数据。

3.7.3 键的过期时间

​ 在使用Redis存储数据的时候,有些数据可能在某个时间点之后就不再有用了,用户可以使用del命令显示地删除这些无用数据,也可以通过Redis的过期时间(expiration)特性来让一个键在给定的时限(timeout)之后自动被删除。当我们说一个键"带有生存时间"或者一个键"会在特定时间之后过期(expire)"时,我们指的是Redis会在这个键的过期时间到达时自动删除该键。

​ 虽然过期时间特性对于清理缓存数据非常有用。只有少数几个命令可以原子地为键设置过期时间,并且对于列表、集合、散列和有序集合这样的容器来说,键过期命令只能为整个键设置过期时间,而没办法为键里面的单个元素设置过期时间。

命令 示例和描述
persist persist key-name — 移除键的过期时间
ttl ttl key-name — 查看给定键距离过期还有多少秒
expire expire key-name seconds — 让给定键在指定的描述之后过期
expireat expireat key-name timestamp 将给定键的过期时间设置为给定的UNIX时间戳
pttl pttl key-name — 查看给定键距离过期时间还有多少毫秒,这个命令在Redis2.6或以上版本可用
pexpire pexpire key-name milliseconds — 让给定键在指定的毫秒数之后过期,这个命令在Redis2.6或以上版本可用
pexpireat pexpireat key-name timestamp-milliseconds — 将一个毫秒级精度的UNIX时间戳设置为给定键的过期时间,这个命令在Redis2.6或以上版本可用
>>> conn.set('key', 'value')
True
>>> conn.get('key')
'value'
>>> conn.expire('key', 2)
True
>>> time.sleep(2)
>>> conn.get('key')
>>> conn.set('key', 'value2')
True
>>> conn.expire('key', 100);conn.ttl('key')
True
100
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 《Redis 入门指南》(第二版) 第一章 Redis 是什么 Redis (REmote Dictionary ...
    EdenPP阅读 67,288评论 3 10
  • 可能是当下双方在彼此心中的地位不同,你把她的微信置顶,永远放在第一页。她可能并没有这样做,也许在后面几页,没有及时...
    匠人阿立阅读 158评论 0 0
  • 前段时间看到一本书叫做《盖洛普优势识别器》,这本书里有一句话:发挥优势的机会,比薪水,头衔,更重要。看到这句话时我...
    绿山墙的_安妮阅读 776评论 4 10
  • 一上色就毁了,本来也没画好( ・_・) ╮(╯_╰)╭ 攒钱攒钱买颜料!!!
    铅笔只演绎黑白阅读 147评论 2 1
  • 清晨的医院,对面病房打开门换空气,阳光穿过病房晒在了楼道里,感觉整个楼道都温暖了起来。 清晨的病房是忙碌的...
    明月365阅读 479评论 1 0