python对缓存(memcached,redis)的操作

1.Memcached

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信

memcached服务端安装部署

安装libevent
memcached依赖于libevent API,因此要事先安装之,项目主页:http://libevent.org/,读者可自行选择需要的版本下载。本文采用的是目前最新版本的源码包libevent-2.0.16-stable.tar.gz。安装过程:

# tar xf libevent-2.0.20-stable.tar.gz
# cd libevent-2.0.20
# ./configure --prefix=/usr/local/libevent
# make && make install

# echo "/usr/local/libevent/lib" > /etc/ld.so.conf.d/libevent.conf
# ldconfig 

安装配置memcached

1、安装memcached
# tar xf memcached-1.4.15.tar.gz 
# cd memcached-1.4.15
# ./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent
# make && make install

 

memcached的常用选项说明
-l <ip_addr>:指定进程监听的地址;
-d: 以服务模式运行;
-u <username>:以指定的用户身份运行memcached进程;
-m <num>:用于缓存数据的最大内存空间,单位为MB,默认为64MB;
-c <num>:最大支持的并发连接数,默认为1024;
-p <num>: 指定监听的TCP端口,默认为11211;
-U <num>:指定监听的UDP端口,默认为11211,0表示关闭UDP端口;
-t <threads>:用于处理入站请求的最大线程数,仅在memcached编译时开启了支持线程才有效;
-f <num>:设定Slab Allocator定义预先分配内存空间大小固定的块时使用的增长因子;
-M:当内存空间不够使用时返回错误信息,而不是按LRU算法利用空间;
-n: 指定最小的slab chunk大小;单位是字节;
-S: 启用sasl进行用户认证;

 

python操作memcached

安装python的memcached的模块
pip install python-memcached

  • 常用方法
    • Client() 连接memcache服务器
    • set设置key/value key存在则更新,不存在则创建
    • set_multi 设置多个键值对,如果key不存在,则创建,如果key存在,则修改
    • get 根据单个key获取value
    • get_multi 根据key列表获取value,最终返回一个key/value的字典
    • add 添加一对key/value,如果已经存在的 key,则添加失败
    • replace 修改某个key的value ,key不存在则失败
    • delete 根据key删除对应的key/value
    • delete_multi 根据key 列表删除对应的key/value
    • append 根据key在对应的value后面追加值
    • prepend 根据key在对应的value前面追加值
    • incr 自增,将Memcached中的某一个值增加 N ( N默认为1 )
    • decr 自减,将Memcached中的某一个值减少 N ( N默认为1 )
  • 举例
import memcache

host_port = '127.0.0.1:11211'    #memcache的ip和端口

mc = memcache.Client([host_port], debug=True)        #debug表示开启调试,

mc.set('name','fuzj')               #设置key
mc.set('name','jeck')               #设置key,key存在则更新
mc.set('name1','test')               #设置key,key存在则更新
mc.set('name2','test')               #设置key,key存在则更新
mc.set_multi({'k1':'v1','k2':'v2'})     #批量设置key

print(mc.get('name'))   #获取key
print(mc.get('123'))    #获取不存在的key,返回None
print(mc.get_multi('k1','k2'))

mc.add('age','22')
mc.add('age','123')  #key存在会抱错

mc.replace('age','26')    #修改key的value
mc.replace('abc','26')    #修改不存在的key,会抱错

mc.delete('name1')     #删除age键值对
mc.delete_multi('k1','k2')  #删除k1 k2 键值对

mc.append('name','你好')    #在name的value 的后面加 '你好'
mc.prepend('name','hi')   #在name的value 的前面加'hi'

mc.incr('age')          #age对应的value增加1
mc.decr('age',10)           #age对应的value 减少10
mc.decr('name')     #name的value不能被加减,会抱错

 

  • 支持集群

python-memcached模块原生支持集群操作,其原理是在内存维护一个主机列表,且集群中主机的权重值和主机在列表中重复出现的次数成正比

     主机    权重
    1.1.1.1   1
    1.1.1.2   2
    1.1.1.3   1
 
那么在内存中主机列表为:
    host_list = ["1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.3", ]
 

如果用户根据如果要在内存中创建一个键值对(如:k1 = v1),那么要执行一下步骤:

