Redis
简介
Redis是一个key - value存储系统,非关系型数据库(nosql数据库),缓存数据库,数据存在于内存中
Redis支持五大数据类型
- 字符串
- 列表
- hash(字典)
- 集合
- 有序集合
使用Redis的好处
- 速度快:缓存数据库
- 支持丰富数据类型(五大数据类型)
- 支持事务(通过管道支持)(单实例上有效)
- 丰富的特性:可用于缓存、消息队列、按key设置过期时间,过期后自动删除
Redis对比memcached
- 都是内存数据库
- Redis类型丰富(五大数据类型),memcached只支持字符串类型
- Redis支持持久化,memcached不支持持久化
- Redis速度比memcached块
Redis特点
- 可以持久化
- 单线程,单进程
- Redis最新版本是6.x
- 6.x版本之前是单线程,单进程
Redis单线程为什么这么快(官方:10w左右并发,实际:6w左右并发)
- 纯内存操作
- 使用了io多路复用模型(epoll、select)
- 因为是单线程,不存在线程间切换,节省资源
服务:客户端,服务端(跟MySQL一样,是存数据的地方)
安装
- Redis是使用C写的且开源,官方并不支持Windows平台
- 第三方基于Redis源码,修改编译成Windows平台可以运行的(老:维护到3.x,新:维护到5.x)
- https://github.com/microsoftarchive/redis(Windows平台(3.x))
- 图形化界面客户端(redis-desktop-manager)
- 监听默认端口:6379
启动Redis服务
- 在任意路径下敲(前提:已经添加环境变量):redis-server
- Windows平台已经做成服务,直接开启服务
客户端连接
- 任意路径下敲:redis-cli -h 127.0.0.1 -p 6379
- 使用客户端连接
- 使用python操作(pip install redis)
普通连接与连接池
普通连接
# redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py
import redis
conn = redis.Redis(host=‘127.0.0.1‘, port=6379)
conn.set(‘foo‘, ‘Bar‘)
print(r.get(‘foo‘))
连接池
# redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销
# 默认:每个Redis实例都会维护一个自己的连接池
# 可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池
import redis
pool = redis.ConnectionPool(host=‘127.0.0.1‘, port=6379)
conn = redis.Redis(connection_pool=pool)
conn.set(‘foo‘, ‘Bar‘)
print(r.get(‘foo‘))
把POOL以单例形式使用
# pool.py
import redis
# 拿到一个连接数为3的连接池
# 把POOL做成单例:把它以模块导入方式
POOL=redis.ConnectionPool(max_connections=3,host=‘127.0.0.1‘, port=6380)
========================================================================================
import redis
from pool import POOL # 以模块形式导入,是天然单例
conn=redis.Redis(connection_pool=POOL)
# conn2=redis.Redis(connection_pool=POOL)
# conn3=redis.Redis(connection_pool=POOL)
# conn4=redis.Redis(connection_pool=POOL)
# conn5=redis.Redis(connection_pool=POOL)
# conn6=redis.Redis(connection_pool=POOL)
conn.set(‘age‘,19)
conn.close() # 不是关闭连接,把该连接,放回到连接池
Redis五大数据类型之字符串
字符串操作,Redis中的字符串再内存中按照一个name对应一个value来存储
‘‘‘
set(name, value, ex=None, px=None, nx=False, xx=False) 重点
setnx(name, value)
setex(name, value, time)
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)
strlen(name) 重点
incr(self, name, amount=1) 重点
incrbyfloat(self, name, amount=1.0)
decr(self, name, amount=1)
append(key, value)
‘‘‘
import redis
conn=redis.Redis()
# set:放字符串值
‘‘‘
ex,过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
‘‘‘
# conn.set(‘name‘,‘lqz‘)
# res=conn.set(‘name‘,‘lqz‘,ex=3)
# res=conn.set(‘name‘,‘lqz‘,px=3000)
# res=conn.set(‘name‘,‘lqz‘)
# res=conn.set(‘name‘,‘egon‘)
# res=conn.set(‘name‘,‘lqz‘,nx=True)
# res=conn.set(‘name‘,‘19‘,xx=True)
# get 获取值
# res=conn.get(‘name‘)
# mset 批量设置值
# conn.mset({‘name‘:‘lqz‘,‘age‘:19,‘sex‘:‘男‘})
# mget 批量获取值
# res=conn.mget([‘name‘,‘age‘])
# res=conn.mget(‘name‘,‘age‘)
# conn.setex(name, value, time)
# conn.psetex(name, time_ms, value)
# conn.set(name, valeu, px=time)
# res=conn.getset(‘name‘, ‘刘亦菲‘)
# res=conn.getrange(‘name‘,3,5) #前闭后闭区间 拿到 亦 这个字 拿字节
#
# print(str(res,encoding=‘utf-8‘))
# res=conn.setrange(‘name‘,0,‘lqzzzzzzz‘)
# res=conn.setrange(‘name‘,0,‘qq‘)
##获取和设置比特位
# setbit(name, offset, value)
# getbit(name, offset)
# print(conn.getbit(‘name‘,3))
# conn.setbit(‘name‘,0,1)
# print(conn.getbit(‘name‘,0))
# print(conn.strlen(‘name‘)) # 指的是字节 获取字节长度
# conn.incr(‘age‘) # 让该键自增1
# conn.incrby(‘age‘,3) # 让该键自增3
# conn.incrbyfloat(‘age‘, amount=1.1)
conn.append(‘name‘, ‘nb‘)
conn.close()
Redis五大数据类型之hash
‘‘‘
hset(name, key, value) 重点
hmset(name, mapping) 重点
hget(name,key) 重点
hmget(name, keys, *args) 重点
hgetall(name)
hlen(name) 重点
hkeys(name)
hvals(name)
hexists(name, key)
hdel(name,*keys)
hincrby(name, key, amount=1) 重点
hincrbyfloat(name, key, amount=1.0)
hscan(name, cursor=0, match=None, count=None)
hscan_iter(name, match=None, count=None)
‘‘‘
import redis
conn=redis.Redis()
# conn.hset(‘users‘,‘name‘,‘egon‘)
# conn.hset(‘users‘,‘age‘,‘19‘)
# res=conn.hget(‘users‘,‘age‘)
# res=conn.hget(‘users‘,‘name‘)
# conn.hmset(‘h1‘,{‘name‘:‘lqz‘,‘age‘:19})
# res=conn.hmget(‘h1‘,[‘name‘,‘age‘])
# res=conn.hmget(‘h1‘,‘name‘,‘age‘)
# res=conn.hlen(‘users‘) # 统计键值对的个数
# res=conn.hkeys(‘users‘)
# res=conn.hvals(‘users‘)
# hvals(name)
# print(str(res[2],encoding=‘utf-8‘))
# res=conn.hexists(‘users‘,‘age1‘)
# res=conn.hdel(‘users‘,‘age‘)
# conn.hincrby(‘h1‘, ‘age‘,7)
# conn.hincrbyfloat(‘h1‘, ‘age‘, amount=-1.01)
# for i in range(1000):
# conn.hset(‘htest‘,i,‘第%s个鸡蛋‘%i)
###hash(字典)类型是无序的
# res=conn.hgetall(‘htest‘)
# print(res)
# res=conn.hscan(‘htest‘, cursor=0,count=500) # 用的少
# print(res)
# res=conn.hscan(‘htest‘, cursor=254,count=499) # 用的少
# print(res)
# res=conn.hscan(‘htest‘, cursor=511,count=2) # 用的少
# print(res)
# print(res)
# print(len(res[1]))
res=conn.hscan_iter(‘htest‘, count=20) # 把hash类型的所有数据都打印出来,比hgetall节省内存
for item in res:
print(str(item[1],encoding=‘utf-8‘))
# print(res)
conn.close()
Redis五大数据类型之列表
‘‘‘
lpush(name,values) #从左侧插入值
lpushx(name,value)
llen(name) # 重点
linsert(name, where, refvalue, value)) # 重点
lset(name, index, value)
lrem(name, value, num) # 删除值
lpop(name) #从左侧弹出
lindex(name, index)
lrange(name, start, end)
ltrim(name, start, end)
rpoplpush(src, dst)
blpop(keys, timeout) # 阻塞式弹出
brpoplpush(src, dst, timeout=0)
‘‘‘
import redis
conn=redis.Redis()
# conn.lpush(‘list1‘,‘1‘) #左侧插入,上部
# conn.lpush(‘list1‘,‘lqz‘)
# conn.rpush(‘list1‘,‘egon‘) #右侧插入,下部
# conn.rpushx(‘list1‘,‘刘亦菲‘) # 只有name存在才能插入
# conn.lpushx(‘list2‘,‘刘亦菲‘)
# print(conn.llen(‘list1‘))
# conn.linsert(‘list1‘,‘before‘,‘egon‘,‘迪丽热巴‘)
# conn.linsert(‘list1‘,‘AFTER‘,‘egon‘,‘古力娜扎‘)
# conn.linsert(‘list1‘,‘AFTER‘,‘1‘,‘99999‘)
# conn.lset(‘list1‘,3,66666) # 修改某个位置的值
# 如果count=0,表示删除所有符合条件的,删除所有66666
# conn.lrem(‘list1‘,0,‘66666‘)
# 如果count=1,表示从左侧删除1个符合条件的,删除1个99999
# conn.lrem(‘list1‘,1,‘99999‘)
# 如果count=-2,表示从底部,从右侧,删除符合条件的2个
# conn.lrem(‘list1‘,-2,‘99999‘)
# res=conn.lpop(‘list1‘)
# res=conn.rpop(‘list1‘)
# res=conn.lindex(‘list1‘,1) #返回某个索引的值 列表操作 l[0]
# res=conn.lrange(‘list1‘, 0, 4) # 前闭后闭
# res=conn.ltrim(‘list1‘, 1, 3) #修剪,不符合范围的都删除,前闭后闭
# conn.lpush(‘list2‘,‘李易峰‘,‘刘亦菲‘,‘迪丽热巴‘)
# conn.rpoplpush(‘list1‘, ‘list2‘)
res=conn.blpop(‘list2‘,timeout=2) # 阻塞式弹出,通过它做一些分布式任务,可以用来做消息队列
print(res)
# brpoplpush(src, dst, timeout=0)
conn.close()
其他通用操作
‘‘‘
delete(*names)
# 根据删除redis中的任意数据类型
exists(name)
# 检测redis的name是否存在
keys(pattern=‘*‘)
# 根据模型获取redis的name
expire(name ,time)
# 为某个redis的某个name设置超时时间
rename(src, dst)
# 对redis的name重命名为
move(name, db))
# 将redis的某个值移动到指定的db下
randomkey()
# 随机获取一个redis的name(不删除)
type(name)
#查看类型
‘‘‘
##通用操作
import redis
conn=redis.Redis()
# conn.delete(‘h1‘)
# print(conn.exists(‘name‘))
# res=conn.keys(‘list*‘)
# print(res)
# conn.expire(‘list1‘,3)
# conn.rename(‘sex‘,‘sex111‘)
# conn.move(‘sex111‘,2)
# res=conn.randomkey()
# res=conn.type(‘users‘)
res=conn.type(‘name‘)
print(res)
conn.close()
管道
# redis支持事务,借助于管道
# import redis
#
#
# conn=redis.Redis()
#
# conn.decr(‘zhangsan‘,100)
#
# raise Exception(‘程序废了,出异常了‘)
#
# conn.incrby(‘wo‘,100)
#
# conn.close()
# 借助于管道实现
import redis
conn=redis.Redis()
pip_line=conn.pipeline(transaction=True) # 开启一个管道
pip_line.decr(‘zhangsan‘,100) # 往管道中放一个命令
raise Exception(‘程序崩了‘)
pip_line.incrby(‘wo‘,100) # 往管道中放一个命令
pip_line.execute() # 把管道中的命令一次性执行
conn.close()
Django使用Redis
# 第一种:通用方式(任何web框架都可以)
新建一个文件,其他文件导入使用
# 第二种:django提供的
-在配置文件中配置
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} # 连接池大小为100
# "PASSWORD": "123",
}
}
}
-安装django_redis模块
-使用
from django_redis import get_redis_connection
def index(request):
conn=get_redis_connection()
res=conn.get(‘name‘)
print(res)
return HttpResponse(‘okkkkk‘)
-一旦这样配置,django中的缓存,也用redis
cache.set(‘key‘,python的任意对象)