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” 安装

111037_j2gU_3314358.png

2.2 建立连接

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/5/15 20:39
# @Author  : zhouyuyao
# @File    : demon1.py

import 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")))


''' 结果
zhouyuyao
zhouyuyao
<class 'str'>
'''

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.py

import redis

pool1 = 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的name
key,name对应的hash中的key
value,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中所有的key
print(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的name
mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
'''

r.hmset("hash2", {"k2": "v2", "k3": "v3"}) 
hget(name,key)

'''
在name对应的hash中获取根据key获取value
hmget(name, keys, *args)
在name对应的hash中获取多个key的值

参数:
name,reids对应的name
keys,要获取key集合,如:['k1', 'k2', 'k3']
*args,要获取的key,如:k1,k2,k3
'''

r1.hmset("hash2", {"k2": "v2", "k3": "v3"})
print(r1.hget("hash2", "k2"))  # 单个取出"hash2"的key-k2对应的value
print(r1.hmget("hash2", "k2", "k3"))  # 批量取出"hash2"的key-k2 k3对应的value --方式1
print(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 k2
r.hset("hash1", "k11", "v1")               # 新增键值对 k11
r.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,集合的长度是4
print(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 redis
import time

pool = 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())                   # 查询所有的Key
print(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.py

import redis
import time

pool = 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自增1
pipe.execute()

print(r.get("name"))
print(r.get("age"))
print(r.get("sex"))


 

 

 

参考资料

1. https://www.zhihu.com/question/30219620/answer/219543231  来源:知乎 作者:网易云

2. https://www.w3cschool.cn/zookeeper/zookeeper_overview.html

3. https://www.jianshu.com/p/2639549bedc8  來源:简书 作者:君惜

 

转载于:https://my.oschina.net/u/3314358/blog/1814979

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值