luffy__redis配置和启动,, python操作deris普通连接_连接池——py普通连接redis_连接池链接,,redis字符串操作,,redis之hash等操作,,django使用redis方式,,接口缓存
# 1 登录前端 Login.vue # 小组件 放在Header.vue 组件中 -用户名密码登录 -短信登录 -发送短信验证码 #2 后端发送短信接口,限制频率 -以什么做限制,一分钟只能调用一次? ?mobile=11111111 手机号 # 3 注册前端 -发送短信验证码 # 4 redis介绍 -关系型数据库:mysql,postgrasql,oracle -库---》表---》字段--》行 -结构性很强 -sql 语句 -非关系型数据库:reids,mongodb,elasticsearch,clickhouse。。。 nosql # 5 redis是什么? cs架构软件,存储数据的软件,用c写的,支持很多客户端(python,go,java,c,php),速度很快 key-value形式存储,5大数据类型:字符串,字典,列表,集合,有序集合 # 6 为什么速度这么快 - 纯内存操作 - io多路复用网络模型 - 单线程单进程,避免了锁操作和线程切换 # 7 redis适合干什么? -缓存(最多) -验证码放在redis中,有过期时间,只要过期,这个数据就没了 -计数器 -去重 -排行榜:有序集合 -地址位置信息 -消息队列 。。。。 # 8 安装 -win:一路下一步 -创建了服务:启动停止 服务端 # 9 redis客户端: resp navicate cmd
# 配置成服务---》启动停止服务即可 # 通过命令启动停止 # 安装目录下,重要的可执行文件 redis-cli :客户端 等同于mysql 的 mysql redis-server :服务端等同于mysql 的 mysqld #重要的配置文件---》mysql 的 my.ini -redis.windows-service.conf - databases 16 - port 6379 - bind 127.0.0.1 # 启动redis-->使用配置文件 redis-server 配置文件路径 redis-server redis.windows-service.conf # 关闭 -服务中点停止 -cmd客户端链接上:执行 shutdown # redis数据是存在内存中得 -重启redis服务,关机---》数据都会丢失 -咱们不会丢 redis.windows-service.conf 已经写了持久化方案 -从内存把数据保存到硬盘上的过程称之为持久化
# 使用 py操作redis
-pip install redis
from redis import Redis conn = Redis(host="localhost", port=6379, db=0,decode_responses=True) res = conn.get('name') print(res) # 返回\xe6\x9d\x8e\xe6\xb8\x85\xe7\x85\xa7 # print(res.decode(encoding='utf-8')) # print(str(res,encoding='utf-8')) # 默认字符串形式存储是以 utf-8 形式存储 conn.close()
连接池链接
# 1 什么是连接池---》如下图 # 2 django 操作mysql ,有没有连接池? 使用第三方解决 -django原生操作mysql ,是没有连接池的 -一个请求---》视图函数--》操作数据库--》创建一个mysql链接---》断开链接--》数据返回 -django也需要有 mysql 连接池 -django如何使用连接池?先去研究 # 3 redis自带连接池 ###@@@@@@###pool.py@@@@@@###@@@@@@###@@@@@@###@@@@@@###@@@@@@### import redis POOL = redis.ConnectionPool(max_connections=3, host="localhost", port=6379, decode_responses=True) ###@@@@@@###@@@@@@### 测试.py@@@@@@###@@@@@@###@@@@@@###@@@@@@### import redis from threading import Thread from pool import POOL # 创建池--->保证pool是单例的---》全局只有一个pool对象实例 # python如何实现单例:6种 # 最简单方式:以模块导入 实现单例 # pool = redis.ConnectionPool(max_connections=3, host="localhost", port=6379, decode_responses=True) # 每次从池中取一个链接 # conn = redis.Redis(connection_pool=pool) # res = conn.get('name') # print(res) # conn.close() # 把链接放回到连接池 # 起10个线程,获取链接 def task(): conn = redis.Redis(connection_pool=POOL) res = conn.get('name') print(res) conn.close() # 把链接放回到连接池 l = [] for i in range(3): t = Thread(target=task) t.start() l.append(t) for t in l: t.join() print('结束了')
# 对每种数据类型的操作及这个数据类型有什么作用 # [字符串 字典 列表] 集合 有序集合 ## 主要应用场景 1 计数器 2 做缓存--》轮播图数据---》json格式字符串---》放到redis中
from pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
# 字符串操作
# 1 set(name, value, ex=None, px=None, nx=False, xx=False) 设置值
# conn.set('hobby','篮球')
# 带过期时间
# conn.set('age','19',ex=3)
# conn.set('age','19',px=3000)
# 如果key存在才设置或不存在才设置
# nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
# xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
# conn.set('age',19,nx=True)
# conn.set('age',99,nx=True)
# conn.set('age',99,xx=True)
# conn.set('yy',99,xx=True)
# 2 setnx(name, value) 存在就不改了
# conn.setnx('age',999) # 等同于 conn.set('age',19,nx=True)
# 3 psetex(name, time_ms, value) # 设置过期时间
# conn.psetex('xxx',3000,'阿斯顿发')
# conn.setex('xxx',3,'阿斯顿发')
# 4 mset(*args, **kwargs) 批量设置
# conn.mset({'name':'yyy','age':888,'height':180})
# 5 get(name) # 获取
# res=conn.get('name')
# print(res)
# 6 mget(keys, *args)
# res=conn.mget('name','age','height')
#'name','age','height'----> name [age,height]--->[name,age,height]
# ['name','age','height']-->['name','age','height']--->[name,age,height]
# res=conn.mget(['name','age','height'])
# print(res)
# 7 getset(name, value)
# res=conn.getset('name','彭于晏')
# # 先get,再set和 getset区别?
# print(res)
# 8 getrange(key, start, end) 数字指的是字节长度
# 字符: a b 你 中 国
# 字节: 8个bit位一个字节,存 一个字母字符 用一个字节就够了 但存中文--》utf-8---》需要3个字节存一个字符
# res=conn.getrange('name',0,2) # 前闭后闭区间
# print(res)
# print(str(res,encoding='utf-8'))
# s='lqz厉害'
# print(len(s))
# print(len(bytes(s,encoding='utf-8')))
# print(len(bytes(s,encoding='gbk')))
# 9 setrange(name, offset, value) # 设置字节
# conn.setrange('name',3,'eeee')
#### 比特位操作####
# 10 setbit(name, offset, value)
# print(conn.getbit('name',3))
# 11 getbit(name, offset)
# 12 bitcount(key, start=None, end=None)
# 13 bitop(operation, dest, *keys)
#### 比特位操作####
# 14 strlen(name)
# print(conn.strlen('name')) # 字节长度
# 15 incr(self, name, amount=1)
# #incrby
# conn.incrby('age') # 文章阅读量 计数器 单线程 不会有并发安全问题
# 16 incrbyfloat(self, name, amount=1.0)
# 17 decr(self, name, amount=1)
# conn.decrby('age',2)
# 18 append(key, value)
# conn.append('age',8888)
conn.append('hobby','很好')
conn.close()
'''
1 set(name, value, ex=None, px=None, nx=False, xx=False)
2 setnx(name, value)
3 psetex(name, time_ms, value)
4 mset(*args, **kwargs)
5 get(name)
6 mget(keys, *args)
7 getset(name, value)
8 getrange(key, start, end)
9 setrange(name, offset, value)
10 setbit(name, offset, value)
11 getbit(name, offset)
12 bitcount(key, start=None, end=None)
13 bitop(operation, dest, *keys)
14 strlen(name)
15 incr(self, name, amount=1)
#incrby
16 incrbyfloat(self, name, amount=1.0)
17 decr(self, name, amount=1)
18 append(key, value)
'''
'''
你需要掌握的
get
set
setex
getrange
setrange
strlen
append
应用场景 1 缓存 2 计数器 ''' 1 hset(name, key, value) 2 hmset(name, mapping) 3 hget(name,key) 4 hmget(name, keys, *args) 5 hgetall(name) 6 hlen(name) 7 hkeys(name) 8 hvals(name) 9 hexists(name, key) 10 hdel(name,*keys) 11 hincrby(name, key, amount=1) 12 hincrbyfloat(name, key, amount=1.0) 13 hscan(name, cursor=0, match=None, count=None) 14 hscan_iter(name, match=None, count=None)
1 hset(name, key, value)
2 hmset(name, mapping)
3 hget(name,key)
4 hmget(name, keys, *args)
5 hgetall(name)
6 hlen(name)
7 hkeys(name)
8 hvals(name)
9 hexists(name, key)
10 hdel(name,*keys)
11 hincrby(name, key, amount=1)
12 hincrbyfloat(name, key, amount=1.0)
13 hscan(name, cursor=0, match=None, count=None)
14 hscan_iter(name, match=None, count=None)
'''
# hash ---> 字典 key -value形式
# name ={}
from redis import Redis
conn = Redis(decode_responses=True)
# 1 hset(name, key, value) 设置值
# conn.hset('userinfo','name','lqz')
# conn.hset('userinfo','age',18)
# 2 hmset(name, mapping) 批量设置
# conn.hmset('userinfo1',{'name':'彭于晏','age':19})
# conn.hset('userinfo2', mapping={'name': '刘亦菲', 'age': 19, 'hobby': '抽烟'})
# 3 hget(name,key)
# print(conn.hget('userinfo2','hobby'))
# 4 hmget(name, keys, *args)
# print(conn.hmget('userinfo2','hobby','name','age'))
# print(conn.hmget('userinfo2',['hobby','name','age']))
# 5 hgetall(name) 慎用
# res=conn.hgetall('userinfo2')
# print(res)
# 6 hlen(name)
# print(conn.hlen('userinfo2'))
# 7 hkeys(name)
# print(conn.hkeys('userinfo2'))
# 8 hvals(name)
# print(conn.hvals('userinfo2'))
# 9 hexists(name, key)
# print(conn.hexists('userinfo2','hobby1'))
# 10 hdel(name,*keys)
# res=conn.hdel('userinfo2','hobby')
# print(res)
# 11 hincrby(name, key, amount=1)
# conn.hincrby('userinfo2','age')
# 12 hincrbyfloat(name, key, amount=1.0)
# conn.hincrbyfloat('userinfo2', 'age', amount=1.2)
###### hgetall 慎用,一次性全取出来,会撑爆内存###
# 一点点取值 ---》hscan一般不单独用
# 13 hscan(name, cursor=0, match=None, count=None)
# 造数据
# for i in range(1000):
# conn.hset('map_demo',i,'鸡蛋_%s'%i)
# 一次性取出来
# res=conn.hgetall("map_demo")
# print(res)
# 一点点取---》取的数量不准确(上下相差一点点),下次取值取决于上次的结果---》不单独使用
# res=conn.hscan('map_demo',cursor=0,count=20)
# print(res) # (数字,{数据})
# print(len(res[1]))
# res=conn.hscan('map_demo',cursor=320,count=10)
# print(res) # (数字,{数据})
# conn.hscan('map_demo')
# 14 hscan_iter(name, match=None, count=None) # 取出所有数据等同于 hgetall,但是是一点点取(count的值),一点点用
# 内部是生成器
# res=conn.hscan_iter('map_demo',count=10) # 这个数字并不是取10条,而是 每次取10条,把所有都取尽
# print(res) # generator
# for item in res:
# print(item)
# 内部具体如何实现---》内部使用了hscan
# res=conn.hscan_iter('map_demo',count=10)
# 内部通过调用 hscan实现 每次取10条,用完再继续取10条,直到所有数据都取完
conn.close()
'''
hset
hget
hmget
hlen
hincr
hscan_iter
'''
# 作用 1 跨进程间通信 ---》消息队列 2 实现分布式 3 队列和栈 ''' 1 lpush(name, values) 2 rpush(name, values) 表示从右向左操作 3 lpushx(name, value) 4 rpushx(name, value) 表示从右向左操作 5 llen(name) 6 linsert(name, where, refvalue, value)) 7 r.lset(name, index, value) 8 r.lrem(name, value, num) 9 lpop(name) 10 rpop(name) 表示从右向左操作 11 lindex(name, index) 12 lrange(name, start, end) 13 ltrim(name, start, end) 14 rpoplpush(src, dst) 15 blpop(keys, timeout) 16 r.brpop(keys, timeout),从右向左获取数据 17 brpoplpush(src, dst, timeout=0) '''
1 lpush(name, values) 2 rpush(name, values) 表示从右向左操作 3 lpushx(name, value) 4 rpushx(name, value) 表示从右向左操作 5 llen(name) 6 linsert(name, where, refvalue, value)) 7 r.lset(name, index, value) 8 r.lrem(name, value, num) 9 lpop(name) 10 rpop(name) 表示从右向左操作 11 lindex(name, index) 12 lrange(name, start, end) 13 ltrim(name, start, end) 14 rpoplpush(src, dst) 15 blpop(keys, timeout) 16 r.brpop(keys, timeout),从右向左获取数据 17 brpoplpush(src, dst, timeout=0) ''' from redis import Redis conn = Redis(decode_responses=True) # 1 lpush(name, values) # resp:上面是左,下面是右 # conn.lpush('girls', '刘亦菲') # 从左侧插入值 # conn.lpush('girls', '迪丽热巴') # 从左侧插入值 # 2 rpush(name, values) 表示从右向左操作 # conn.rpush('girls','李清照') # 3 lpushx(name, value) # conn.lpushx('girls','lqz') #girls key 要存在,如果不存在,就放不进去 # conn.lpushx('boys','lqz') #girls 要存在,如果不存在,就放不进去 # conn.lpush('boys','lqz') # 4 rpushx(name, value) 表示从右向左操作 # conn.rpushx('girls','小红') # 5 llen(name) # print(conn.llen('girls')) # 6 linsert(name, where, refvalue, value)) # 在 刘亦菲 后面插入 上海刘亦菲 # conn.linsert('girls',where='after',refvalue='刘亦菲',value='上海刘亦菲') # conn.linsert('girls', where='before', refvalue='刘亦菲', value='山东刘亦菲') # 7 lset(name, index, value) # conn.lset('girls',1,'lqz') # 从0 开始计算 # conn.lset('girls',3,'lqz') # 从0 开始计算 # 8 r.lrem(name, value, num) # conn.lrem('girls',1 ,'lqz') # 从左往右删1个 # conn.lrem('girls',-1 ,'lqz') # 从右往左删1个 # conn.lrem('girls',0 ,'lqz') # 所有都删除 # 9 lpop(name) # print(conn.lpop('girls')) # 10 rpop(name) 表示从右向左操作 # print(str(conn.rpop('girls'),encoding='utf-8')) # 11 lindex(name, index) # res=conn.lindex('girls',1) # 按索引取值,从0开始 # print(str(res,encoding='utf-8')) # 12 lrange(name, start, end) # res=conn.lrange('girls',0,1) # 前闭后闭区间 # print(res) # 13 ltrim(name, start, end) # conn.ltrim('girls',1,3) # 前闭后闭 # 14 rpoplpush(src, dst) #两个列表 ,从第一个列表的右侧弹出,放到第二个列表的左侧 # 15 blpop(keys, timeout) # 阻塞式弹出--》可以做消息队列---》分布式 res=conn.blpop('boys',timeout=5) print(res) # 16 r.brpop(keys, timeout),从右向左获取数据 # 17 brpoplpush(src, dst, timeout=0) conn.close() ''' lpush llen lpop lindex lrange blpop '''
redis其他操作
# 通用操作 # delete(*names) # exists(name) # keys(pattern='*') # expire(name ,time) # rename(src, dst) # move(name, db)) #randomkey() # type(name)
from redis import Redis conn = Redis(decode_responses=True) # delete(*names) # conn.delete('name','age') # exists(name) # res=conn.exists('userinfo1','name','height') # print(res) # keys(pattern='*') # res=conn.keys('u*') # print(res) # expire(name ,time) # conn.expire('girls',3) # rename(src, dst) # conn.rename('hobby','bobby1') # move(name, db)) # conn.move('bobby1',2) # randomkey() # print(conn.randomkey()) # type(name) print(conn.type('height')) #string print(conn.type('map_demo')) #hash # list #string # hash # set # zset conn.close()
####### 写个pool.py import redis POOL = redis.ConnectionPool(max_connections=10, decode_responses=True) ###### 在哪里用,导入用即可 from utils.pool import POOL import redis class RedisView(ViewSet): def list(self, request): conn = redis.Redis(connection_pool=POOL) conn.incrby('count') count = conn.get('count') return APIResponse(msg='您是第%s个访问的' % count)
#1 安装 pip install django-redis # 2 配置文件配置 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", } } } # 3 在使用的位置 from django_redis import get_redis_connection class RedisView(ViewSet): def list(self, request): conn = get_redis_connection() # 从池中获取一个链接 conn.incrby('count') count = conn.get('count') return APIResponse(msg='您是第%s个访问的' % count)
# django内置的,可以直接操作缓存---》缓存的位置--》内存--》只要项目一重启,数据就没了 # 后期,我们要把缓存数据,放到 redis中 ,redis可以持久化,项目停止,但redis还运行,数据就不会丢 # 只要在配置文件中,如下配置,以后只要使用 cache.set 和 cache.get 通过都是去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", } } } # cache.set() 设置缓存 # cache.get() 获取缓存 ## 以后再django中,可以直接使用 cache.set 和cache.get 操作redis,用它错缓存 ---》非常简单 ## 重点: -优势:redis 分数据类型, 只能设置5种数据类型 -django的缓存来讲 ,不限制类型,可以放python的任意类型 -django的缓存来讲,cache.set('redis的key','不区分类型:放python的任意类型') # person -cache.get('userinfo') -django cache 底层是基于: 把你存储的类型---》使用pickle序列化--》bytes格式---》当redis的字符串形式存到redis中 -以后咱们做redis的操作,可以直接使用django的缓存, 不需要考虑类型
# 首页轮播图接口----》只要有一个用户访问一次首页---》就会查询一次数据库 # 用户量很大---》同时来访问首页---》不停的查询 Banner表 # django的cache---》第一次去数据库拿---》把取出来的数据,放到缓存中,以后 只要访问轮播图接口,都从缓存中取---》这个接口的响应速度就会非常快 # 接口的响应速度 # 轮播图接口改造成 有缓存的 class BannerView(GenericViewSet, CommonListModelMixin): # qs对象可以切片----》 limit 2 queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT] serializer_class = BannerSerializer def list(self, request, *args, **kwargs): # 缓存中如果没有,就去数据中查---》放入缓存 # 先查缓存,缓存中有直接返回 banner_list = cache.get('banner_list') # 列表类型 if not banner_list: # 缓存中没有:去数据库查 ,查完序列化 ListModelMixin.list logger.info('走了数据库') res = ListModelMixin.list(self, request, *args, **kwargs) banner_list = res.data cache.set('banner_list', banner_list) return APIResponse(results=banner_list)
#1 后期如果banner表中数据变了---》由于一直取的是缓存数据---》缓存不会变--->导致数据不一致 -解决这个问题 -专业的叫法:双写一致性 mysql redis要同步写 # 2 后期,所有查询所有或单条的接口,都可以加缓存 -封装一个 mixin的类,不需要重写list方法,只需要配置一下,就能使用缓存 -作业:写一个mixin的类,只要继承,并且配置了cache_key,这个接口自带缓存
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了