Redis介绍
博客地址:https://www.cnblogs.com/liuqingzheng/articles/9833534.html
1 非关系型数据库,纯内存操作,key-value存储,性能很高
2 缓存,计数器,验证码,geo地理位置信息,发布订阅,独立用户统计
3 5大数据类型:字符串,列表,hash,集合,有序集合
4 6.x之前是单线程,单进程,为什么这么快? qps:每秒查询率:10w,真实6w
-纯内存操作
-使用了io多路复用的模型,epoll (select,poll,epoll)
-避免了线程间切换的浪费
5 redis安装
-官方提供了 源码 c语言,编译安装
-编译型语言,如果要执行,需要在不同平台编译成不同平台的可执行文件
-在linux装,编译(gcc),编译成可执行文件,就可以运行
-win:不支持win,5.x 3.x基础,一路下一步
-监听的端口是:6379
-win上自动做成服务,启动关闭服务
-使用命令启动服务端
redis-server 配置文件
-客户端连接:命令窗口
redis-cli
-python代码连接
-图形化客户端:很多
-redis-desktop-manager:一路下一步
-连远端可以,
# 补充
6 QT:平台
-使用c/C++语言在平台上开发----》图形化界面软件(GUI)
-pyqt:在qt平台上使用python代码写图形化界面
-Tkinter
7 django 2.0.7以后,如果还使用pymysql连mysql,源码不兼容,改源码
-mysqlclient:什么都不用配,直接用,不需要改源码
1 Python操作Redis之普通连接和连接池
下载redis模块,pip install redis
#### redis之普通连接
import redis
# 拿到一个连接,通过连接操作数据
# conn = redis.Redis()
conn = redis.Redis(host='localhost', port=6379)
conn.set('name','lqz')
conn.close()
#### redis连接池
import redis
from pool import POOL
# 从池中获取连接
conn=redis.Redis(connection_pool=POOL)
# 设置age的值为24
conn.set('age',14)
conn.close()
# 创建池,池的大小是10,最多放10个连接
# 池需要做成单例,整个项目全局只有一个
# Python 的模块就是天然的单例模式
# 在pool.py中设置链接池大小
import redis
POOL = redis.ConnectionPool(max_connections=10)
2 Python操作Redis之字符串操作
import redis
conn = redis.Redis(host='localhost',port=6379)
set(name, value, ex=None, px=None, nx=False, xx=False)(重要)
ex,过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有key不存在时,才会为key赋值value
xx,如果设置为True,则只有key存在时,才会为key赋值value
conn.set('hobby','篮球',ex=8) # 8秒后清空
conn.set('names','aaa',xx=True) # names存在则赋值为aaa
conn.set('names','aaa',nx=True) # names不存在则赋值aaa
print(conn.get('names'))
setnx(name,value)
conn.setnx('name','ddd') # key值存在则值不变
print(conn.get('name'))
conn.setnx('name1','ddd') # key值不存在,则给这个key赋值
print(conn.get('name1'))
setex(name, time, value)
conn.setex('age',18,5) # 设置age的值为5,18秒后清空
psetex(name, time_ms, value)
conn.psetex('xx',3000,'ss') # 字符串xx的值为'ss',以毫秒为过期时间
mset(*args, **kwargs) 批量设置(重要)
conn.mset({'name2':'egon','name3':'lyf'}) # 批量设置name2和name3
get(name) 获取字符串的值,值的类型是byte格式(重要)
print(conn.get('name2')) # 获取'name2'的值,byte格式
mget({‘k1’: ‘v1’, ‘k2’: ‘v2’}) 批量获取key值(重要)
res=conn.mget('name1','name2') # 可以放多个key值
res=conn.mget(['name1','name2']) # 也可以用列表放key值
getset(name, value) 获取原值并设置新值
res=conn.getset('name1','ppp') # 获取name1,并将name1设置成'pop'
print(res)
getrange(key, start, end) 获取字符串指定范围:[start,and]闭区间
res=conn.getrange('name1',0,5) # 获取字符串中索引0到5夫人字符串
print(res.decode('utf-8'))
setrange(name, offset, value)
修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
参数: offset,字符串的索引,字节(一个汉字三个字节)
value,要设置的值
conn.setrange('name1',10,'pppppp')
# 向索引10开始替换pppppp
setbit(name, offset, value)
参数:
-name,redis的name
-offset,位的索引(将值变换成二进制后再进行索引)
-value,值只能是 1 或 0
注:如果在Redis中有一个对应: n1 = "foo",
那么字符串foo的二进制表示为:01100110 01101111 01101111
所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
getbit(name, offset)
获取name对应的值的二进制表示中的某位的值 (0或1)
print(conn.getbit('name1',2)) # 获取索引2的值,值为0或1
bitcount(key, start=None, end=None)
获取name对应的值的二进制表示中 1 的个数
参数:
- key,Redis的name
- start,位起始位置
- end,位结束位置
strlen(name) 获取字符串字节长度(重要)
返回name对应值的字节长度(一个汉字3个字节)
print(conn.strlen('name1'))
incr(self, name, amount=1) 自增(重要)
- 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
- 参数:
- name,Redis的name
- amount,自增数(必须是整数)
- 注:同incrby
conn.incr('name1') # 自增1
decr(self, name, amount=1) 自减
- 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
- 参数:
- name,Redis的name
- amount,自减数(整数)
incrbyfloat(self, name, amount=1.0)自增
- 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
- 参数:
- name,Redis的name
- amount,自增数(浮点型)
conn.incrbyfloat('name1',1.2)
print(res)
3 Python操作Redis之hash操作
import redis
conn = redis.Redis()
hset(name, key, value)(重要)
name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
参数: name,redis的name
key,name对应的hash中的key
value,name对应的hash中的value
注:hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
conn.hset('hash1','name','lqz')
conn.hset('hash1', 'age', '19')
hget(name,key)(重要)
在name对应的hash中获取根据key获取value
print(conn.hget('hash1','age')) # 获取hash1中age对应的值
hmset(name, mapping) # 弃用了,但是还可以用(重要)
在name对应的hash中批量设置键值对
参数:
name,redis的name
mapping,字典,如:{‘k1’:‘v1’, ‘k2’: ‘v2’}
conn.hmset('user_1_info', {'name': 'lyf', 'age': 33,'hobby':'篮球'})
conn.hset('user_2_info', mapping={'name': 'lyf', 'age': 33,'hobby':'篮球'})
hmget(name, keys, *args)(重要)
在name对应的hash中获取多个key的值
参数:
name,reids对应的name
keys,要获取key集合,如:[‘k1’, ‘k2’, ‘k3’]
*args,要获取的key,如:k1,k2,k3
res=conn.hmget('user_2_info','age','hobby')
res = conn.hmget('user_2_info', ['age', 'hobby'])
print(res)
list_or_args使用
from redis.client import list_or_args
conn.hmget('user_2_info','age','hobby')
res = list_or_args('age', ['hobby', ])
res = conn.hmget('user_2_info', ['age', 'hobby'])
res = list_or_args(['age', 'hobby'],[])
print(res)
hgetall(name) 获取所有 ,慎用,生产环境中,尽量不要用
res=conn.hgetall('user_2_info')
print(res)
hlen(name)获取name对应的hash中键值对的个数(重要)
res=conn.hlen('user_2_info')
print(res)
hkeys(name)获取name对应的hash中所有的key的值
res=conn.hkeys('user_2_info')
print(res)
hvals(name)获取name对应的hash中所有的value的值
res=conn.hvals('user_2_info')
print(res)
hexists(name, key)检查name对应的hash是否存在当前传入的key(重要)
res=conn.hexists('user_2_info','name1')
print(res)
hdel(name,*keys)将name对应的hash中指定key的键值对删除
res=conn.hdel('user_2_info','name')
print(res)
hincrby(name, key, amount=1)(重要)
自增name对应的hash中的指定key的值,不存在则创建key=amount
参数:name,redis中的name
key, hash对应的key
amount,自增数(整数)
conn.hincrby('user_2_info','age')
hincrbyfloat(name, key, amount=1.0)
自增name对应的hash中的指定key的值,不存在则创建key=amount
参数:
- name,redis中的name
- key, hash对应的key
- amount,自增数(浮点数)
自增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,从hash中取出一部分值,会以count为基准,但不完全是count
res=conn.hscan('test_hash',count=11)
print(len(res[1]))
hscan_iter(name, match=None, count=None)(重要)
利用yield封装hscan创建生成器,实现分批去redis中获取数据
参数:
match,匹配指定key,默认None 表示所有的key
count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# hash 类型本身就无需
# for i in range(1000):
# conn.hset('test_hash','%s_key'%i,'鸡蛋_%s'%i)
# res=conn.hgetall('test_hash')
# 取出所有数据,跟hgetall比,更节省内存
for key in conn.hscan_iter('test_hash',count=100): # 生成器
print(key)
4 Python操作Redis之列表操作
# redis 之列表操作
import redis
conn = redis.Redis()
lpush(name,values) # 从列表的左侧插入值(重要)
conn.lpush('names','lqz','egon')
conn.lpush('names','lyf','dlrb')
conn.lpush('names','pyy')
# 顺序:['pyy','dlrb','lyf','egon','lqz']
conn.rpush('names','mrzh')
# rpush(name, values) 表示从右向左操作
lpushx(name,value) # 只能key存在,才能放进去
在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
更多:rpushx(name, value) 表示从右向左操作
conn.lpushx('names','999')
conn.lpushx('names1','999') #不存在的放不进去
llen(name) name对应的list元素的个数(重要)
print(conn.llen('names'))
linsert(name, where, refvalue, value))
在name对应的列表的某一个值前或后插入一个新值
参数:name,redis的name
where,BEFORE或AFTER(小写也可以)
refvalue,标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
value,要插入的数据
# where:before或者after,大小写都可以
conn.linsert('names','after','egon','fengjie')
conn.linsert('names','BEFORE','egon','rh')
r.lset(name, index, value) 位置从零开始
对name对应的list中的某一个索引位置重新赋值
参数:name,redis的name
index,list的索引位置
value,要设置的值
conn.lset('names',5,'l_egon')
r.lrem(name, num,value )(重要)
在name对应的list中删除指定的值
参数:name,redis的name
value,要删除的值
num, num=0,删除列表中所有的指定值;
num=2,从前到后,删除2个;
num=-2,从后向前,删除2个
conn.lrem('names',0,'lqz') #把内部所有lqz都移除
conn.lrem('names',1,'lqz') #从左往右移除一个符合
conn.lrem('names', -1, 'lqz') # 从右往左移除一个符合
lpop(name)(重要)
在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
更多: rpop(name) 表示从右向左操作
conn.lpop('names') # 从左侧弹出一个
conn.rpop('names') # 从右侧弹出一个
lindex(name, index) 拿某个位置的值,从0开始(重要)
在name对应的列表中根据索引获取列表元素
res=conn.lindex('names',1)
print(res)
lrange(name, start, end)(重要)
在name对应的列表分片获取数据
参数:name,redis的name
start,索引的起始位置
end,索引结束位置
print(re.lrange('aa',0,re.llen('aa')))
res=conn.lrange('names',1,conn.llen('names')) # 获取起始到结束位置的值,前闭后闭
print(res)
ltrim(name, start, end) 修剪
在name对应的列表中移除没有在start-end索引之间的值
参数:name,redis的name
start,索引的起始位置
end,索引结束位置(大于列表长度,则代表不移除任何)
res=conn.ltrim('names',2,4) # 只保留2--4之间的,其他全删除
rpoplpush(src, dst) # 两个列表
从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
参数:src,要取数据的列表的name
dst,要添加数据的列表的name
conn.lpush('names1','xx','yy')
conn.rpoplpush('names','names1')
blpop(keys, timeout) 阻塞式弹出,有值可以弹,没有值就夯住,直到有值,
将多个列表排列,按照从左到右去pop对应列表的元素
参数:keys,redis的name的集合
timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞
更多:
r.brpop(keys, timeout),从右向左获取数据
爬虫实现简单分布式:多个url放到列表里,往里不停放URL,程序循环取值,但是只能一台机器运行取值,可以把url放到redis中,多台机器从redis中取值,爬取数据,实现简单分布式
# 基于它可以做消息队列,如果是简单的消息队列,就可以使用redis的list类型
res=conn.blpop('names',4) # 超时时间
print(res)
brpoplpush(src, dst, timeout=0)
从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
参数:src,取出并要移除元素的列表对应的name
dst,要插入元素的列表对应的name
timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
# 一次性把list中值全取出来
res = conn.lrange('names', 0, conn.llen('names'))
自定义增量迭代取值(哪个地方用过生成器)
由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:
1、获取name对应的所有列表
2、循环列表
但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
import redis
conn=redis.Redis(host='127.0.0.1',port=6379)
# conn.lpush('test',*[1,2,3,4,45,5,6,7,7,8,43,5,6,768,89,9,65,4,23,54,6757,8,68])
def scan_list(name, count=2):
index = 0
while True:
data_list = conn.lrange(name, index, count + index - 1)
if not data_list:
return
index += count
for item in data_list:
yield item
for item in scan_list('test',2):
print(item)
5 Redis其他操作(通用操作)
import redis
conn=redis.Redis()
delete(*names) 根据删除redis中的任意数据类型
conn.delete('test_hash')
exists(name)检测redis的name是否存在
res=conn.exists('names')
print(res) # 1 表示存在 0表示不在
keys(pattern=’*’) 获取所有的key值,可以过滤
根据模型获取redis的name
更多:KEYS * 匹配数据库中所有 key 。
KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
KEYS h*llo 匹配 hllo 和 heeeeello 等。
KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
res=conn.keys(pattern='n*')
res=conn.keys(pattern='nam?')
print(res)
expire(name ,time)为某个redis的某个name设置超时时间
conn.expire('names1',9)
rename(src, dst)对redis的name重命名
conn.rename('test','test1')
move(name, db))将redis的某个值移动到指定的db下
conn.move('hash1',5)
randomkey()随机获取一个redis的name(不删除)
res=conn.randomkey()
print(res)
type(name) 查看key的类型 5 大数据类型
res=conn.type('user_1_info')
res=conn.type('name1')
print(res)
6 管道(pipline)
# redis是非关系型数据库,不支持事务
# 管道,我们可以通过管道来模拟事务----》要么都成功,要么都失败:一个事务
import redis
conn = redis.Redis()
# 创建一个管道
pipe = conn.pipeline(transaction=True)
# 开启事务
pipe.multi()
#向管道中放入命令
#向管道中放入命令
pipe.decrby('egon_money',50)
# raise Exception('ssss')
pipe.incrby('lqz_money',50)
pipe.execute() #让管道中的命令执行
7 Django中使用redis
7.1 通用方案
pool.py
import redis
POOL = redis.ConnectionPool(max_connections=1000)
在使用的位置
from .pool import POOL
import redis
def test(request):
conn = redis.Redis(connection_pool=POOL)
res=conn.get('name')
print(res)
return HttpResponse('ok')
7.2 django方案,第三方模块
pip install django-redis
# 在配置文件中写
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "123",
}
}
}
# 使用的位置
from django_redis import get_redis_connection
def test(request):
conn = get_redis_connection() #从连接池中拿一个连接
res=conn.get('name')
print(res)
return HttpResponse('ok')
# 一旦使用了它,后续的djagno缓存,都缓存到redis中
cache.set('name','xxx')
# django的缓存很高级,可以缓存python中所有的数据类型,包括对象---》把对象通过pickle序列化成二进制,存到redis的字符串中
作业
1 三大数据类型所有操作,实验一把,把笔记整理出来
2 redis其他操作,管道,django中集成,都写一下