根据算法将 k1 转换成一个数字
将数字和主机列表长度求余数,得到一个值 N( 0 <= N < 列表长度 )
在主机列表中根据 第2步得到的值为索引获取主机,例如:host_list[N]
连接 将第3步中获取的主机,将 k1 = v1 放置在该服务器的内存中
代码实现如下:

mc = memcache.Client([('1.1.1.1:12000', 1), ('1.1.1.2:12000', 2), ('1.1.1.3:12000', 1)], debug=True)
 
mc.set('k1', 'v1')

 

  • 解决乐观锁 gets/cas

如果两个客户端同时操作一个key,就会导致数据冲突
如商城商品剩余个数,假设改值保存在memcache中,product_count = 900
A用户刷新页面从memcache中读取到product_count = 900
B用户刷新页面从memcache中读取到product_count = 900

如果A、B用户均购买商品

A用户修改商品剩余个数 product_count=899
B用户修改商品剩余个数 product_count=899

如此一来缓存内的数据便不在正确,两个用户购买商品后,商品剩余还是 899
如果使用python的set和get来操作以上过程,那么程序就会如上述所示情况!

如果采用CAS协议,则是如下的情景。
第一步,A取出product_count=900,并获取到CAS-ID1;
第二步,B取出product_count=900,并获取到CAS-ID2;
第三步,A购买时,修改product_count=899,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“一致”,就将修改后的带有CAS-ID1的product_count写入到缓存。
第四步,B购买时,修改product_count=899,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“不一致”,则拒绝写入,返回存储失败。

2.Redis

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

redis服务端安装部署

1. 下载Redis
目前,最新的Redist版本为3.0,使用wget下载,命令如下:

# wget http://download.redis.io/releases/redis-3.0.4.tar.gz

2. 解压Redis
下载完成后,使用tar命令解压下载文件:

# tar -xzvf redis-3.0.4.tar.gz

3. 编译安装Redis
切换至程序目录,并执行make命令编译:

# cd redis-3.0.4
# make
执行安装命令

# make install
make install安装完成后,会在/usr/local/bin目录下生成下面几个可执行文件,它们的作用分别是:

redis-server:Redis服务器端启动程序
redis-cli:Redis客户端操作工具。也可以用telnet根据其纯文本协议来操作
redis-benchmark:Redis性能测试工具
redis-check-aof:数据修复工具
redis-check-dump:检查导出工具

备注

有的机器会出现类似以下错误:

