Python 操作 Redis

网友投稿 796 2022-11-15

Python 操作 Redis

Python 操作 Redis

一、NoSQL简介

传统关系型数据库面对数据规模、数据模型复杂时的不足,导致了NoSQL的快速发展,后者易扩展,性能高,支持灵活的数据模型。

NoSQl数据库的种类

存储类型

代表解决方案

特点

列存储

Hbase,Cassandra,Hypertable

按列存储,适用于数据压缩,对一个或几个字段进行查询的效率很高

文档存储

Mongodb。CouchDB,riak

保证海量数据存储的同时,具有良好的查询性能,用类json格式进行存储

key-value存储

Dynamo,Redis,Tokyo,Cabinet,MemcacheDB    

具有极高的并发读写性能,通过key迅速查找到value,但只能通过key查询

图数据库

Neo4j,HyperGraphDB

图形关系的最佳存储模式

对象数据库

db4o,Versant

类似面向对象语言的语法操作数据库,通过对象的方式存取数据

XML数据库

Berkerey DB XML,BaseX

高效存储XML数据,并支持XML的内部查询语法

MongoDB 做高性能数据库,Redis 做缓存,HBase 做大数据分析。MongoDB 还无法取代关系型数据库。

MongoDB 是高性能、无模式的文档型数据库,支持二级索引,非常适合文档化格式的存储及查询。MongoDB 的官方定位是通用数据库,确实和 MySQL 有些像,现在也很流行,但它还是有事务、join 等短板,在事务、复杂查询应用下无法取代关系型数据库,适合存储json类型数据,不经常变化,比如排行榜,每天刷新一次,remove 一次再从 db 更新过去

Redis是内存型 Key/Value 系统,读写性能非常好,支持操作原子性,支持 list,set 等多种数据格式,适合读多写少的业务场景,很适合用来做高速缓存。

HBase 存储容量大,一个表可以容纳上亿行、上百万列,可应对超大数据量要求扩展简单的需求。Hadoop的无缝集成,让 HBase 的数据可靠性和海量数据分析性能(MapReduce)值得期待。

二、Redis

2.1 部署环境

1)我们首先在已经装有 docker 的远程服务器环境上,pull 个 redis 镜像,然后直接启动即可

docker run --name test -p $(ifconfig eth1|grep inet |awk '{print $2}'):11111:6379 -d docker.io/redis

2)本地 Windows 机器的 PyCharm 环境上,则需要安装好 redis 模块,或者直接使用 “pip install redis” 安装

2.2 建立连接

#!/usr/bin/env python# -*- coding:utf-8 -*-# @Time : 2018/5/15 20:39# @Author : zhouyuyao# @File : demon1.pyimport redis # 导入redis模块,通过python操作redis 也可以直接在redis主机的服务端操作缓存数据库r = redis.Redis(host="xxx.xxx.xxx.xxx", port=11111, decode_responses=True)# host是redis主机,需要redis服务端和客户端都启动 redis默认端口是6379# 连接redis,加上decode_responses=True,写入的键值对中的value为str类型,不加这个参数写入的则为字节类型r.set("name", "zhouyuyao") # key 是"name",value是"zhouyuyao",将键值对存入redis缓存print(r["name"])print(r.get("name")) # 取出键name对应的值print(type(r.get("name")))''' 结果zhouyuyaozhouyuyao'''

2.3 连接池

redis-py 使用 connection pool 来管理对一个 redis server 的所有连接,避免每次建立、释放连接的开销。默认,每个 Redis 实例都会维护一个自己的连接池。 可以直接建立一个连接池,然后作为参数 Redis,这样就可以实现多个 Redis 实例共享一个连接池

#!/usr/bin/env python# -*- coding:utf-8 -*-# @Time : 2018/5/18 11:35# @Author : zhouyuyao# @File : demon2.pyimport redispool1 = redis.ConnectionPool(host='xxx.xxx.xxx.xxx', port=11111, decode_responses=True)pool2 = redis.ConnectionPool(host='xxx.xxx.xxx.xxx', port=11112, decode_responses=True)r1 = redis.Redis(connection_pool=pool1)r2 = redis.Redis(connection_pool=pool2)r1.set('gender', 'female') # key 是"gender" value是"female" 将键值对存入redis缓存print(r1.get('gender')) # gender 取出键female对应的值r2.set('gender', 'male') # key 是"gender" value是"male" 将键值对存入redis缓存print(r2.get('gender')) # gender 取出键 male 对应的值

2.4 基本命令

1)string

set方法

set(name, value, ex=None, px=None, nx=False, xx=False)'''在Redis中设置值,默认,不存在则创建,存在则修改参数:ex,过期时间(秒)px,过期时间(毫秒)nx,如果设置为True,则只有name不存在时,当前set操作才执行xx,如果设置为True,则只有name存在时,当前set操作才执行'''

ex,过期时间(秒) 这里过期时间是3秒,3秒后p,键food的值就变成None

