1. redis介绍
redis是一个key-value存储系统, 速度快, 支持丰富数据类型, 支持事务, 支持持久化, 丰富的特性...
速度快: 因为数据存在内存中, 类似于HashMap, HashMap的优势就是查找和操作的时间复杂度都是O(1)
使用I/O多路复用, 是单进程单线程的架构, 避免了线程和进程之间切换的资源消耗.
支持丰富数据类型: 支持string, list, set, sorted set(有序集合), hash(字典)
支持事务:操作都是原子性, 所谓的原子性就是对数据的更改要么全部执行, 要么全部不执行.
支持持久化: 将数据存到硬盘中.
丰富的特性:可用于缓存, 消息, 按key设置过期时间, 过期后将会自动删除.
PS: Memcached:另一个key-value存储系统, 操作更简洁.
只支持字符串类型数据, 不支持持久化, 数据断电丢失.
redis适合的场景:
1 排行榜 (使用 sorted set数据类型)
2 网站访问量, 文章访问量
3 缓存数据库(str 缓存接口 hash)
4 发布订阅
5 去重(set类型)
6 分布式blpop (类别类型)
2. redis安装
* 1. 官方不支持Windows版本的Redis, 因此官网上不提供下载.
但微软开发和维护着支持win-64的Redis版本, 因此可以去下载.
地址: https://github.com/MicrosoftArchive/redis/releases
MSI文件是Windows Installer的数据包, 它实际上是一个数据库.
包含安装一种产品所需要的信息和在很多安装情形下安装(和卸载)程序所需的指令和数据.
* 2. 点击安装包安装(包含服务端和客户端)
勾选 Add the Redis installation filder to the PATH environment variable. 添加到环境变量
端口 6379, 不限制使用的内存大小.
服务端会自动添加到windows的服务中, 自动启动.
这个服务使用 redis-server 配置文件.conf 启动服务端.
* 3. 客户端连接服务端
redis-cli -h 地址 -p 端口 连接服务端
redis-cli 命令默认使用127.0.0.1 6379
* 4. 存值&取值&退出
存值: set key 值
取值: get key
退出: exit
3. redis桌面管理器
该工具为您提供了一个易于使用的GUI, 可以访问您的Redis数据库并执行一些基本操作:
将键视为树, CRUD键, 通过shell执行命令.
官网: https://redisdesktop.com/download
* 1. 安装步骤
* 2. 连接服务端
* 3. 基操, 嘿嘿
4. Python连接redis
* 1. 安装redis模块
pip install redis
* 2. 简单使用
from redis import Redis
conn = Redis()
conn.set('age', 18)
res = conn.get('age')
print(res)
decode_response=True 参数对取出的值进行解码.
from redis import Redis
conn = Redis(decode_responses=True)
conn.set('age', 18)
res = conn.get('age')
print(res)
ps: QPS: 指每秒查询率。
redis的QPS 10万, 实际测试在6万多.
5. 连接池
正确访问操作一次连接一次, 如果项目中建立很多连接, 会影响redis的性能.
连接池: (一次连接就会使用一个连接对象)
在项目运行时先创建固定个数的连接对象, 放进连接池(列表)中.
需要操作redis时, 从连接池中随机拿一个连接对象, 操作完之后再放进连接池.
连接池是一个单例模式.
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.
当你希望在整个系统中, 某个类只能出现一个实例时, 单例对象就能派上用场.
模块实现方式:
python 的模块就是天然的单例模式, 因为模块在第一次导入时, 会生成 .pyc 文件.
当第二次导入时, 就会直接加载 .pyc 文件, 而不会再次执行模块代码.
因此, 我们只需把相关的函数和数据定义在一个模块中, 就可以获得一个单例对象了.
* 1. connection_pool.py 中创建连接池
import redis
POOL = redis.ConnectionPool(max_connections=100)
* 2. test_redis.py 中使用连接池的对象
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
print(conn.get('name'))
6. 字符串类型使用
set 没有值则创建值, 值存在则修改值.
set(key, value, ex, px, nx, xx)
key: 键
value: 值
ex: 过期时间(秒)
px: 过期时间(毫秒)
nx: 设置为True, 当key不存在的时候, set操作才会生效. (新建增的操作)
xx: 设置为True, 当key存在的时候, set操作才会生效. (修改值的操作)
6.1 存值取值
set(key, value) 设置值
get(key) 取值
getset(key, value) 先取值在刷新值
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 1)
res = conn.getset('k1', 2)
print(res)
print(conn.get('k1'))
6.1 过期时间
set('key', 'value', ex) ==> setex('key', 'value', '过期时间, 秒')
set('key', 'value', px) ==> psetex('key', '过期时间, 毫秒', 'value')
from connection_pool import POOL
import time
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 'v1', 3)
res = conn.get('k1')
print(res)
time.sleep(4)
res = conn.get('k1')
print(res)
6.2 写入限制
set('key', 'value', nx) ==> setnx('key', 'value')
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 'v1')
res = conn.get('k1')
print(res)
conn.set('k1', 'v2', nx=True)
res = conn.get('k1')
print(res)
6.3 修改限制
set('key', 'value', xx) ==> setxx('key', 'value')
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
res = conn.get('k4')
print(res)
conn.set('k4', 'v4', xx=True)
res = conn.get('k4')
print(res)
6.4 批量操作
批量设置
mset({'k1': 'v1', 'k2': 'v2'})
批量取值
mget('k1', 'k2')
批量设置并取值
mget({'k1': 'v1', 'k2': 'v2'})
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.mset({'k1': 'v1', 'k2': 'v2'})
print(conn.mget('k1', 'k2'))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
print(conn.mget({'k1': 'v1', 'k2': 'v2'}))
6.5 区间取值
getrange(key, start, end) 区间取值, 前比后闭区间, 顾前顾后.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 123)
print(conn.getrange('k1', 0, 0))
print(conn.getrange('k1', 0, 1))
print(conn.getrange('k1', 0, 2))
6.6 替换
一个汉字占三位.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 123)
conn.setrange('k1', 1, 789)
print(conn.get('k1'))
6.7 bit操作
value的二进制表示值操作
修改位: setbit(key, 位, 1/0) (值只能是0/1)
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('str1', 'goo')
conn.setbit('str1', 4, 1)
conn.setbit('str1', 7, 1)
print(conn.get('str1'))
获取位: getbit(key, 位)
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('str1', 'goo')
print(conn.getbit('str1', 0))
print(conn.getbit('str1', 1))
统计位: bitcount(key, strat, end) 获取值对应值的二进制数1的个数
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
print(conn.bitcount('str1'))
print(conn.bitcount('str1', 0, 0))
print(conn.bitcount('str1', 0, 1))
bitop (operation, dest, *keys)
参数:
operation, AND(并), OR(或), NOT(非, key只能有1个), XOR(异或)
dest 新的redis的key
*key 需要查询的key, 可以是多个key.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.mset({'k1': 'o', 'k2': 'o'})
print(conn.mget('k1', 'k2'))
conn.bitop('AND', 'k3', 'k1', 'k2')
print(conn.get('k3'))
strlen(key) 返回value的字节格式, 一个汉字三个字节.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', '你好')
print(conn.strlen('k1'))
6.8 自增/自减
incr(self, key, amount=1) ==> incrby() 调用一次计数加1, amount可以设置,
decr(self, key) 调用一次计数减1
incrbyfloat(self, name, amount=1.0) 当可以没有值时, key = amount, 否则自增.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 0)
conn.incr('k1')
conn.incr('k1')
conn.incr('k1')
print(conn.get('k1'))
conn.decr('k1')
print(conn.get('k1'))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 0)
conn.incr('k1', 2)
print(conn.get('k1'))
conn.incr('k1', -2)
print(conn.get('k1'))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.incrbyfloat('k1')
print(conn.get('k1'))
6.9 追加
append(key, value) 在key对应的值后面追加内容
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.set('k1', 'hello')
conn.append('k1', ' word!')
print(conn.get('k1'))
7. hash类型操作
hset(name, key, value) 存值
hsetnx(name, key, value) name对应的hash的key不存在时则创建.
hget(name, key) 取值
name: redis的name
key hash的key
value hash的value
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hset('n1', 'k1', 'v1')
print(conn.hget('n1', 'k1'))
7.1 批量设置
hmset(name,{key1: value1, key2: value2}) 存多个值
hgset(name, k1, k2) / hgset(name, [k1, k2]) 去多个值
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2'})
print(conn.hmget('n1', 'k1', 'k2'))
print(conn.hmget('n1', ['k1', 'k2']))
7.2 查询所有
hgetall(name) 获取name对应的hash所有键值对.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2'})
print(conn.hgetall('n1'))
7.3 获取个数
hlen(name) 获取name对应hash的所有键值对个数.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2'})
print(conn.hlen('n1'))
7.4 获取所有键/值
hkeys(name) 获取name对应的hash所有的键
hvals(name) 获取name对应的hash所有的值
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2'})
print(conn.hkeys('n1'))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2'})
print(conn.hvals('n1'))
7.5 判断键是否存在
hexists(name, key) 判断hash中是否存在某个键
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2'})
print(conn.hexists('n1', 'k1'))
7.6 删除键
hdel(name, *keys) 将指定的key从hash中删除.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2'})
print(conn.hdel('n1', 'k1'))
print(conn.hgetall('n1'))
7.7 自增
hincrby(namen, key, amount=1) 自增
hincrbyfloat(namen, key, amount=1.0)
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hset('n1', 'k1', 0)
print(conn.hincrby('n1', 'k1'))
7.8 迭代取值
hscan(name, cursor=0, match=None, count=None)
增量式迭代获取, 可以实现切片获取数据, 并非一次性将全部取出, 全局取出数据太大会撑爆内存.
对数据量大的数据非常有用.
name: redis的name
cursor: 游标(基于游标批量获取数据)
match: 匹配指定key, 默认为None, 表示所有的Key
count: 每次分片最少获取格式, 默认None表示采用Redis的默认分片个数.
hscan_iter(name, match=None, count=None)
利用yield封装hscan创建生成器, 实现分批去redis中获取数据.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2', 'k3': 'v3', 'k4': 'v4', })
print(conn.hscan('n1'))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.hmset('n1', {'k1': 'v1', 'k2': 'v2', 'k3': 'v3', 'k4': 'v4', })
res = conn.hscan_iter('n1')
print(res)
for i in res:
print(i)
"""
(b'k2', b'v2')
(b'k1', b'v1')
(b'k3', b'v3')
(b'k4', b'v4')
"""
7.9 其他
smove(rec, dstm value) 将某个成员从一个集合移动动到另一个集合
spop(name) 从集合的右侧(尾部)弹出一个成员, 并将其放回.
srandmenber(name, numbers) 从nane对应的集合移除numbers指定个数的元素
...
8. 列表操作
8.1 存取值
lpush(name, *values) 从左侧插值 后写的数据 ---> 先写的数据
rpush(name, *values) 从右侧插值 先写的数据 <--- 后写的数据
name存在才能写入
rpushx(name, values)
lpushx(name, values)
lrange 获取list指定索引区间的数据
lrange(name, start, end)
name: 列表名称
start: 开始的索引值
end: 结束的索引值
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
res = conn.lpush('list1', 1, 2, 3, 4, 5)
print(res)
print(conn.lrange('list1', 0, -1))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
res = conn.rpush('list2', 1, 2, 3, 4, 5)
print(res)
print(conn.lrange('list2', 0, -1))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
res1 = conn.rpushx('list3', 1, )
res2 = conn.lpushx('list4', 1, )
print(res1)
print(res2)
8.2 统计长度
llen(name) 统计name对应的列表的长度.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list5', 1, 2, 3)
res = conn.llen('list5')
print(res)
8.3 插入值
linsert(name, where, refvalue, value)
name: redis的name
where: BEFORE 或 AFTER (大小写都可以, before 在..前 after在..后)
refvalue: 游标值, 即: 它在前后插入数据(如果有多个游标值, 以找打的第一个为准)
value: 要插入的数据
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list6', 1, 2, 3)
conn.linsert('list6', 'BEFORE', 3, 'a')
conn.linsert('list6', 'AFTER', 3, 'b')
print(conn.lrange('list6', 0, -1))
8.4 依据索引设置值
lset(name, index, value) index从0开始
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list8', 1, 2, 3)
res1 = conn.lset('list8', 2, 'a')
8.5 弹出值
lpop(name) 左弹
rpop(name) 右弹
rpoplpush(src, des) 从一个列表取出最右边的的元素, 同时将其添加到另一个列表的最左边.
src 要取数据的列表name
dst 要添加数据的列表name
blpop(name, timeout)
brpop(name, timeout)
timeout 超时时间, 超时后还没值, 返回None. timeout=0表示永久.
brpoplpush(src, des, timeout) 从一个列表取出最右边的的元素, 同时将其添加到另一个列表的最左边.
值没有了就hang住等待.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list7', 1, 2, 3)
res1 = conn.rpop('list7')
print(res1)
conn.rpop('list7')
conn.rpop('list7')
print(conn.rpop('list7'))
res2 = conn.lpop('list7')
print(res2)
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list13', 1, 2, 3, 4, 5)
print(conn.lrange('list13', 0, -1))
conn.rpoplpush('list13', 'list14')
print(conn.lrange('list14', 0, -1))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list14', 1, 2, 3)
print(conn.blpop('list14'))
print(conn.blpop('list14'))
print(conn.blpop('list14'))
print(conn.blpop('list14'))
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list14', 4)
8.6 删除值
lrem(name, count, value)
name: 对应的list中删除指定的值
count=0: 删除列表中所有的指定值(列表中的值可以重复)
count=2 从前到后, 删除两个value指定的值
count=-2 从后向前, 删除两个value指定的值
value: 要删除的值
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list10', 1, 2, 3, 4, 5)
print(conn.lrange('list10', 0, -1))
conn.lrem('list10', count=0, value=1)
print(conn.lrange('list10', 0, -1))
8.7 索引取值
lindex(name, index) 依据索引取值
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list11', 1, 2, 3, 4, 5)
print(conn.lindex('list11', 0))
8.8 居间移除
ltrim(name, start, end) 移除区间以外的值
start: 索引的起始位置
end: 索引结束位置(大于列表长度, 则代表不异常任何)
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('list12', 1, 2, 3, 4, 5)
print(conn.lrange('list12', 0, -1))
conn.ltrim('list12', 0, 0)
print(conn.lrange('list12', 0, -1))
8.9 制作生成器
redis中没有提供列表的增量迭代, 如果想要循环name对应的列表所有元素, 需要自己定义.
如果列表非常发, 可能会撑爆内存, 每次值取值一部分值, 使用yield放回, 制作一个生成器.
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL)
conn.lpush('l1', *[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
def scan_list(name, count):
index = 0
while True:
data_list = conn.lrange(name, start=index, end=count + index - 1)
if not data_list:
return
for item in data_list:
yield item
index += count
for i in scan_list('l1', 2):
print(i)
"""
b'10'
b'9'
b'2'
b'1'
"""
9. 管道
redis中使用管道实现事务. 要么都成功要么都失败, redis是非关系型数据库, 对事务的支持都不是很好(没有回滚).
账单操作都是使用关系型数据库
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL, decode_responses=True)
conn.set('user1', 100)
conn.set('user2', 100)
pipe = conn.pipeline(transaction=True)
pipe.multi()
user1_money = int(conn.get('user1')) - 20
pipe.set('user1', user1_money)
user2_money = int(conn.get('user2')) + 20
pipe.set('user2', user2_money)
pipe.execute()
print(conn.get('user1'))
print(conn.get('user2'))
10. 其他操作
10.1 查看所有键
keys: 查看redis中所有的键
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL, decode_responses=True)
print(conn.keys())
"""
可以使用通配符 * - []
"""
print(conn.keys('user*'))
10.2 删除键
delete(key) 删除键
from connection_pool import POOL
import redis
conn = redis.Redis(connection_pool=POOL, decode_responses=True)
print(conn.keys())
print(conn.delete('user1'))
print(conn.keys())
10.3 类方法生成对象
类调用方法生成连接对象
Redis.from_url('redis://127.0.0.1:6379/')
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/')
print(conn.get('user2'))
10.4 执行redis命令
redis模块并没有把所有的redis命名都封装成方法, 如果没有该该方法使用execute方法去调用.
conn.execute_command(*args命令, **options参数)
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/')
print(conn.execute_command('get', 'user2'))
10.5 判断键是否存在
exists(name) 判断键是否存在, 存在返回1, 不会在返回0.
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/')
print(conn.exists('user2'))
print(conn.exists('user1'))
10.6 设置超时
expire(key, timeout) 为key设置超时时间
import time
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/')
print(conn.expire('user2', 1))
print(conn.get('user2'))
time.sleep(2)
print(conn.get('user2'))
10.7 重命名
rename(src, dst)
src 旧名字
dst 新名字
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/', decode_responses=True)
conn.set('l1', '我叫l1')
print(conn.get('l1'))
print(conn.get('l2'))
conn.rename('l1', 'l2')
print(conn.get('l1'))
print(conn.get('l2'))
10.8 切换db库
move(name, db库名)
db库是自定生成的, 一般的不会去操作, 值是int.
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/', decode_responses=True)
conn.move('l2', 1)
10.9 随机获取键
randomkey() 随机获取一个键
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/', decode_responses=True)
conn.set('k1', 'v1')
conn.set('k2', 'v2')
conn.set('k3', 'v3')
print(conn.randomkey())
10.10 获取键的对应值的类型
import redis
conn = redis.Redis.from_url('redis://127.0.0.1:6379/', decode_responses=True)
conn.set('k1', 'v1')
conn.set('k2', 'v2')
conn.set('k3', 'v3')
print(conn.type('k2'))
11. Django中使用redis
* 1. 通用方式: 使用redis模块, 生成连接对象去操作.
* 2. django_redis模块.(继承django的cache, 使用方式进而cache的使用一致,
数据被加密存到redis中, 不需要考虑数据类型.)
1. pip install django_redis
2. 在settings.py 配置文件中配置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}
}
}
}
文件夹是自动创建的...
* 3. 使用 django_redis 生成连接对象(需要考虑数据类型)
from django_from django_redis import get_redis_connection
conn = get_redis_connection('default')
conn.set('name', 'kid')
print(conn.get('name'))