redis介绍和安装
redis 什么
-数据库就是个存数据的地方:只是不同数据库数据组织,存放形式不一样 -mysql 关系型数据库(oracle,sqlserver,postgrasql) -非关系型数据(no sql):redis,mongodb,clickhouse,infludb,elasticsearch,hadoop。。。 -没有sql:没有sql语句 -not olny sql 不仅仅是sql -redis:一款纯内存存储的非关系型数据库(数据都在内存),速度非常快
redis特点
https://www.cnblogs.com/liuqingzheng/articles/9833534.html -redis是一个key-value存储系统 -数据类型丰富,支持5大数据类型:字符串,列表,hash(字典),集合,有序集合 -纯内存操作 -可以持久化:能都把内存数据,保存到硬盘上永久存储
redis为什么这快
-1 纯内存,减少io -2 使用了 io多路复用的 epoll 网络模型 -3 数据操作是单线程,避免了线程间切换 -多个客户端同时操作,不会存在并发安全问题
安装
-redis:最新是7, 公司里5,6比较多 -redis:开源软件,免费的,他们不支持win -epoll模型不支持win -微软官方:基于源码修改---》编译成可执行文件 -第三方:https://github.com/tporadowski/redis/releases/ -win:下载安装包,一路下一步 -安装目录在环境变量中:任意路径敲 redis-server reidis-cli 都能找到 -把redis做成了服务,以后通过服务启动即可 -mac:官网下载,解压即可 -win,mac:两个可执行文件: redis-server :等同于 mysqld reidis-cli :等同于mysql
redis普通连接和连接池
Python 代码作为客户端连接
安装模块
pip install redis
普通连接
from redis import Redis conn = Redis(host="localhost",port=6379,db=0,decode_responses=True) res=conn.get('name') print(res) conn.close()
连接池
import redis # 把池写成单例----》整个项目中,只有这一个实例(对象)---》python 中实现单例的5种方式---》模块导入的方式 POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=50) import redis from threading import Thread from pool import POOL def task(): conn = redis.Redis(connection_pool=POOL) print(conn.get('name')) conn.close() if __name__ == '__main__': for i in range(10): t = Thread(target=task) t.start()
Redis字符串类型
'''
1 set(name, value, ex=None, px=None, nx=False, xx=False)
2 setnx(name, value)
3 setex(name, value, time)
4 psetex(name, time_ms, value)
5 mset(*args, **kwargs)
6 get(name)
7 mget(keys, *args)
8 getset(name, value)
9 getrange(key, start, end)
10 setrange(name, offset, value)
11 setbit(name, offset, value)
12 getbit(name, offset)
13 bitcount(key, start=None, end=None)
14 bitop(operation, dest, *keys)
15 strlen(name)
16 incr(self, name, amount=1)
# incrby
17 incrbyfloat(self, name, amount=1.0)
18 decr(self, name, amount=1)
19 append(key, value)
'''
import redis
conn = redis.Redis(decode_responses=True)
# 1 set(name, value, ex=None, px=None, nx=False, xx=False)
# conn.set('age','19') # 没有就新增,有值就修改
# conn.set('hobby','篮球',ex=5) # ex过期时间 秒
# conn.set('hobby','篮球',px=5000)# 过期时间,毫秒
# conn.set('name','彭于晏')
# conn.set('name', 'lqz', nx=True) # nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果
# conn.set('name', 'lqz', xx=True) # 如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
# 2 setnx(name, value)
# conn.setnx('name','彭于晏')
# 3 setex(name, value, time)
# conn.setex('name',5,'sdasdfs')
# 4 psetex(name, time_ms, value)
# conn.psetex('name',5000,'lqz')
# 5 mset(*args, **kwargs) 批量设置
# conn.mset({'name': 'lqz', 'age': 29, 'gender': '男'})
# 6 get(name)
# print(conn.get('name'))
# 7 mget(keys, *args)
# print(conn.mget(['name','age']))
# print(conn.mget('name','age','gender'))
# 8 getset(name, value) # 等同于 get set
# print(conn.getset('name','oooo'))
# 9 getrange(key, start, end)
# print(conn.getrange('name',1,3)) # 前闭后闭区间
# 10 setrange(name, offset, value)
# conn.setrange('name',2,'ooo') # 包含2
# 先不聊---》操作比特位---》后面聊
# 11 setbit(name, offset, value)
# conn.setbit('name',7,1) # l=[1 1 0 0 0 1 0 0 ]
# 12 getbit(name, offset)
# 13 bitcount(key, start=None, end=None)
# 14 bitop(operation, dest, *keys)
# 15 strlen(name)
# print(conn.strlen('name'))
# 16 incr(self, name, amount=1)
# conn.incr('age',2) # 自加1,单线程,没有并发安全,数据不会错乱,天然适合计数器 计数器---》日活(日活跃用户数,只要有用户登录,就+1)
# # incrby
# 17 incrbyfloat(self, name, amount=1.0)
# conn.incrbyfloat('age',1.1)
# 18 decr(self, name, amount=1)
# conn.decr('age')
# 19 append(key, value)
# conn.append('name','ooo')
conn.close()
'''
set
get
getrange
strlen
'''
redis hash类型
''' hash 类型,就是咱们python中的字典类型, 数据结构:数据的组织形式 底层存储 数组---》根据key值使用hash函数得到结构,存到数组中 字典的key值必须可hash 字典的key值必须是不可变数据类型 hash 类型无序,跟放的先后顺序无关的 python 的字典是 有序的 字典+列表 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) ''' # print(hash(1)) # print(hash('asdfasdf')) # # print(hash([1,2,3,])) # unhashable # print(hash((1,2,3))) # 不可变数据类型 # a={(1,2,3):8} # print(a[(1,2,3)]) # class Person(object): # pass # p=Person() # # a={p:'asdf'} # print(a[p]) import redis conn = redis.Redis(decode_responses=True) # 1 hset(name, key, value) # conn.hset('userinfo','name','lqz') # conn.hset('userinfo','age','19') # 2 hmset(name, mapping) 弃用了,统一用hset # conn.hmset('userinfo2',{'name':'pyy',"age":33}) # conn.hset('userinfo3',mapping={'name':'xxx',"age":33}) # 3 hget(name,key) # print(conn.hget('userinfo','name')) # 4 hmget(name, keys, *args) # print(conn.hmget('userinfo',['name','age'])) # print(conn.hmget('userinfo','name','age')) # 5 hgetall(name) # 慎用---》userinfo 对应的value值非常多,一次性拿出来,很耗时 # print(conn.hgetall('userinfo')) # 6 hlen(name) # print(conn.hlen('userinfo')) # 7 hkeys(name) # print(conn.hkeys('userinfo')) # 8 hvals(name) # print(conn.hvals('userinfo')) # 9 hexists(name, key) # print(conn.hexists('userinfo','hobby')) # 10 hdel(name,*keys) # conn.hdel('userinfo',['name','age']) # conn.hdel('userinfo','name','age') # 11 hincrby(name, key, amount=1) # conn.hincrby('userinfo2','age') # 12 hincrbyfloat(name, key, amount=1.0) # 13 hscan(name, cursor=0, match=None, count=None) # 不单独用 ## 造数据 # for i in range(1000): # conn.hset('hash2','egg_%s'%i,'鸡蛋%s号'%i) # count 数字是大致的 大小,如果拿了10 ,可能是9 可能是11 # res=conn.hscan('hash2',cursor=0,count=10) # 无序,所以不是从egg_0开始的 # print(len(res[1])) # 14 hscan_iter(name, match=None, count=None) # 替代hgetall,一次性全取出值,如果占内存很大,会有风险 , 使用hscan_iter 分批获取值,内存占用很小 for item in conn.hscan_iter('hash2',count=10): print(item) # 分批获取数据 conn.close() ''' hset hget hlen hexists hincrby hscan_iter '''
redis列表类型
''' 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) ''' import redis conn = redis.Redis(decode_responses=True) # 1 lpush(name, values) # conn.lpush('girls','刘亦菲','迪丽热巴') # conn.lpush('girls','小红') # 2 rpush(name, values) 表示从右向左操作 # conn.rpush('girls', '小绿') # 3 lpushx(name, value) # 只有key存在,才能追加 # conn.lpushx('girls','小紫') conn.lpushx('girls1', '小紫') # 4 rpushx(name, value) 表示从右向左操作 # 5 llen(name) # print(conn.llen('girls')) # 6 linsert(name, where, refvalue, value)) # conn.linsert('girls', where='before', refvalue='刘亦菲', value='新刘亦菲') # conn.linsert('girls', where='after', refvalue='刘亦菲', value='老刘亦菲') # 7 r.lset(name, index, value) # conn.lset('girls',0,'oooo') # 按索引修改某个位置值 # 8 r.lrem(name, value, num) # conn.lrem('girls',1,'刘亦菲') # 从左侧删一个 # conn.lrem('girls',-1,'刘亦菲') # 从右侧删一个 # conn.lrem('girls',0,'刘亦菲') # 全删除 # 9 lpop(name) # print(conn.lpop('girls')) # 左侧弹出一个 # print(conn.rpop('girls')) # 右侧弹出 # 10 rpop(name) 表示从右向左操作 # 11 lindex(name, index) # print(conn.lindex('girls',0)) # 12 lrange(name, start, end) # print(conn.lrange('girls',1,10000)) # 前闭后闭 # 一次性把列表中数据都取出来 # print(conn.lrange('girls', 0, -1)) # 可以用-1 # print(conn.lrange('girls', 0, conn.llen('girls'))) # 可以用-1 # 13 ltrim(name, start, end) # conn.ltrim('girls',2,4) # 14 rpoplpush(src, dst) # conn.rpoplpush('girls','girls') # 15 blpop(keys, timeout) # block:阻塞 实现分布式的系统 消息队列 res=conn.blpop('girls',timeout=5) print(res) # 16 r.brpop(keys, timeout),从右向左获取数据 # 17 brpoplpush(src, dst, timeout=0) conn.close() # utf-8 编码的 bytes格式 # b=b'\xe8\xbf\xaa\xe4\xb8\xbd\xe7\x83\xad\xe5\xb7\xb4' # print(b.decode('utf-8')) # # for i in b: # print(bin(i)) ''' lpush rpush llen lrange lpop '''
案例(把redis中列表数据类型lrang使用生成器,写一个分批获取列表所有值的生成器,相当于封装成函数)
import redis conn = redis.Redis(decode_responses=True) def lrange_iter(conn, name, count): start = 0 end = count - 1 while True: values = conn.lrange(name, start, end) if not values: break yield from values start = end + 1 end = start + count - 1 for batch in lrange_iter(conn, 'girls', count=3): print(bat
redis其他操作
import redis conn = redis.Redis(decode_responses=True) conn.delete('gender1') # 删除keys print(conn.exists('gender1')) # 判断key值是否存在,0表示不存在,1表示存在 print(conn.keys(pattern='*')) # 取出所有的key值 conn.expire('userinfo', 5) # 设置key值存在多久,单位秒 conn.rename('userinfo3', 'userinfo') # 修改key值名字 conn.move('hash1', 1) # 将数据存在的位置换库,如果是同一个库会报错 print(conn.randomkey()) # 随机弹出一个数据 print(conn.type('hash')) # 查看数据的类型
redis管道
事物四大特性(关系型数据库都支持事务)
原子性:要么都成功,要么都失败 一致性:数据前后要一致 隔离性:多个事务之间相互不影响 持久性:事务一旦完成,数据永久改变
补充
redis没有专门的事务,但是可以通过其他方式来实现事物的几个特性,所以我们认为他是具备事物的。 redis要支持事务,要完成事务的几大特性,需要使用管道来支持 单实例redis是支持管道的 集群模式下,不支持管道,就不支持事务
案例(不使用管道)
一旦程序发生错误,先执行的一方减少,但另一方没有增加,不符合事务的特性:
import redis conn=redis.Redis() print(conn.decrby('my_money',20)) l=[0,1,2] print(l[5]) print(conn.incrby('he_money',10)) conn.close()
以上代码打印
案例(使用管道)
一旦程序发生错误,双方都失败
import redis conn = redis.Redis() # 创建了一个管道,把命令都一个个放到管道中,先不执行,当执行execute,才执行管道中所有的命令 pipeline = conn.pipeline(transaction=True) print(pipeline.decrby('my_money', 10)) # 我的钱扣了,写了点别的逻辑--》有可能抛异常 l = [0, 2, 3] print(l[9]) print(pipeline.incrby('he_money', 10)) pipeline.execute() conn.close()
以上代码结果
Django中使用redis
通用方案
# 写一个池 import redis POOL = redis.ConnectionPool(max_connections=20) # 在要使用的地方,导入使用即可 def redis_demo(requset): conn = redis.Redis(connection_pool=POOL, decode_responses=True) res = conn.incrby('count') print(res) return HttpResponse(f'您是我们第:{res}个用户')
第三方模块
# django-redis ---》配置文件中配置即可 pip install 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} # "PASSWORD": "123", } }, } # 在使用的位置,导入使用 from django_redis import get_redis_connection def redis_demo(requset): conn = get_redis_connection() res = conn.incrby('count') print(res) return HttpResponse(f'您是我们第:{res}个用户')
django缓存
redis数据存在内存中,取放速度快,非常适合做缓存
本来数据在mysql中,每次都查询,速度慢,把查询出来的数据,暂时存储到redis(缓存),
下次请求再来,直接从redis中拿,速度就会很快
django中如何使用缓存
配置文件配置(缓存位置:内存,文件中,redis中)
ACHES = { "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", }
-把数据放到缓存中(放到redis中) from django.core.cache import cache cache.set(key,value可以是任意类型,过期时间) -redis只支持5大数据类型,可以放python的任意类型 -本质:pickle序列化---》bytes格式---》以redis字符串的形式放在了redis中 cache.get(key)
前后端分离
cache.set # 存在redis cache.get # 取出redis数据
前后端混合
可以整站缓存 可以要缓存一个页面 可以缓存页面中的某个位置
可以缓存的位置
内存中 本地文件中 数据库中 reids中 (咱们用的多)
标签:name,redis,value,key,print,conn From: https://www.cnblogs.com/shanghaipudong/p/17770003.html