python 操作缓存
memcached
python需要安装memcached驱动:python-memcached
python天生支持memcached集群,其原理是在内存中维护了一个主机列表,且集群中主机权重值和主机在列表中重复出现的次数成正比
主机 权重 1.1.1.1 1 1.1.1.2 2 1.1.1.3 3host_list = ["1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.3", "1.1.1.3", "1.1.1.3"] 连接到memcached
import memcache
mc = memcache.Client(["192.168.253.167:11211"], debug=True)
添加
mc.add("k1", "v1") 添加键值队
如果"k1"键已经存在,则不能添加,否则会抛出异常
替换
mc.replace("k1", "111") 修改key的值,key不存在则抛出异常
修改
mc.set("k0", 0) 设置键值对,key存在,则修改,不存在则创建
mc.set_multi({'key2': 'v2', 'key3': 'v3'}) 批量修改键值对时,需要以字典为参数传递进去
获取
ret = mc.get("k1") 取出"k1"所对应的值
ret = mc.get_multi(["k1", "k2", "k3"]) 批量取出键所对应的值
删除
mc.delete("k1") 删除键值队
mc.delete_multi(["key1", "key2"]) 批量删除
值追加内容
mc.append("k1", "after") 在key对应的值后面加上字符串"after"
mc.prepend("k1", "before") 在key对应的值前面加上字符串"before"
值自增自减
mc.incr("k0")
mc.incr("k0", 10)
mc.decr("k0")
mc.decr("k0", 10)
锁机制:gets,cas
mc.gets("k0")
如果一个对象在gets之后另外一个对象修改了k0的值,那么下次cas执行失败,抛出异常,避免异常数据
mc.cas("k0", 999)
本质上每次执行gets时,会从memcache中获取一个自增的数字,通过cas去修改gets的值时,会携带之前获取的自增值和memcache中的自增值进行比较,如果相等,则可以提交,如果不想等,那表示在gets和cas执行之间,又有其他人执行了gets(获取了缓冲的指定值), 如此一来有可能出现非正常数据,则不允许修改
redis
python连接redis驱动:pip3 install redis
import redis
pool = redis.ConnectionPool(host="192.168.253.167", port=6379)
r = redis.Redis(connection_pool=pool)
string操作:在内存中按照键值对存储
set(name, value, ex=None, px=None, nx=False, xx=False)
ex:过期时间(秒)
px:过期时间(毫秒)
nx:如果设置成True,只有name不存在时,当前set操作才执行
xx:如果设置成True,只有name存在时,当前set操作才执行
setnx(name, value) 设置值,只有name不存在时,才能执行设置操作(等效set(nx=True))
ret = r.setnx('foo', 'Bar') #当'foo'存在时,设置新值不成功,返回False
print(ret)
False
setex(name, value, time) time为过期时间,单位为秒
psetex(name, time_ms, value) time_ms为过期时间,单位为毫秒
mset(*args, **kwargs) 批量设置值
如:
r.mset(k1='v1', k2='v2')
r.mset({'k1': 'v1', 'k2': 'v2'})
get(name) 获取值
mget(keys, *args) 批量获取
如:
r.mget('k1', 'k2')
r.mget(['k1', 'k2'])
r.mset(k1="v1", k2="v2")
print(r.mget('k1', 'k2'))
[b'v1', b'v2']
getset(name, value) 设置新值并获取原来的值
getrange(key, start, end) 返回字符串的值,并截取固定字符位置
start:起始位置
end:结束位置
r.set("k1", "abcdef")
print(r.getrange('k1', 2, 3))
b'cd'
setrange(name, offset, value) 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
offset:字符串的索引,字节
value:设置的值
scan(cursor=0, match=None, count=None) SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程,SCAN 命令的游标参数被设置为0时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束
r.set("k1", "abc")
print(r.scan(0))
(0, [b'k1', b's2'])
setbit(name, offset, value) 对name对应的二进制表示位进行修改
getbit(name, offset) 获取name对应二进制的某值
bitcount(key, start=None, end=None) 获取对应值二进制中1的个数
bitop(operation, dest, *keys) 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
# operation,AND(并)、 OR(或)、 NOT(非)、 XOR(异或)
# dest, 新的Redis的name
# *keys, 要查找的Redis的name
如:
bitop("AND", 'new_name', 'n1', 'n2', 'n3')
# 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中
strlen(name) 返回name对应值的字节长度
r.set("k1", "abcdef")
print(r.strlen("k1"))
6
incr(self, name, amount=1) name存在则自增其对应值,否则创建name=amount, name的值必须为整数,否则报错
r.set("k1", 10)
r.set("k2", 100)
print(r.incr("k1"))
print(r.incr("k1", 10))
11
110
incrbyfloat(self, name, amount=1.0) 浮点数自增
decr(self, name, amount=1) 自减,name对应的值为整数
append(key, value) 在name对应值后面追加内容,为字符拼接操作
r.set("k1", "abc")
r.append("k1", "def")
print(r.get("k1"))
b'abcdef'
list列表操作
lpush(name, values) 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
rpush(name, values) 从name对应的list右边添加元素
lpushx(name, values) 在name对应的list中添加元素,只有name存在时,添加到列表的最左边
llen(name) name对应的list元素的个数
r.lpush("l1", 11, 22, 33)
r.lpush("l1",44)
print(r.lrange("l1", 0, -1))
print(r.llen("l1"))
[b'44', b'33', b'22', b'11']
4
linsert(name, where, refvalue, value) name对应的list某个值前或后插入一个新值
where:BEFORE或AFTER
refvalue,标杆值,即:在它前后插入数据
value,要插入的数据
r.lpush("l1", 11, 22, 33, 44)
r.linsert("l1", "before", 22, "aa")
print(r.lrange("l1", 0, -1))
[b'44', b'33', b'aa', b'22', b'11']
lset(name, index, value) name对应的list中的索引位置重新赋值
index为list的索引
lrem(name, value, num) name对应的list中删除指定值
num:
num=0, 删除列表中所有指定值
num=2,从前到后,删除2个
num=-2,从后向前,删除2个
r.lpush("l1", "a", "a", "a")
r.lrem("l1", "a", 2) #删除列表中,2个为"a"的值
print(r.lrange("l1", 0, -1))
[b'a']
lpop(name) name对应的list左侧获取第1个元素并从列表从pop掉,返回值为pop掉的值
lindex(name, index) name对应的list索引对应的元素
r.lpush("l1", "a", "b", "c")
print(r.lrange("l1", 0, -1))
print(r.lindex("l1", 2))
[b'c', b'b', b'a']
b'a'
lrange(name, start, end) name对应的list分片数据
ltrim(name, start, end) name对应的list中移除没有在指定位置之间的数据
r.lpush("l1", "a", "b", "c", "d")
print(r.lrange("l1", 0, -1))
r.ltrim("l1", 1, 2)
print(r.lrange("l1", 0, -1))
[b'd', b'c', b'b', b'a']
[b'c', b'b']
rpoplpush(src, dst) 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
src,要取数据的列表的name
dst,要添加数据的列表的name
r.lpush("l1", "a", "b", "c", "d")
r.lpush("l2", "e", "f", "g", "h")
print(r.lrange("l1", 0, -1))
print(r.lrange("l2", 0, -1))
r.rpoplpush("l1", "l2")
print(r.lrange("l1", 0, -1))
print(r.lrange("l2", 0, -1))
[b'd', b'c', b'b', b'a']
[b'h', b'g', b'f', b'e']
[b'd', b'c', b'b']
[b'a', b'h', b'g', b'f', b'e']
blpop(keys, timeout) 将多个列表排列,按照从左到右去pop对应列表的元素
keys:name的集合
timeout:超时事件,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0表示永远阻塞
r.lpush("l1", "a", "b", "c", "d")
r.lpush("l2", "e", "f", "g", "h")
print(r.lrange("l1", 0, -1))
print(r.lrange("l2", 0, -1))
r.blpop(["l1","l2"], 1)
print(r.lrange("l1", 0, -1))
print(r.lrange("l2", 0, -1))
[b'd', b'c', b'b', b'a']
[b'h', b'g', b'f', b'e']
[b'c', b'b', b'a'] #当"l1"中的元素pop完之后,才会pop"l2"的
[b'h', b'g', b'f', b'e']
brpoplpush(src, dst, timeout=0)
src,取出并要移除元素的列表对应的name
dst,要插入元素的列表对应的name
timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0表示永远阻塞
set操作,set集合就是不允许重复列表
sadd(name,values) name对应的集合中添加元素
scard(name) 获取name对应的集合中元素个数
r.sadd("s1", 1,2,3)
print(r.scard("s1"))
sismember(name, value) 检查value是否是name对应的集合的成员
smembers(name) 获取name对应的集合的所有成员
r.sadd("s1", 1,2,3)
print(r.sismember("s1", 55))
print(r.smembers("s1"))
False
{b'3', b'2', b'1'}
sdiff(keys, *args) 在第一个name对应的集合中且不在其他name对应的集合的元素集合
r.sadd("s1", 1,2,3)
r.sadd("s2", 3,4,5)
print(r.sdiff("s1", "s2"))
{b'1', b'2'}
sdiffstore(dest, keys, *args) 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中
r.sadd("s1", 1,2,3)
r.sadd("s2", 3,4,5)
r.sdiffstore("s3", "s2", "s1")
print(r.smembers("s3"))
{b'5', b'4'}
sinter(keys, *args) 获取多一个name对应集合的交集
sinterstore(dest, keys, *args) 获取多一个name对应集合的并集,再讲其加入到dest对应的集合中
r.sadd("s1", 1,2,3)
r.sadd("s2", 3,4,5)
print(r.sinter("s1", "s2"))
r.sinterstore("s3", "s1", "s2")
print(r.smembers("s3"))
{b'3'}
{b'3'}
smove(src, dst, value) 将某个成员从一个集合中移动到另外一个集合
r.sadd("s1", 1,2,3)
r.sadd("s2", 3,4,5)
r.smove("s1", "s2", 1)
print(r.smembers("s2"))
{b'1', b'5', b'3', b'4'}
spop(name) 从集合的右侧(尾部)移除一个成员,并将其返回
srandmember(name, numbers) 从name对应的集合中随机获取numbers个元素
srem(name, values) 在name对应的集合中删除某些值
r.sadd("s1", 1,2,3)
r.srem("s1", 1)
print(r.smembers("s1"))
{b'2', b'3'}
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) 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
有序集合
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对应的有序集合的元素
# start,有序集合索引起始位置(非分数)
# end,有序集合索引结束位置(非分数)
# desc,排序规则,默认按照分数从小到大排序
# withscores,是否获取元素的分数,默认只获取元素的值
# score_cast_func,对分数进行数据转换的函数
zrevrange(name, start, end, withscores=False, score_cast_func=float) #从大到小排序
zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float) 按照分数范围获取name对应的有序集合的元素
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),并按照从低到高的顺序,返回排序后的集合成员。如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大
# 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的成员
如:zrem('zz', ['s1', 's2'])
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,用来对分数进行操作
hash操作
hset(name, key, value) name对应hash中设置一个键值对(不存在,则创建,否则,修改)
hsetnx(name, key, value) 当name对应的hash中不存在当前key时则创建(相当于添加)
hmset(name, mapping) 在name对应的hash中批量设置键值对
如: r.hmset('xx', {'k1':'v1', 'k2': 'v2'})
hget(name, key) 在name中hash获取key对应的值
hmget(name, keys, *args) 在name对应的hash中获取多个key的值
keys,要获取key集合,如:['k1', 'k2', 'k3']
*args,要获取的key,如:k1,k2,k3
如:
r.mget('xx', ['k1', 'k2'])
r.hmget('xx', 'k1', 'k2')
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可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
# cursor,游标(基于游标分批取获取数据)
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
如:
# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
# ...
# 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
hscan_iter(name, match=None, count=None) 利用yield封装hscan创建生成器,实现分批去redis中获取数据
其他操作:
delete(*names) 删除任意数据类型
exists(name) 检测name是否存在
r.sadd("s1", 1,2,3)
print(r.exists("s1"))
r.delete("s1")
print(r.exists("s1"))
True
False
keys(pattern="*") 正则匹配name对应的值
# KEYS * 匹配数据库中所有key
# KEYS h?llo 匹配hello,hallo和hxllo等
# KEYS h*llo 匹配hllo和heeeeello等
# KEYS h[ae]llo 匹配hello和hallo,但不匹配hillo
expire(name, time) 为name设置超时时间
rename(src, dst) 对name重新命名
move(name, db) 把name值移动到指定的db下
randomkey() 随机获取一个name(不会删除)
randomkey() 随机获取一个name(不会删除)
r.sadd("s1", 1,2,3)
print(r.randomkey())
b's3'
type(name) 获取name对应值的类型
scan(cursor=0, match=None, count=None) 游标遍历key值
scan_iter(match=None, count=None)
redis管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline是原子性操作
如:
import redis
pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
r = redis.Redis(connection_pool=pool)
pipe = r.pipeline(transaction=True)
r.set('name', 'alex')
r.set('role', 'sb')
pipe.execute()
浙公网安备 33010602011771号