r1.set('food', 'mutton', ex=3) # key是"food" value是"mutton" 将键值对存入redis缓存print(r1.get('food')) # mutton 取出键food对应的值

px,过期时间(豪秒) 这里过期时间是30豪秒,30毫秒后,键foo的值就变成None

r1.set('food', 'beef', px=30)print(r1.get('food'))

nx,如果设置为True,则只有name不存在时,当前set操作才执行 (新建)

print(r1.set('fruit', 'watermelon', nx=True)) # True--不存在# 如果键fruit不存在,那么输出是True;如果键fruit已经存在,输出是None

xx,如果设置为True,则只有name存在时,当前set操作才执行 (修改)

print((r1.set('fruit', 'apple', xx=True))) # True--已经存在# 如果键fruit已经存在,那么输出是True;如果键fruit不存在,输出是None

mset方法

mset(*args, **kwargs)'''批量设置值'''r1.mget({'k1': 'v1', 'k2': 'v2'})r1.mset(k1="v1", k2="v2") # 这里k1 和k2 不能带引号 一次设置多个键值对print(r1.mget("k1", "k2")) # 一次取出多个键对应的值print(r1.mget("k1"))

mget方法

mget(keys, *args)'''批量获取'''print(r1.mget('k1', 'k2'))print(r1.mget(['k1', 'k2']))print(r1.mget("fruit", "fruit1", "fruit2", "k1", "k2")) # 将目前redis缓存中的键对应的值批量取出来

getset方法

getset(name, value)'''设置新值并获取原来的值'''print(r.getset("fruit", "barbecue")) # 设置的新值是barbecue 设置前的值是apple

2)hash

a. 单个增加--修改(单个取出)--没有就新增,有的话就修改

hset(name, key, value)'''name对应的hash中设置一个键值对(不存在,则创建;否则,修改)参数:name,redis的namekey,name对应的hash中的keyvalue,name对应的hash中的value'''

# 注:hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)

r1.hset("hash1", "k1", "v1")r1.hset("hash1", "k2", "v2")print(r1.hkeys("hash1")) # 取hash中所有的keyprint(r1.hget("hash1", "k1")) # 单个取hash的key对应的值print(r1.hmget("hash1", "k1", "k2")) # 多个取hash的key对应的值r1.hsetnx("hash1", "k2", "v3") # 只能新建print(r1.hget("hash1", "k2"))'''结果['k1', 'k2']v1['v1', 'v2']v2'''

b. 批量增加(取出)

hmset(name, mapping)'''在name对应的hash中批量设置键值对参数:name,redis的namemapping,字典,如:{'k1':'v1', 'k2': 'v2'}'''r.hmset("hash2", {"k2": "v2", "k3": "v3"})

hget(name,key)'''在name对应的hash中获取根据key获取valuehmget(name, keys, *args)在name对应的hash中获取多个key的值参数:name,reids对应的namekeys,要获取key集合,如:['k1', 'k2', 'k3']*args,要获取的key,如:k1,k2,k3'''r1.hmset("hash2", {"k2": "v2", "k3": "v3"})print(r1.hget("hash2", "k2")) # 单个取出"hash2"的key-k2对应的valueprint(r1.hmget("hash2", "k2", "k3")) # 批量取出"hash2"的key-k2 k3对应的value --方式1print(r1.hmget("hash2", ["k2", "k3"])) # 批量取出"hash2"的key-k2 k3对应的value --方式2''' 结果v2['v2', 'v3']['v2', 'v3']'''

c. 取出所有的键值对

hgetall(name)'''获取name对应hash的所有键值'''print(r1.hgetall("hash1"))

d. 得到所有的keys

(类似字典的取所有keys)

hkeys(name)'''获取name对应的hash中所有的key的值'''print(r.hkeys("hash1"))

e. 得到所有的value

(类似字典的取所有value)

hvals(name)'''获取name对应的hash中所有的value的值'''print(r.hvals("hash1"))

f. 判断成员是否存在

(类似字典的in)

hexists(name, key)'''检查name对应的hash是否存在当前传入的key'''print(r.hexists("hash1", "k4")) # False 不存在print(r.hexists("hash1", "k1")) # True 存在

g. 删除键值对

hdel(name,*keys)'''将name对应的hash中指定key的键值对删除'''print(r.hgetall("hash1"))r.hset("hash1", "k2", "v222") # 修改已有的key k2r.hset("hash1", "k11", "v1") # 新增键值对 k11r.hdel("hash1", "k1") # 删除一个键值对print(r.hgetall("hash1"))''' 结果{'k1': 'v1', 'k2': 'v2'}{'k2': 'v222', 'k11': 'v1'}'''

3)list

