文章目录
- redis介绍和安装
- redis配置文件启动
- pthon操作普通连接和连接池
- 最好写成单例
- 字符串操作
- hash类型操作
- list类型操作
- redis的其他操作
- django只要Redis
- 使用缓存案例
redis介绍和安装
redis 是什么?
-非关系型数据库:redis,mongodb,es,clickhouse,influxDB
no sql: not only sql
-关系型数据库:mysql,oracle,postgrasql,sqlserver,sqlite
-去IOE ,国产化
IBM:服务器
Oracle:数据库 达梦
EMC:存储
-redis 到底是什么? redis是一个key-value存储系统【软件】,用c语言写的,c/s架构的软件,纯内存存储,可以持久化【断电数据可以恢复】 value:有5钟数据类型 string:字符串 hash:字典 list:列表 set:集合 zset:有序集合
redis为什么这么快?
- qps :10w 6w左右
-1 纯内存操作,避免了io
-2 使用了io多路复用的网络模型--(epoll)
-3 数据操作是单线程单进程---【没有锁操作,没有线程间切换】
安装redis-redis开源项目,社区不支持win
-使用的epoll模式,不能再win上运行的
-微软团队---》基于人家源码,修改+编译---》安装包--》可以装在win上
-最新:最新7.x
win平台最新只有
-最新5.x版本 https://github.com/tporadowski/redis/releases/
-最新3.x版本 https://github.com/microsoftarchive/redis/releases
下载:Redis-x64-5.0.14.1.msi,一路下一步
安装完成之后 会自己做成系统服务 直接启动即可
客户端安装-cmd窗口
-redis-cli -h 127.0.0.1 -p 6379
-redis-cli
可以连接图形化页面的软件
-resp
-安装链接
-Navicate 16
链接即可
-...
redis配置文件启动
python
# 配置成服务---》启动停止服务即可
# 通过命令启动停止
# 安装目录下,重要的可执行文件
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 已经写了持久化方案
-从内存把数据保存到硬盘上的过程称之为持久化
pthon操作普通连接和连接池
python
# 普通连接
from redis import Redis
comm = Redis(host="localhost",
port=6379,
db=0,
decode_responses=True) # decode_responses 中文名需要转码则是加上这个参数
# print(res.decode(encoding='utf-8')) 手动转码
# print(str(res,encoding='utf-8'))
res = comm.get('name') # 如果是中文 需要转码
print(res)
comm.close()
# 链接池的创建
import redis
# 创建池 max_connections 最大链接数 decode_responses 中文转码
# 创建池--->保证pool是单例的---》全局只有一个pool对象实例
pool = redis.ConnectionPool(max_connections=11, host='localhost', port=6379, decode_responses=True)
comm = redis.Redis(connection_pool=pool)
res = comm.get('name') # 如果是中文 需要转码
print(res)
comm.close()
最好写成单例
不写成单例 每次链接就会生成一个链接池
python
# 单例写法
# 创建一个文件
import redis
POOL = redis.ConnectionPool(max_connections=11, host='localhost', port=6379, decode_responses=True)
# 使用的时候
from s1 import POOL
import redis
from threading import Thread
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('结束了')
字符串操作
python
# key value ex 设置过期时间 px 设置毫秒过期时间 nx 值不存在设置 xx 值存在设置
# 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区别? 一直io操作
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) # 设置字节 在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 两个一样的
# 自增 什么数字是增加的步长
#incrby
conn.incrby('age') # 文章阅读量 计数器 单线程 不会有并发安全问题
conn.incrby('age',3)
# 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()
'''
你需要掌握的
get
set
setex
getrange
setrange
strlen
append
'''
hash类型操作
python
import redis
POOL = redis.ConnectionPool(max_connections=11, host='localhost', port=6379, decode_responses=True)
conn = redis.Redis(connection_pool=POOL)
# 1 hset(name, key, value) 设置值 和批量设置 加上mapping指定一个字典
conn.hset('userinfo','name','lqz')
conn.hset('userinfo','age',18)
# userinfo = {'name':'huang','age':18} 这样的形式展现
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) 获取所有的key
print(conn.hkeys('userinfo2'))
# 8 hvals(name) 获取所有的 value
print(conn.hvals('userinfo2'))
# 9 hexists(name, key) 判断有没有这个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) 可以通过amount 来设置自增多少
conn.hincrbyfloat('userinfo2', 'age', amount=1.2)
# 造数据
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]))
# cursor值是从那个开始取 count 一次取多少条数据 取的数量不准确(上下相差一点点),下次取值取决于上次的结果---》不单独使用
res=conn.hscan('map_demo',cursor=320,count=10)
print(res) # (数字,{数据})
# 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条,直到所有数据都取完
'''
掌握
hset
hget
hmget
hlen
hincr
hscan_iter
'''
list类型操作
python
import redis
POOL = redis.ConnectionPool(max_connections=11, host='localhost', port=6379, decode_responses=True)
conn = redis.Redis(connection_pool=POOL)
# 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))
# where='after' 指定前面还是后面加值 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(res)
# 12 lrange(name, start, end) 取索引0-1这两个值
res=conn.lrange('girls',0,1) # 前闭后闭区间
print(res)
# 13 ltrim(name, start, end) 只保存开始和结束的值
# res = # 前闭后闭 只保留索引开始和结束的值 以外的全部删除
conn.ltrim('6666',1,2)
# 14 rpoplpush(src, dst) #两个列表 ,从第一个列表的右侧弹出,放到第二个列表的左侧
# 15 blpop(keys, timeout) # 阻塞式弹出--》可以做消息队列---》分布式
res=conn.blpop('boys',timeout=5)
print(res)
# 16 r.brpop(keys, timeout),从右向左获取数据
# 右边的列表弹出一个值 放到左边的列表中 timeout 设置时间 时间到了返回None
# 17 brpoplpush(src, dst, timeout=0)
redis的其他操作
python
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='*')
# 查找key 可以是多个
# 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
django只要Redis
python
# 第一种方式 使用第三方redis
# 创建一个文件
import redis
POOL = redis.ConnectionPool(max_connections=11, host='localhost', port=6379, decode_responses=True)
conn = redis.Redis(connection_pool=POOL)
# 视图类
from scripts.redis_demo.dome import POOL
# CommonListModelMixin 继承了ListModelMixin
class RedisView(GenericAPIView,CommonListModelMixin):
def get(self, request, *args, **kwargs):
conn = redis.Redis(connection_pool=POOL)
conn.incrby('count',1)
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",
}
}
}
# 视图类
from django_redis import get_redis_connection
class RedisViews(GenericAPIView,CommonListModelMixin):
def get(self, request):
conn = get_redis_connection() # 从池中获取一个链接
conn.incrby('count')
count = conn.get('count')
return APIResponse(msg='您是第%s个访问的' % count)
# 方式三:django的缓存
# 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的缓存, 不需要考虑类型
使用缓存案例
python
from rest_framework.mixins import ListModelMixin
from rest_framework.views import APIView
from utils.common_logger import logger
from django.core.cache import cache
class HomeView(GenericViewSet, CommonListModelMixin):
queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
serializer_class = SerializerBanner
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)
# 路由
http://127.0.0.1:8000/api/v1/home/banner/