make[1]: Entering directory `/root/redis/src'
You need tcl 8.5 or newer in order to run the Redis test
……
这是因为没有安装tcl导致,yum安装即可:

yum install tcl

4. 配置Redis
复制配置文件到/etc/目录:

# cp redis.conf /etc/
为了让Redis后台运行,一般还需要修改redis.conf文件:

vi /etc/redis.conf
修改daemonize配置项为yes,使Redis进程在后台运行:

daemonize yes

5. 启动Redis
配置完成后,启动Redis:

# cd /usr/local/bin
# ./redis-server /etc/redis.conf
检查启动情况:

# ps -ef | grep redis
看到类似下面的一行,表示启动成功:

root     18443     1  0 13:05 ?        00:00:00 ./redis-server *:6379 

6. 添加开机启动项
让Redis开机运行可以将其添加到rc.local文件,也可将添加为系统服务service。本文使用rc.local的方式,添加service请参考:Redis 配置为 Service 系统服务 。

为了能让Redis在服务器重启后自动启动,需要将启动命令写入开机启动项:

echo "/usr/local/bin/redis-server /etc/redis.conf" >>/etc/rc.local

7. Redis配置参数
在前面的操作中,我们用到了使Redis进程在后台运行的参数,下面介绍其它一些常用的Redis启动参数:

daemonize:是否以后台daemon方式运行
pidfile:pid文件位置
port:监听的端口号
timeout:请求超时时间
loglevel:log信息级别
logfile:log文件位置
databases:开启数据库的数量
save * *:保存快照的频率,第一个*表示多长时间,第三个*表示执行多少次写操作。在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。
rdbcompression:是否使用压缩
dbfilename:数据快照文件名(只是文件名)
dir:数据快照的保存目录(仅目录)
appendonly:是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率。
appendfsync:appendonlylog如何同步到磁盘。三个选项,分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步

 

python操作redis

安装redis模块
pip install redis

连接方式
  • 单连接

    使用redis.Redis()方法,直接和redis建立连接,操作完成之后,连接释放

import redis

r = redis.Redis(host='127.0.0.1', port=6379)
r.set('foo', 'Bar')
print(r.get('foo'))

 

  • 连接池 redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)
r.set('foo1', 'Bar')
print(r.get('foo1'))

 

操作
  • 字符串存储操作

    • 存储形式:

    • set(name, value, ex=None, px=None, nx=False, xx=False)

      参数:
      name:key的名称
      value:key的值
      ex,过期时间(秒)
      px,过期时间(毫秒)
      nx,如果设置为True,则只有name不存在时,当前set操作才执行
      xx,如果设置为True,则只有name存在时,当前set操作才执行

    • setnx(name, value) 设置值,只有name不存在时,执行设置操作(添加)

    • setex(name, value, time) 设置值,同时支持设置过期时间,单位:秒s

    • psetex(name, time_ms, value) 设置值,同时支持设置过期时间,单位:毫秒ms

    • mset(*args, **kwargs) 批量设置值 ,支持 字典形式{'k1':'v1','k2':'v2'}和key=value形式k1=v1,k2=v2

    • get(name) 获取key的value

    • mget(keys, *args) 批量获取值 支持k1,k2,k3形式和[k1,k2]形式

    • getset(name, value) 获取原来的值,并设置新的值

    • getrange(key, start, end) 获取指定key的value的子序列 start和end为value的起始位置

    • setrange(name, offset, value) 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加),offset,字符串的索引,字节(一个汉字三个字节),value,要设置的值

    • setbit(name, offset, value) 对name对应值的二进制表示的位进行操作,offset,字符串的索引,字节(一个汉字三个字节),value,要设置的值

    • getbit(name, offset) 获取name对应的值的二进制表示中的某位的值 (0或1)

    • bitcount(key, start=None, end=None) 获取name对应的值的二进制表示中 1 的个数

    • bitop(operation, dest, *keys) 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值

      参数:
      operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
      dest, 新的Redis的name
      *keys,要查找的Redis的name

    • strlen(name) 返回name对应值的字节长度(一个汉字3个字节)

    • incr(name, amount=1) 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增 ,amount必须为整数

    • incrbyfloat(name, amount=1.0) 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增,amount为浮点数

    • decr(name, amount=1) 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减

    • decrbyfloat(name, amount=1.0) 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减,amount为浮点数

    • append(key, value) 在redis name对应的值后面追加内容

    • 用法示例:

import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)


r.set('k1','v1',ex=2)
r.setnx('k2','v2')  #设置k1 v1

r.mset({'k3':'v3','k4':'v4'})           #批量设置kv
print(r.get('k1'))       #获取k1的value
print(r.mget(['k2','k3']))         #批量获取k2,k3
print(r.getset('k2','v22'))        #获取k2的值,并将k2的值设置为v22
print(r.get('k2'))
print(r.getrange('k2',1,2))     #获取k2的value的子序列1-2,默认从0开始
r.setrange('k4',2,'12313')
print(r.get('k4'))
print(r.strlen('k4'))         #获取k4的value长度
r.set('k5','123')
r.incr('k5',10)      #设置k5的值自增10
r.append('k5','')   #在k5的值后面追加元
print(r.get('k5').decode())
View Code

 

  • hash存储操作

    • 存储形式

    • hset(name, key, value) name对应的hash中设置一个键值对(不存在,则创建;否则,修改
    • hmset(name, mapping) 在name对应的hash中批量设置键值对,mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
    • hget(name,key) 在name对应的hash中获取根据key获取value
    • hmget(name, keys, *args) 在name对应的hash中获取多个key的值 keys,要获取key集合,如:['k1', 'k2', 'k3'], *args,要获取的key,如:k1,k2,k3
    • hgetall(name) 获取name对应hash的所有键值
    • hlen(name) 获取name对应的hash中键值对的个数
    • hkeys(name) 获取name对应的hash中所有的key的值
    • hvals(name) 获取name对应的hash中所有的value的值
    • hexists(name, key) 检查name对应的hash是否存在当前传入的key
    • hdel(name,*keys) 将name对应的hash中指定key的键值对删除
    • hincrby(name, key, amount=1) 自增name对应的hash中的指定key的值,不存在则创建key=amount
    • hincrbyfloat(name, key, amount=1.0) 自增name对应的hash中的指定key的值,不存在则创建key=amount
    • hscan(name, cursor=0, match=None, count=None) 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆

      参数:
      name,redis的name
      cursor,游标(基于游标分批取获取数据)
      match,匹配指定key,默认None 表示所有的key
      count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数

    • hscan_iter(name, match=None, count=None) 利用yield封装hscan创建生成器,实现分批去redis中获取数据

    参数:
    match,匹配指定key,默认None 表示所有的key
    count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数

    • 用法示例:
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)
r.hset('k1','hk1','hv1')            #设置k1 对应的键值对 hk1 hv1
print(r.hget('k1','hk1'))           #获取k1下的对应键值对hk1的值
r.hmset('k22',{'hk2':'hv2','hhk2':'hhv2'})
print(r.hmget('k22',['hk2','hhk2']))
print(r.hgetall('k22'))     #返回一个字典
print(r.hlen('k22'))            #获去k22对应的键值对个数
print(r.hkeys('k22'))       #获取k22对应键值对的keys
print(r.hvals('k22'))       #获取k22对应键值对的values
print(r.hexists('k22','jkkk'))  #判断k22对应键值对中是否存在jkkk的key 返回True和False

 

  • list存储操作

    • 存储形式

    • lpush(name,values) 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
    • rpush(name, values) 表示从右向左操作添加元素
    • lpushx(name,value) 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
    • llen(name) name对应的list元素的个数
    • linsert(name, where, refvalue, value)) 在name对应的列表的某一个值前或后插入一个新值

      参数:
      name,redis的name
      where,BEFORE或AFTER
      refvalue,标杆值,即:在它前后插入数据
      value,要插入的数据

    • lset(name, index, value) 对name对应的list中的某一个索引位置重新赋值
      参数:
      name,redis的name
      index,list的索引位置
      value,要设置的值

    • lrem(name, value, num) 在name对应的list中删除指定的值
      参数:
      name,redis的name
      value,要删除的值
      num, num=0,删除列表中所有的指定值;
      num=2,从前到后,删除2个;
      num=-2,从后向前,删除2个

    • lpop(name) 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素

    • rpop(name) 表示从右向左操作

    • lindex(name, index) 在name对应的列表中根据索引获取列表元素

    • lrange(name, start, end) 在name对应的列表分片获取数据

    • ltrim(name, start, end) 在name对应的列表中移除没有在start-end索引之间的值

    • rpoplpush(src, dst) 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边,src,要取数据的列表的name,dst,要添加数据的列表的name

    • blpop(keys, timeout) 将多个列表排列,按照从左到右去pop对应列表的元素
      参数:
      keys,redis的name的集合
      timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞;r.brpop(keys, timeout),从右向左获取数据

    • brpoplpush(src, dst, timeout=0) 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
      参数:
      src,取出并要移除元素的列表对应的name
      dst,要插入元素的列表对应的name
      timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞

    • 自定义增量迭代

    # 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:
        # 1、获取name对应的所有列表
        # 2、循环列表
    # 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
    
    def list_iter(name):
        """
        自定义redis列表增量迭代
        :param name: redis中的name,即:迭代name对应的列表
        :return: yield 返回 列表元素
        """
        list_count = r.llen(name)
        for index in xrange(list_count):
            yield r.lindex(name, index)
    
    # 使用
    for item in list_iter('pp'):
        print item

     

    • 用法举例:
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)

# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
r.lpush("k11",2)
r.lpush("k11",3,4,5)#保存在列表中的顺序为5,4,3,2
r.rpush('k11',0)   #从右边添加
r.lpushx('k11','abc')    #从左边添加元素,切name必须存在
print(r.llen('k11'))   #打印k11对应的列表长度
print(r.lrange('k11',0,r.llen('k11')))   #获取k11对应的列表所有值
print(r.lindex('k11',0))   #根据索引去值
print(r.lpop('k11'))   #移除第一个元素,并返回
r.ltrim('k11',0,5)          #删除索引0-5之外的值
print(r.lrange('k11',0,-1))
View Code

 

  • set集合存储操作

    Set集合就是不允许重复的列表

    • sadd(name,values) name对应的集合中添加元素
    • scard(name) 获取name对应的集合中元素个数
    • sdiff(keys, *args) 在第一个name对应的集合中且不在其他name对应的集合的元素集合
    • sdiffstore(dest, keys, *args) 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中
    • sinter(keys, *args) 获取多一个name对应集合的并集
    • sinterstore(dest, keys, *args) 获取多一个name对应集合的并集,再讲其加入到dest对应的集合中
    • sismember(name, value) 检查value是否是name对应的集合的成员
    • smembers(name)获取name对应的集合的所有成员
    • smove(src, dst, value) 将某个成员从一个集合中移动到另外一个集合
    • spop(name) 从集合的右侧(尾部)移除一个成员,并将其返回
    • srandmember(name, numbers) 从name对应的集合中随机获取 numbers 个元素
    • srem(name, values) 在name对应的集合中删除某些值
    • sunion(keys, *args) 获取多一个name对应的集合的并集
    • sunionstore(dest,keys, *args) 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中
    • sscan(name, cursor=0, match=None, count=None)

      sscan_iter(name, match=None, count=None)
      同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大

    • 用法举例:

pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)

r.sadd("k12","aa")
r.sadd("k12","aa","bb",'123')     #往k12对应的集合添加元素
r.sadd("k13","aa","bb",'cc','00')     #往k12对应的集合添加元素
print(r.smembers('k12'))   #获取对应的集合的所有成员
print(r.scard('k12'))   #获取k12对应集合元素的个数
print(r.sdiff('k12','k13'))    #比较k12中不在k13 的元素
r.sdiffstore('k123','k12','k13')   #比较k12中不在k13 的元素 ,并将结果添加到k123中
print(r.smembers('k123'))
print(r.sinter('k12','k13'))   #取k12和k13的并集
print(r.sismember('k12','555'))     #判断555是否是k12的元素
r.smove('k12','k13','123')  #将k12下的123移动到k13
print(r.smembers('k12'))
print(r.smembers('k13'))
View Code

 

  • 有序集合存储操作

    在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。

    • zadd(name, *args, **kwargs) 在name对应的有序集合中添加元素 如: zadd('zz', 'n1', 1, 'n2', 2) 或 zadd('zz', n1=11, n2=22)
    • zcard(name) 获取name对应的有序集合元素的数量
    • zcount(name, min, max) 获取name对应的有序集合中分数 在 [min,max] 之间的个数
    • zincrby(name, value, amount) 自增name对应的有序集合的 name 对应的分数
    • zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
    # 按照索引范围获取name对应的有序集合的元素
    
            # 参数:
                # name,redis的name
                # start,有序集合索引起始位置(非分数)
                # end,有序集合索引结束位置(非分数)
                # desc,排序规则,默认按照分数从小到大排序
                # withscores,是否获取元素的分数,默认只获取元素的值
                # score_cast_func,对分数进行数据转换的函数
    
            # 更多:
                # 从大到小排序
                # zrevrange(name, start, end, withscores=False, score_cast_func=float)
    
                # 按照分数范围获取name对应的有序集合的元素
                # zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
                # 从大到小排序
                # zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)

     

    • zrank(name, value) 获取某个值在 name对应的有序集合中的排行(从 0 开始),更多:zrevrank(name, value),从大到小排序
    • zrangebylex(name, min, max, start=None, num=None)
    # 当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的 值 (lexicographical ordering)来进行排序,而这个命令则可以返回给定的有序集合键 key 中, 元素的值介于 min 和 max 之间的成员
    # 对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 返回排序后的集合成员。 如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大
    
    # 参数:
                # name,redis的name
                # min,左区间(值)。 + 表示正无限; - 表示负无限; ( 表示开区间; [ 则表示闭区间
                # min,右区间(值)
                # start,对结果进行分片处理,索引位置
                # num,对结果进行分片处理,索引后面的num个元素
    
            # 如:
                # ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga
                # r.zrangebylex('myzset', "-", "[ca") 结果为:['aa', 'ba', 'ca']
    
                # 更多:
                # 从大到小排序
                # zrevrangebylex(name, max, min, start=None, num=None)

     

    • zrem(name, values) 删除name对应的有序集合中值是values的成员
    • zremrangebyrank(name, min, max) 根据排行范围删除
    • zremrangebyscore(name, min, max) 根据分数范围删除
    • zremrangebylex(name, min, max) 根据值返回删除
    • zscore(name, value) 获取name对应有序集合中 value 对应的分数
    • zinterstore(dest, keys, aggregate=None) 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作 ,aggregate的值为: SUM MIN MAX
    • zunionstore(dest, keys, aggregate=None) 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作 aggregate的值为: SUM MIN MAX
    • zscan(name, cursor=0, match=None, count=None, score_cast_func=float) zscan_iter(name, match=None, count=None,score_cast_func=float) 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作
  • 使用举例:

pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)
r.zadd("k999", "a1", 6, "a2", 2,"a3",5)     #往k999有序集合中添加值
#
r.zadd('k888', b1=10, b2=5)

print(r.zcard('k999'))   #获取k999的元素个数
print(r.zcount('k999',4,6))  #获取k999中分数在4-6之间的元素个数
r.zincrby('k999','a1',10)  #自增k999中a1的分数
print(r.zrevrank('k999','a3'))   #获取a3的分数在k999所有元素的排行,默认从0开始
View Code
  • 其他操作
    •  delete(*names) 根据删除redis中的任意数据类型
    • exists(name) 检测redis的name是否存在
    • keys(pattern='*')  根据模型获取redis的name 

      更多:

           KEYS * 匹配数据库中所有 key

           KEYS h?llo 匹配 hello , hallo 和 hxllo 等。

           KEYS h*llo 匹配 hllo 和 heeeeello 等。

           KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo

    • expire(name ,time)   为某个redis的某个name设置超时时间

    • rename(src, dst)  对redis的name重命名为

    • move(name, db))   将redis的某个值移动到指定的db

    • randomkey()   随机获取一个redis的name(不删除)

    • type(name)  获取name对应值的类型

    • scan(cursor=0, match=None, count=None)

    • scan_iter(match=None, count=None)  同字符串操作,用于增量迭代获取key

  • 管道 Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤: 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。 服务端处理命令,并将结果返回给客户端。 Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
import redis
import time
pool = redis.ConnectionPool(host='203.130.45.173', port=6379,)
r = redis.Redis(connection_pool=pool)

def without_pipeline():
    for i in range(1000):
        r.ping()
    return
def with_pipeline():
    pipeline=r.pipeline()
    for i in range(1000):
        pipeline.ping()
    pipeline.execute()
    return
def bench(desc):
    start=time.clock()
    desc()
    stop=time.clock()
    diff=stop-start
    print("%s has token %s" % (desc.__name__,str(diff)))

bench(without_pipeline)     #不使用管道
bench(with_pipeline)            #使用管道

输出结果:发现差别很大,不使用管道耗时将近是使用管道的10倍 without_pipeline has token
0.349842 with_pipeline has token 0.03180000000000005
  •  实现消息订阅模型

     

    所谓消息订阅模型,类似于广播电台,电台中心发送广播内容,凡事在此频道内的收音机都可以收到消息

     

     

    发布者订阅者类

     

    ```python
    
    import redis
    
     
    
    class RedisHelper:
    
        def __init__(self):
    
            self.pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
    
            self.__connect = redis.Redis(connection_pool=self.pool)
    
     
    
        def public(self,msg,channel):
    
        '''发布者方法'''
    
            try:
    
                self.__connect.publish(channel,msg)
    
                return True
    
            except Exception as e:
    
                return False
    
     
    
        def subscript(self,channel):
    
        '''订阅者方法'''
    
            pub = self.__connect.pubsub()
    
            pub.subscribe(channel)
    
            pub.parse_response()
    
            return pub
    
     
    
    ```

     

     

    订阅者:

     

    import RedisHelper
    
     
    
    obj = RedisHelper.RedisHelper()
    
    data = obj.subscript('fm110')
    
    print(data.parse_response())

     

     

    发布者:

    import RedisHelper
    
     
    
    obj = RedisHelper.RedisHelper()
    
    obj.public('alex','fm110')

     

转载于:https://www.cnblogs.com/pycode/p/cache.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值