文章目录
1. redis 介绍
介绍
- 是一个 cs 架构的开源软件(用c语言写的,初级版本代码只有1w多行。公司使用 5.x,4.x多)
- 非关系型(没有外键关联关系)数据库
- 数据都放在内存中(读写速度超级快,每秒的 qps 10w)
- 以 key-value 形式存储
- 有5大数据类型(字符串,list,hash,集合,有序集合)
好处
- 速度快,因为数据存在内存中,类似于python的字典的优势就是查找和操作的速度快
- 支持丰富数据类型,支持 string,list,set,sorted set,hash
- 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存(最广泛:缓存数据库),消息,按key设置过期时间,过期后将会自动删除
应用场景
-
会话缓存(Session Cache)。原来 session 放在了 djagno-session 表中,速度慢,如果放到 redis 中速度超级快
-
缓存:全页缓存,接口缓存:
-
队列:程序的解耦,不是专业的,真正专业的消息队列: kafka、rabbitmq…
-
排行榜/计数器:有序集合, 字符串类型做计数器累加(由于6.x之前redis是单线程单进程架构,不存在并发安全问题)。网站访问量,转发量,评论数(文章转发,商品销量,单线程模型,不会出现并发问题)
-
社交网络:很多特效跟社交网络匹配,粉丝数,关注数
-
实时系统:垃圾邮件处理系统,布隆过滤器
速度快的原因
- 纯内存操作
- 使用 io 多路复用的网络模型(select,poll,epoll模型)的 epoll 模型
- 单线程,单进程架构,没有进程线程间切换的消耗
Redis 的 key 可以存储的最大值为 512M
2. redis 安装
redis 作者对 win 的支持不好(win 不支持 epoll 的网络模型,只支持 select )
于是微软把 redis 编译成可执行文件并且可以运行在win上,其主要有俩个分支,如下所示
- 一个分支只维护到3.x
官方下载地址:https://github.com/microsoftarchive/redis/releases - 另一个分支维护到5.x
官方下载地址:https://github.com/tporadowski/redis/releases/
- 要安装Redis,首先要获取安装包。Windows的Redis安装包需要到以下GitHub链接找到。打开网站后,找到 Release,点击前往下载页面。
- 在下载网页中,找到最后发行的版本点击下载。这里说明一下,第一个是msi微软格式的安装包,第二个是压缩包。点击按照以下顺序安装即可
选择“添加Redis目录到环境变量PATH中”,这样方便系统自动识别Redis执行文件在哪里
端口号可保持默认的6379,并选择防火墙例外,从而保证外部可以正常访问Redis服务。
设定最大值为100M。作为实验和学习,100M足够了。也可以不勾选
3. redis 配置命令
- 找到服务设置进行配置,可以设置为手动启动。
- 测试一下Redis是否正常提供服务。进入Redis的目录。输入
redis-cli
并回车。( redis-cli 是客户端程序)如图正常提示进入,并显示正确端口号,则表示服务已经启动。如果设置了环境变量可以直接敲不需要进入目录。
- 启动 redis 服务
redis-server.exe redis.windows-service.conf
- 客户端连接(cmd窗口)
redis-cli -h 127.0.0.1 -p 6379
redis-cli
- 关闭服务
先连接数据库,再关闭 redis 服务
redis-cli -h ip地址 -p 端口号 -n 数据库编号 -a 密码
shutdown
直接连接数据库并关闭 redis 服务
redis-cli -h ip地址 -p 端口号 -n 数据库编号 -a 密码 shutdown
- 清空 redis 数据库
连接数据库执行
flushall
- redis 相关配置
1)绑定的ip地址,多个ip用空格隔开
bind 127.0.0.1
2)端口,默认6379,一般不做修改
port 6379
3)是否以守护进程启动,默认为no,一般改为yes代表后台启动(windows系统不支持)
daemonize no
4)定义日志级别,默认值为notice,有如下4种取值:
debug(记录大量日志信息,适用于开发、测试阶段)
verbose(较多日志信息)
notice(适量日志信息,使用于生产环境)
warning(仅有部分重要、关键信息才会被记录)
loglevel notice
5)配置日志文件保持地址,默认打印在命令行终端的窗口上
如果填写 "./redis.log" 就会在启动redis服务的终端所在目录下,用redis.log记录redis日志
logfile ""
eg)终端首先切断到log文件夹所在目录(一般就可以采用redis的安装目录,也可以自定义),再启动reids服务
logfile "./log/redis.log"
6)数据库个数,默认是16个,没特殊情况,不建议修改
databases 16
7)数据持久化
save 900 1 # 超过900秒有1个键值对操作,会自动调用save完成数据持久化
save 300 10 # 超过300秒有10个键值对操作,会自动调用save完成数据持久化
save 60 10000 # 超过60秒有10000个键值对操作,会自动调用save完成数据持久化
8)数据库持久化到硬盘失败,redis会立即停止接收用户数据,让用户知道redis持久化异常,避免数据灾难发生(重启redis即可),默认为yes,不能做修改
stop-writes-on-bgsave-error yes
9)消耗cpu来压缩数据进行持久化,数据量小,但会消耗cpu性能,根据实际情况可以做调整
rdbcompression yes
10)增持cpu 10%性能销毁来完成持久化数据的校验,可以取消掉
rdbchecksum yes
11)持久化存储的文件名称
dbfilename dump.rdb
12)持久化存储文件的路径,默认是启动服务的终端所在目录
dir ./
13)reids数据库密码
requirepass 密码
4. 图形化客户端
这里使用的是 redis-desktop-manage 其源码是个开源软件,用 qt 写的,图形化界面,类似于 navicate。链接:https://github.com/uglide/RedisDesktopManager 正常安装即可
- 安装后进行链接
- 链接后的界面如下所示,redis 是 key-value 存储,但是它默认有16个库
5. python 使用 redis
需要下载依赖:
pip3 install redis
普通链接
import redis
# decode_responses=True得到的结果会自动解码(不是二进制数据)
# db 指定某一个库
r = redis.Redis(host='127.0.0.1', port=6379, db=1, password=None, decode_responses=True)
# 或者简写
r = Redis(host="localhost",port=6379,)
r.close()
连接池方式
- pool.py
import redis
POOL = redis.ConnectionPool(max_connections=5)
- 链接
import redis
from utlis import pool
# 添加连接池
r = redis.Redis(host='127.0.0.1', port=6379, connection_pool=pool.POOL)
r.set('name', 'xwx')
r.close()
6. redis 数据类型
- 5 大数据类型
字符串,链表,hash,集合,有序集合 - redis 支持5大数据类型,但只支持到一层,第二层必须是字符串,例如 hash 中的值类型必须为字符串,但可以使用 JSON 格式字符串
redis={
k1:'123', 字符串
k2:[1,2,3,4], 列表/数组
k3:{1,2,3,4} 集合
k4:{name:xwx, age:12} 字典/哈希表
k5:{('lqz', 18),('egon', 33)} 有序集合
}
7. 数据类型之字符串
String操作,redis中的String在在内存中按照一个name对应一个value来存储
主要方法 | 作用 |
---|---|
set(name, value, ex=None, px=None, nx=False, xx=False) | 不存在则创建,存在则修改 |
setnx(name, value) | 不存在执行操作,存在则不会修改 |
psetex(name, time_ms, value) | 设置值和过期时间 |
mset(*args, **kwargs) | 批量设置值 |
get(name) | 获取值 |
mget(keys, *args) | 批量获取 |
getset(name, value) | 设置新值并获取原来的值 |
getrange(key, start, end) | 获取子序列 |
setrange(name, offset, value) | 修改指定索引字符串内容 |
setbit(name, offset, value) | 对应值的二进制表示的位进行操作 |
getbit(name, offset) | 获取对应的值的二进制表示的值 |
bitcount(key, start=None, end=None) | 对应的值的二进制表示中 1 的个数 |
strlen(name) | 返回对应值的字节长度 |
incr(self, name, amount=1) | 自增 name对应的值 |
incrbyfloat(self, name, amount=1.0) | 自增 name对应的值 |
decr(self, name, amount=1) | 自减 name对应的值 |
append(key, value) | 对应的值后面追加内容 |
set(name, value, ex=None, px=None, nx=False, xx=False)
参数 | 作用 | 示例 |
---|---|---|
ex | 过期时间(秒)最适合存验证码 | r.set(‘name’, ‘xwx’, ex=2) |
px | 过期时间(毫秒) | r.set(‘name’,‘xwx’,px=2000) |
nx | 如果设置为 True,则只有name 不存在 时,当前操作才执行。值存在,执行没效果 | r.set(‘name’,‘xwx’,nx=True) |
xx | 如果设置为 True,则只有name 存在 时,当前操作才执行,值存在才能修改,值不存在,不会设置新值 | r.set(‘name’,‘xwx’,xx=True) |
setnx(name, value)
等同于 set 方法 nx 为 True 的状态,当 name 不存在时,当前操作才执行。值存在,执行没效果
psetex(name, time_ms, value)
设置过期时间,并以毫秒计算 示例: r.psetex('age', 3000, '12')
mset(*args, **kwargs)
批量设置,如下示例
r.mset({'name': 'xwx', 'age': 18})
一次 mset 和多次 set 的区别
- 执行多条 SET 命令需要客户端和服务器之间进行多次网络通信,并因此耗费大量的时间;
- 通过使用一条 MSET 命令去代替多条 SET 命令,可以将原本所需的多次网络通信降低为只需一次网络通信,从而有效地减少程序执行多个设置操作时所需的时间。
get(name)
获取值,如下所示
print(r.get('age')) # b'18' 获取的值是二进制类型
print(str(r.get('name'), encoding='utf8')) # xwx 可以进行转码
mget(keys, *args)
批量获取值,如下所示
print(r.mget(['name', 'age']))
print(r.mget('name', 'age'))
getset(name, value)
获取并设置值,返回原本的值或者None,并重新设置该值
print(r.getset('xwx', 222))
getrange(key, start, end)
根据起始和终止,获取字符串,以字节计数( redis 中存储是用 utf-8 存储的)
s = r.getrange('name', 1, 2)
print(str(s, encoding='utf8'))
setrange(name, offset, value)
在指定位置可以理解为索引后设置值,原本有值会覆盖
r.setrange('name', 4, 'xxxx')
setbit(name, offset, value)
修改比特位,只能为 0 或 1,例如原本为 00000000 可以修改为 00000001
r.setbit('name', 2, 0)
getbit(name, offset)
获取指定位置的比特位,0 或 1
print(r.getbit('name', 2))
bitcount(key, start=None, end=None)
获取name对应的值的二进制表示中 1 的个数
print(r.bitcount('name', 0, 5))
strlen(name)
返回name对应值的字节长度,一个汉字为3字节
print(r.strlen('name'))
incrby(self, name, amount=1)
计数器,自增 name 对应的值(该值必须为数字),若不存在则创建,创建的默认值为 amount ,可以指定,默认为 1 。该方法自增必须为整数
print(r.incrby('age', amount=2)) # 自增对应的值并将自增后的数值返回
incrbyfloat(self, name, amount=1.0)
计数器,和 incrby 类似,但是该方法自增的是小数
print(r.incrbyfloat('age', amount=1.5)) # 自增对应的值并将自增后的数值返回
decrby(self, name, amount=1)
自减 name对应的值,当 name 不存在时,则创建 name=amount,否则,则自减。
注意:该值减去的和被减去的值必须为整数
print(r.decrby('age', amount=3)) # 自减对应的值并将自增后的数值返回
append(key, value)
在name对应的值后面追加内容
print(r.append('name', 'x')) # 追加内容并返回值的长度
8. 数据类型之 hash
Hash操作,redis中Hash在内存中的存储格式如下图
主要方法:
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)
hset(name, key, value)
name对应的hash中设置一个键值对(不存在,则创建;否则,修改),有以下俩种方式
# 方式一
r.hset('userinfo', 'name', 'xwx')
r.hset('userinfo', 'age', '12')
# 方式二,指定 mapping
r.hset('userinfo1', mapping={'name': 'xwx', 'age': 12})
hmset(name, mapping)
批量设置,等同于 hset 方法指定 mapping ,但是现在不推荐
hget(name,key)
获取 name 对应的 hash 中指定的值
print(r.hget('userinfo2', 'age')) # b'13' 返回的是二进制
hmget(name, keys, *args)
批量获取指定的值
print(r.hmget('userinfo2', ['name', 'age'])) # 也是二进制
hgetall(name)
获取所有的值,该方法慎用,如果hash类型数据量特别大,很可能撑爆内存
print(r.hgetall('userinfo2')) # {b'name': b'xwx', b'age': b'13'}
hlen(name)
获取name对应的hash中键值对的个数
print(r.hlen('userinfo2'))
hkeys(name)
获取name对应的hash中所有的key的值
print(r.hkeys('userinfo2'))
hvals(name)
获取name对应的hash中所有的value的值
print(r.hvals('userinfo2'))
hexists(name, key)
检查 name 对应的 hash 是否存在当前传入的 key
print(r.hexists('userinfo2', 'abc')) # False
hdel(name,*keys)
将 name 对应的 hash 中指定 key 的键值对删除
print(r.hdel('userinfo2', 'age')) # 1 返回 1
hincrby(name, key, amount=1)
自增 name 对应的 hash 中的指定 key 的值,不存在则创建 key = amount
print(r.hincrby('userinfo2', 'age', amount=3)) # 3 返回自增后的值
hincrbyfloat(name, key, amount=1.0)
同上,不过自增的类型是浮点数
hscan(name, cursor=0, match=None, count=None)
增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
参数 | 说明 |
---|---|
name | redis 的 name |
cursor | 游标(基于游标分批取获取数据) |
match | 匹配指定 key,默认 None 表示所有的key |
count | 每次分片最少获取个数,默认 None 表示采用 Redis 的默认分片个数 |
print(r.hscan('userinfo2', cursor=0, count=55))
# (0, {b'name': b'xwx', b'age': b'3'})
hscan_iter(name, match=None, count=None)
利用 yield
封装 hscan 创建生成器,实现分批去 redis 中获取数据
参数 | 说明 |
---|---|
match | 匹配指定 key,默认 None 表示所有的 key |
count | 每次分片最少获取个数,默认 None 表示采用 Redis 的默认分片个数 |
for i in r.hscan_iter('userinfo2'):
print(i)
# (b'name_0', b'value_0')
# (b'name_1', b'value_1')
# (b'name_2', b'value_2')
# (b'name_3', b'value_3')
# (b'name_4', b'value_4')
# (b'name_5', b'value_5')
# (b'name_6', b'value_6')
# (b'name_7', b'value_7')
# (b'name_8', b'value_8')
# (b'name_9', b'value_9')
9. 数据类型之 list
List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:
主要方法:
1 lpush(name,values)
2 lpushx(name,value)
3 rpushx(name, value) 表示从右向左操作
4 llen(name)
5 linsert(name, where, refvalue, value))
6 lset(name, index, value)
7 lrem(name, value, num)
8 lpop(name)
9 lindex(name, index)
10 lrange(name, start, end)
11 ltrim(name, start, end)
12 rpoplpush(src, dst)
13 blpop(keys, timeout)
14 brpoplpush(src, dst, timeout=0)
15 自定义增量迭代
lpush(name,values)
在name对应的list中添加元素,每个新的元素都添加到列表的最左边。如果列表原本有值则追加值
r.lpush('user', 'user1', 'user2', 'user3') # 添加的顺序是从左到右
lpushx(name,value)
在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
r.lpushx('user', 'user2', 'user3') # user 若不存在该操作无效
rpush(name, values)
也是添加元素,但从右向左
r.rpush('user', 'user1', 'user2', 'user3') # 从右往左添加
rpushx(name, value)
等同于 lpushx,方向相反,从右向左,只有name已经存在时,值添加到列表的最右边
r.rpushx('user', 'user4')
llen(name)
获得name对应的列表中值的个数
print(r.llen('user')) # 4
linsert(name, where, refvalue, value))
在name对应的列表的某一个值前或后插入一个新值
参数 | 说明 |
---|---|
name | redis 的 name |
where | BEFORE(前面) 或 AFTER (之后),小写也可以 |
refvalue | 标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准) |
value | 要插入的数据 |
r.linsert('user', 'before', 'user2', 'user5')
# 等同于
# r.linsert('user',where='before',refvalue='user2',value='user5')
lset(name, index, value)
对name对应的list中的某一个索引位置重新赋值
参数 | 说明 |
---|---|
name | redis的name |
index | list的索引位置 |
value | 要设置的值 |
r.lset('user', 0, 'user6') # 修改索引为0的值(第一个)
lrem(name, num,value)
在name对应的list中删除指定的值,删除的是相同的值
参数 | 说明 | |
---|---|---|
name | redis的name | |
value | 要删除的值 | |
num | num=0 | 删除列表中所有的指定值; |
- | num=2 | 从前到后,删除2个; |
- | num=-2 | 从后向前,删除2个 |
r.rpush('user', 'user', 'user', 'user', 'user', 'user', 'user')
r.lrem('user', 0, 'user') # 删除所有的 user 值,若删完后列表没值该列表键会销毁
lpop(name)
在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是左边第一个元素
rpop(name)
在name对应的列表的右侧获取第一个元素并在列表中移除,返回值则是右边第一个元素
lindex(name, index)
在name对应的列表中根据索引获取列表元素
print(r.lindex('user', 2)) # 获取索引为 2 的元素
lrange(name, start, end)
在name对应的列表分片获取数据,起始结束位置均为闭区间
参数 | 说明 |
---|---|
name | redis的name |
start | 索引的起始位置 |
end | 索引结束位置 |
print(r.lrange('user', 2, 5)) # 获取指定区间数值,左右均为闭区间
print(r.lrange('user', 0, r.llen('user'))) # 配合 llen 获取列表所有值
ltrim(name, start, end)
在name对应的列表中移除没有在start-end索引之间的值,左右均为闭区间
rpoplpush(src, dst)
从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边,添加以下俩个表
- 取出前
r.rpoplpush('user', 'pwd') # 结果如下所示
- 取出后
blpop(keys, timeout)
将多个列表排列,按照从左到右去pop对应列表的元素。
消息队列本质就是基于它,redis可以作为消息队列
其是阻塞式弹出,如果列表中有值,就弹出,如果没有值就阻塞,直到有值再弹出
参数 | 说明 |
---|---|
keys | redis的name的集合 |
timeout | 超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞 |
- test.py
...
print(r.blpop(['user', ]))
...
- test1.py
...
r.lpush('user', 'user1', 'user2')
...
brpoplpush(src, dst, timeout=0)
参数 | 说明 |
---|---|
src | 取出并要移除元素的列表对应的name |
dst | 要插入元素的列表对应的name |
timeout | 当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞 |
准备以下列表
...
r.brpoplpush('user', 'pwd')
print(r.lrange('user', 0, r.llen('user')))
print(r.lrange('pwd', 0, r.llen('pwd')))
...
移除后
自定义增量迭代
由于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])
# conn.flushall() # 清空所有库的表
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
print(conn.lrange('test',0,100))
for item in scan_list('test',5):
print('---')
print(item)
10 数据类型之集合
11 数据类型之有序集合
12 其它操作
- delete(*names)
根据删除redis中的任意数据类型
r.delete('age')
- exists(name)
检测redis的name是否存在
res = r.exists('age')
print(res)
- keys(pattern=‘*’)
根据模型获取redis的name
'KEYS * ' 匹配数据库中所有 key 。
'KEYS h?llo ' 匹配 hello , hallo 和 hxllo 等。
'KEYS h*llo ' 匹配 hllo 和 heeeeello 等。
'KEYS h[ae]llo ' 匹配 hello 和 hallo ,但不匹配 hillo
res = r.keys('*')
res = r.keys('user?')
print(res)
- expire(name ,time)
为某个redis的某个name设置超时时间
r.expire('user',5)
- rename(src, dst)
对redis的name重命名为
r.rename('test', 'test1')
- move(name, db))
将redis的某个值移动到指定的db下
r.move('tes1',3)
- randomkey()
随机获取一个redis的name(不删除)
print(r.randomkey())
- type(name)
获取name对应值的类型
print(r.type('age'))
- scan(cursor=0, match=None, count=None)
scan_iter(match=None, count=None)
同字符串操作,用于增量迭代获取key
- dbsize
查看库里的字段数量
res=conn.dbsize()
print(res)
13 redis 管道
redis-py 默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用 pipline
实现一次请求指定多个命令,并且默认情况下一次 pipline 是原子性操作。
只有在单机时可以使用,集群没有pipline
redis不支持事务但是可以通过管道的操作将多个命令放到一个管道中,然后一次性执行,要么都成功,要么
都失败
redis 支持事务码?支持,可以通过管道实现'弱事务'
- 准备俩个用户,设置其初始金额都是 100
r.hset('user_a', 'money', '100')
r.hset('user_b', 'money', '100')
- 将用户 a 的金额转给用户 b,为了防止用户 a 转完后出现异常导致用户 b 未收到可以使用管道的操作,在下面模拟异常的情况,出现异常不会执行
import redis
from utlis import pool
r = redis.Redis(host='127.0.0.1', port=6379, connection_pool=pool.POOL)
# 创建管道
pipe = r.pipeline(transaction=True)
# 接收多条命令
pipe.multi()
# 减去用户a金额
pipe.hincrby('user_a', 'money', amount=-50)
# 主动抛出异常
raise Exception('异常了')
# 增加用户b金额
pipe.hincrby('user_b', 'money', amount=50)
# 一次性执行管道中的操作,出现异常不会执行
pipe.execute()
r.close()
14 django 中集成 redis
在 django 中集成 redis 有俩种方案
1、通用方案,写一个pool包
- utils文件夹下,建立redis_pool.py
import redis
POOL = redis.ConnectionPool(max_connections=5, decode_responses=True)
- 视图函数中使用:
from utlis.pool import POOL
from redis import Redis
class Test(APIView):
def get(self, request):
r = Redis(connection_pool=POOL)
print(r.get('name'))
return Response('ok')
2、安装 django-redis
模块
安装语句:pip3 install django-redis
- 在 setting 里添加配置
# 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
class Test(APIView):
def get(self, request):
# default 为配置中设置的名称,可以修改
r = get_redis_connection('default')
# 相同的方式使用,但是返回的是没有被转换的二进制数值
print(r.get('name'))
return Response('ok')
3、注意点
一旦配置文件配置 CACHES 后,django的缓存框架(默认是内存),存储的位置也是 redis
,所以以后根本不需要会redis的操作,只需要使用
from django.core.cache import cache
cache.set('name',对象)
cache.get()
其强大之处在于不需要关注设置的类型,直接设置就行
因为它把 python 的对象,通过 pickle 序列化后,以 string 形式存到 redis 中了
储存后示例