上一篇文章: Python--Redis实战:第三章:Redis命令:第六节:发布与订阅
下一篇文章: Python--Redis实战:第四章:数据安全与性能保障:第1节:持久化选项
到目前为止,本章介绍了Redis提供的5种结构以及Redis的发布与订阅模式。本节将要介绍的命令则能够用于处理多种类型的数据:首先要介绍的是能够同时处理字符串、集合、列表和散列的sort命令;以后要介绍的是用于实现基本事务特性的multi命令和exec命令,这两个命令可让用户将多个命令当作一个命令来执行;最后要介绍的是几个不一样的自动过时命令,他们能够自动删除无用的数据。redis
阅读本节有助于读者更好的理解如何同时组合和操做多种数据类型。sql
Redis的排序操做和其余编程语言的排序操做同样,均可以根据某种比较规则对一系列元素进行有序排列。负责执行排序操做的sort命令能够根据字符串、列表、集合、有序集合、散列这5种键Kim存储的数据,对列表、集合以及有序集合进行排序。若是读者以前曾经使用过关系数据库的话,那么能够将soft命令看作是sql语言里的order by。数据库
下表展现了sort命令的定义:编程
命令 | 用例 | 用例描述 |
---|---|---|
soft | soft source-key [by pattern] [limit offset count] [get pattern get pattern ...]] [asc/desc] [alph] [store dest-key] | 根据给定的选项,对输入列表、集合或者有序集合进行排序,而后返回或者存储排序的结果。 |
使用sort命令提供的选项能够实现如下功能:segmentfault
import redis # 导入redis包包 # 与本地redis进行连接,地址为:localhost,端口号为6379 r = redis.StrictRedis(host='localhost', port=6379) r.delete('sort-input') #首先将一些元素添加到列表里面 print(r.rpush('sort-input',23,15,110,7)) #根据数字大小对元素进行排序 print(r.sort('sort-input')) #根据字母顺序对元素进行排序 print(r.sort('sort-input',alpha=True)) #添加一些用于执行排序操做和获取操做的附加数据 print(r.hset('d-7','field',5)) print(r.hset('d-15','field',1)) print(r.hset('d-23','field',9)) print(r.hset('d-110','field',3)) #将散列的域(field)用做权重,对sort-input列表进行排序 print(r.sort('sort-input',by='d-*->field')) #获取外部数据,并将它们用做命令的返回值,而不是返回被排序的数据 print(r.sort('sort-input',by='d-*->field',get='d-*->field'))
运行结果:缓存
4 [b'7', b'15', b'23', b'110'] [b'110', b'15', b'23', b'7'] 0 0 0 0 [b'15', b'110', b'7', b'23'] [b'1', b'3', b'5', b'9']
最开头的几行代码设置了一些初始数据,而后对这些数据进行了数值排序和字符串排序,最后的代码演示了若是经过sort命令的特殊语法来将散列存储的数据做为权重进行排序,以及怎样获取并返回散列存储的数据。
sort命令不只能够对列表进行排序,还能够对集合进行排序,而后返回一个列表形式的排序结果。上述实例除了展现了若是使用alpha关键字参数对元素进行字符串排序以外,还展现了若是基于外部数据对元素进行排序,以及如何获取并返回外部数据。安全
后面讲介绍如何组合使用集合操做和sort命令:当集合结构计算交集、并集和差集的能力,与sort命令获取散列存储的外部数据的能力相结合时,sort命令将变得很是强大。编程语言
尽管sort是Redis中惟一一个能够同时处理3种不一样类型的数据的命令,但基本的Redis事务一样可让咱们在一连串不断执行的命令里面操做多种不一样类型的数据。性能
有时候为了同时处理多个结构,咱们须要向Redis发送多个命令。尽管Redis有几个能够在两个键之间复制或者移动元素,但却没有那种能够在两个不一样类型之间移动元素的命令(虽然可使用zunionstore命令将元素从一个集合复制到一个有序集合)。为了对相同或者不一样类型的多个键执行操做,Redis有5个命令可让用户在不被打断(interruption)的状况下对多个键执行操做,它们分别是watch、multi、exec、unwatch、discard。线程
这一节中介绍最基本的Redis事务用法,其中只会用到multi命令和exec命令。
Redis的基本事务须要用到multi命令和exec命令,这种事务可让一个客户端在不被其余客户端打断的状况下执行多个命令。和关系数据库那种能够在执行的过程当中进行回滚(rollback)的事务不一样,在Redis里面,被multi命令和exec命令包围的全部命令会一个接一个的执行,直到全部命令都执行完毕。当一个事务执行完毕以后,Redis才会处理其余客户端的命令。
要在Redis里面执行事务,咱们首先须要执行multi命令,而后输入那些咱们想要在事务里面执行的命令,最后再执行exec命令。当Redis从一个客户端那里接受到multi命令时,Redis会将这个客户端以后发送的全部命令都放入到一个队列里面,直到这个客户端发送exec命令为止,而后Redis就会在不被打断的状况下,一个接一个地执行存储在队列里面的命令。从语义上来讲,Redis事务在Python客户端上面是由流水线(pipelien)实现:对链接对象用pipeline()方法将建立一个事务,在一切正常的状况下,客户端会自动地使用multi和exec包裹起用户输入的多个命令。此处,为了减小Redis与客户端之间的通讯往返次数,提高执行多个命令时的性能,Python的Redis客户端会存储起事务包含的多个命令,而后在事务执行时一次性地将全部命令都发送给Redis。
跟介绍publish命令和subscribe命令时的状况同样,要展现事务执行结果,最简单的方法就是将事务放到线程里执行。
下面代码展现了在没有使用事务的状况下,执行并行(parallel)自增操做的结果:
import redis # 导入redis包包 import time,threading # 与本地redis进行连接,地址为:localhost,端口号为6379 r = redis.StrictRedis(host='localhost', port=6379) r.delete('notrans:') def notrans(): #对'notrans:'计数器执行自增操做并打印操做的执行结果 print(r.incr('notrans:')) #等待100毫秒 time.sleep(.1) #对'notrans:'计数器执行自减操做。 r.incr('notrans:',-1) if __name__ == '__main__': # 启动3个线程来执行没有被事务包裹的自增、休眠和自减操做 for i in range(3): threading.Thread(target=notrans).start() # 等待500毫秒,让操做有足够的时间完成 time.sleep(.5)
结果:
1 2 3
由于没有使用事务,因此三个线程均可以在执行自减操做以前,对notrans:计数器执行自增操做。虽然代码里面经过休眠100毫秒放大了潜在问题,但若是咱们确实须要在不受其它命令干扰的状况下,对计数器执行自增操做和自减操做,那么咱们就不得不解决这个潜在问题。
下面代码使用事务来执行相同的操做:
import redis # 导入redis包包 import time,threading # 与本地redis进行连接,地址为:localhost,端口号为6379 r = redis.StrictRedis(host='localhost', port=6379) r.delete('trans:') def notrans(): #建立一个事务型(transactional)流水线对象 pipeline=r.pipeline() #把针对'trans:'计数器的自增操做放入队列 pipeline.incr('trans:') #等待100毫秒 time.sleep(.1) #把针对'trans:'计数器的自减操做放入队列 pipeline.incr('trans:',-1) #执行被事务包裹的命令,并打印自增操做的执行结果 print(pipeline.execute()[0]) if __name__ == '__main__': # 启动3个线程来执行没有被事务包裹的自增、休眠和自减操做 for i in range(3): threading.Thread(target=notrans).start() # 等待500毫秒,让操做有足够的时间完成 time.sleep(.5)
结果:
1 1 1
能够看出,尽管自增操做和自减操做直接有一段延迟时间,但经过使用事务,各个线程均可以在不被其它线程打断的状况下,执行各自队列里面的命令。记住,Redis要在接收到Exec命令以后,才会执行那些位于multi和exec之间的入队命令。
在使用Redis存储数据的时候,有些数据仅在一段很短的时间内有用,虽然咱们能够在数据的有效期过了以后删除无用的数据,但更好的办法是使用Redis提供的键过时操做来自动删除无用数据。
在使用Redis存储数据的时候,有些数据可能在某个时间点以后就再也不有用了,用户可使用DEL命令显示删除这些无用数据,也能够经过Redis的过时时间(expiration)特征来让一个键再给定的时限(timeout)以后自动被删除。当咱们说一个键【带有生存时间】(time to live)或者一个键【会在特定时间以后过时】时,咱们指的是Redis会在这个键的过时时间到达时自动删除该键。
虽然过时时间特性对于清理缓存数据很是有用,不过一般只有少数几个命令能够原子地为键设置过时时间,而且对于列表、集合、散列和有序集合这样的容器来讲,键过时命令只能为整个键设置过时时间,而没办法为键里面的单个元素设置过时时间(为了解决这个问题,可使用存储时间戳的有序集合来实现针对的那个元素的过时操做)。
本节将对那些能够在给定时限或者给定时间以后,自动删除过时键的Redis命令进行介绍。经过阅读本节,读者能够学会如何使用过时操做来自动删除过时数据并下降Redis的内存占用。
下表列出了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或以上版本可用。 |
下面代码展现了几个对键执行过时时间操做的例子:
import redis # 导入redis包包 import time # 与本地redis进行连接,地址为:localhost,端口号为6379 r = redis.StrictRedis(host='localhost', port=6379) r.delete('trans:') #设置一个简单的字符串值做为过时时间的设置对象 print(r.set('key','value')) print(r.get('key')) print(r.expire('key',2)) time.sleep(1) #查看键距离过时还有多长时间 print(r.ttl('key')) time.sleep(1) #此时键已通过期,并被删除 print(r.get('key'))
运行结果:
True b'value' True 1 None
上一篇文章: Python--Redis实战:第三章:Redis命令:第六节:发布与订阅
下一篇文章: Python--Redis实战:第四章:数据安全与性能保障:第1节:持久化选项