r.lpush(name,value) # 左边添加r.rpush # 右边添加r.linsert # 插入lpop(name) # 左边删除r.lrange(name,start,end) # 通过分片取list中的值lset(name,index,value) # 修改list中的某个值lrem(name,value,num) # 删除指定的值,'''num 默认为 0,删除所有, num = 2 从左往右删除两个元素,num=-1,从右往左删除1个元素'''

4)set 与有序 set

a. set

Set操作,Set集合就是不允许重复的列表,本身是无序的

sadd(name,values)'''name对应的集合中添加元素'''r.sadd("set1", 33, 44, 55, 66) # 往集合中添加元素print(r.scard("set1")) # 获取元素个数,类似于len,集合的长度是4print(r.smembers("set1")) # 获取集合中所有的成员'''获取集合中所有的成员--迭代器的方式'''sscan_iter(name, match=None, count=None)'''同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大'''for i in r.sscan_iter("set1"): print(i)

b. 有序 set

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

zadd(name, *args, **kwargs)'''在name对应的有序集合中添加元素'''import redisimport timepool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)r = redis.Redis(connection_pool=pool)r.zadd("zset1", n1=11, n2=22)r.zadd("zset2", 'm1', 22, 'm2', 44) print(r.zcard("zset1")) # 获取有序集合元素个数,类似于len,集合长度print(r.zcard("zset2")) # 集合长度print(r.zrange("zset1", 0, -1)) # 获取有序集合中所有元素print(r.zrange("zset2", 0, -1, withscores=True)) # 获取有序集合中所有元素和分数

获取有序集合的所有元素

r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)'''按照索引范围获取name对应的有序集合的元素参数: name,redis的name start,有序集合索引起始位置(非分数) end,有序集合索引结束位置(非分数) desc,排序规则,默认按照分数从小到大排序 withscores,是否获取元素的分数,默认只获取元素的值 score_cast_func,对分数进行数据转换的函数'''

5)其他常用操作

a. 删除

delete(*names)'''根据删除redis中的任意数据类型(string、hash、list、set、有序set)'''r.delete("gender") # 删除key为gender的键值对

b. 检查名字是否存在

exists(name)'''检测redis的name是否存在,存在就是True,False 不存在'''print(r.exists("zset1"))

c. 模糊匹配

keys(pattern='')'''根据模型获取 redis 的 name更多: KEYS * 匹配数据库中所有 key 。 KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 KEYS hllo 匹配 hllo 和 heeeeello 等。 KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo'''print(r.keys("foo*"))

d. 重命名

rename(src, dst)'''对redis的name重命名'''r.lpush("list5", 11, 22)r.rename("list5", "list5-1")

e. 查看所有元素

scan(cursor=0, match=None, count=None)print(r.hscan("hash2"))print(r.sscan("set3"))print(r.zscan("zset2"))print(r.getrange("foo1", 0, -1))print(r.lrange("list2", 0, -1))print(r.smembers("set3"))print(r.zrange("zset3", 0, -1))print(r.hgetall("hash1"))

f. 查看所有元素--迭代器

scan_iter(match=None, count=None)''' =================================== '''for i in r.hscan_iter("hash1"): print(i)for i in r.sscan_iter("set3"): print(i)for i in r.zscan_iter("zset3"): print(i)

g. other 方法

print(r.get('name')) # 查询key为name的值r.delete("gender") # 删除key为gender的键值对print(r.keys()) # 查询所有的Keyprint(r.dbsize()) # 当前redis包含多少条数据r.save() # 执行"检查点"操作,将数据写回磁盘。保存时阻塞# r.flushdb() # 清空r中的所有数据

管道(pipeline)

redis默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作, 如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。

管道(pipeline)是redis在提供单个请求中缓冲多条服务器命令的基类的子类。它通过减少服务器-客户端之间反复的TCP数据库包,从而大大提高了执行批量命令的功能。

#!/usr/bin/env python# -*- coding:utf-8 -*-# @Time : 2018/5/18 17:57# @Author : zhouyuyao# @File : demon3.pyimport redisimport timepool = redis.ConnectionPool(host='120.76.214.30', port=11111, decode_responses=True)r = redis.Redis(connection_pool=pool)# pipe = r.pipeline(transaction=False) # 默认的情况下,管道里执行的命令可以保证执行的原子性,# 执行pipe = r.pipeline(transaction=False)可以禁用这一特性。# pipe = r.pipeline(transaction=True)pipe = r.pipeline() # 创建一个管道pipe.set('name', 'jack')pipe.set('age', '22')pipe.sadd('exes', 'female')pipe.incr('num') # 如果num不存在则vaule为1,如果存在,则value自增1pipe.execute()print(r.get("name"))print(r.get("age"))print(r.get("sex"))

参考资料

1. ​​https://zhihu.com/question/30219620/answer/219543231​​

2. ​​https://w3cschool-/zookeeper/zookeeper_overview.html​​

3. ​​https://jianshu.com/p/2639549bedc8​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:自己动手用Springboot实现仿百度网盘的实践
下一篇:Socket 编程(上)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~