Redis入门一(介绍、安装、启动服务、Python操作redis、redis连接池链接、redis之字符串、hash、list及其他操作、Django中使用Redis、接口缓存)

一、Redis介绍和安装

1.介绍

1)redis是什么?

基于键值对的存储系统:字典形式,多种数据结构:字符串,hash,列表,集合,有序集合,高性能,功能丰富

官网:https://redis.io/

	-非关系型数据库:redis,mongodb,es,clickhouse,influxDB
		    	no sql: not only sql  # 不仅仅只有sql
	    
    -关系型数据库:mysql,oracle,postgrasql,sqlserver,sqlite
    -去IOE  ,国产化
        IBM:服务器     
        Oracle:数据库   达梦
        EMC:存储 

	-redis 到底是什么?
    	edis是缓存数据库【大部分时间做缓存,不仅仅可以做缓存】,属于非关系型数据库【区别于mysql关系型数据库】,存储是 key-value形式存储,没有表的概念。
        redis是一个key-value存储系统【软件】,用c语言写的,c/s架构的软件,纯内存存储,可以持久化【断电数据可以恢复】
        value:5种数据类型
            string:字符串
            hash:字典
            list:列表
            set:集合
            zset:有序集合
       # 版本    
       最新是redis 7.x ,; redis6.x之后,采用了多进程、多线程架构;普遍使用 redis5.x 最多。

2)Redis特性

	1.速度快:10w ops(每秒10w读写),数据存在内存中,c语言实现,单线程模型
	
	2.持久化:rdb和aof
	
	3.多种数据结构
	
	4.五大数据结构
	    BitMaps位图:布隆过滤器 本质是 字符串
	    HyperLogLog:超小内存唯一值计数,12kb HyperLogLog 本质是 字符串
	    GEO:地理信息定位 本质是有序集合
	    
	5.支持多种编程语言:基于tcp通信协议,各大编程语言都支持
	
	6.功能丰富:发布订阅(消息) Lua脚本,事务(pipeline)
		简单:源代码几万行,不依赖外部库
	
	7.主从复制:主服务器和从服务器,主服务器可以同步到从服务器中
	
	8.高可用和分布式:
	    2.8版本以后使用redis-sentinel支持高可用
	    3.0版本以后支持分布式
	    7.x 版本

3)redis为什么这么快?

	'redis为什么这么快?'
		- qps :10w   6w左右
	    -1 纯内存操作,避免了io
	    -2 使用了io多路复用的网络模型--(epoll)
	    -3 数据操作是单线程单进程---【没有锁操作,没有线程间切换】
	    
	    # mysql是多线程操作(有锁操作)
	    
	    
	'redis VS mysql'
	"""
	redis: 内存数据库(读写快)、非关系型(操作数据方便、数据固定)
	mysql: 硬盘数据库(数据持久化)、关系型(操作数据间关系、可以不同组合)
	
	大量访问的临时数据,才有redis数据库更优
	"""
	
	#redis VS memcache
	"""
	redis: 操作字符串、列表、字典、无序集合、有序集合 | 支持数据持久化(数据丢失可以找回(默认持久化,主动持久化save)、可以将数据同步给mysql) | 高并发支持
	memcache: 操作字符串 | 不支持数据持久化 | 并发量小
	"""	

4)redis应用场景

	'redis应用场景'
	1.当缓存数据库使用,接口缓存,提高接口响应速度
	2.做计数器:单线程,不存在并发安全问题
	3.去重操作:集合
	4.排行榜:有序集合
	5.布隆过滤器
	6.抽奖
	7.消息队列

2.安装

	mac   源码编译安装
	linux 源码编译安装
	win   微软自己,基于源码,改动,编译成安装包
	   # 最新5.x版本 https://github.com/tporadowski/redis/releases/
	   # 最新3.x版本 https://github.com/microsoftarchive/redis/releases
	
	    一路下一步,安装完释放出两个命令,会把redis自动加入到服务中
	    '注意安装第一页记得勾上,那个是配置到环境变量中,其他一路下一步即可'
	    -redis-server   # mysqld  服务端的启动命令
	    -redis-cli      # mysql    客户端的启动命令
	    	-h默认127.0.0.1-p默认6379-n默认0-a默认无
	    完整连接:
			>: redis-cli -h ip地址 -p 端口号 -n 数据库编号 -a 密码
	    先连接,后输入密码
	        >: redis-cli -h ip地址 -p 端口号 -n 数据库编号
	        >: auth 密码
	    -redis.windows.conf   # my.ini  配置文件
	    	databases 16  # 修改默认数据库数量
	        port 6379	# 修改端口号(监听端口)
	        bind 127.0.0.1	# 修改ip地址(服务地址)监听0.0.0.0即可别人访问
	    
	'redis默认有16个库(可在配置文件中修改databases 16),默认连进去就是第0个'

3.启动服务

	'前提:前往一个方便管理redis持久化文件的逻辑再启动服务:dump.rdb'

	'mysql也可以通过命令启动服务,而redis也可以,这里就不说在服务中启动了'
	1)前台启动服务
	>: redis-server  # 这个启动没有配置文件
	
	2)配置文件启动前台服务
	>: redis-server 配置文件的绝对路径
	
	3)后台启动服务
	>: redis-server --service-start
	注)Linux系统后台启动(或是修改配置文件,建议采用方式)
	>: redis-server &
	
	4)配置文件启动后台服务
	注)windows系统默认按Redis安装包下的redis.windows-service.conf配置文件启动
	>: redis-server --service-start
	注)Linux系统可以完全自定义配置文件(redis.conf)后台启动
	>: redis-server 配置文件的绝对路径 &
	
	
	"""
	windows系统
	1)前台启动
		i)打开终端切换到redis安装目录
		>: cd C:\Apps\Redis(可直接在redis目录地址栏输入cmd)
		
		ii)启动服务
		>: redis-server redis.windows.conf
	
	2)后台启动
		i)打开终端切换到redis安装目录
		>: cd C:\Apps\Redis(可直接在redis目录地址栏输入cmd)
		
		ii)启动服务(后面的配置文件可以省略)
		>: redis-server --service-start redis.windows-service.conf
	"""
	# 关闭服务
	1.在服务中直接找到redis点击关闭
	2.直接在运行窗口执行shutdown(在客户端执行这个命令) # 得运行起来
	3.直接在运行窗口Ctrl+C结束运行(在服务端执行这个) # 得运行起来
	
	
	# redis数据是存在内存中得
		-重启redis服务或关机,数据都会丢失
	    -咱们不会丢 redis.windows-service.conf 已经写了持久化方案
	    	从内存把数据保存到硬盘上的过程称之为持久化('永久保存')

请添加图片描述
请添加图片描述

二、Python操作Redis(普通链接)

	# 安装模块
	pip install redis

	# 1.导入模块类
	from redis import Redis
	
	# 2.实例化得到对象
	conn = Redis(host="localhost",port=6379,db=0)  # 不传也行,这些就是默认参数
	conn = Redis(decode_responses=True)  # 这个就是转码默认返回bytes格式
	
	
	# 3.获取值
	# print(conn.get('name'))  # 默认是返回bytes格式 b'jack'
	# 前提是redis数据库中要有这条记录,并且是在0库中。否则会显示None
	
	# 分步操作(转换成字符)
	# res = conn.get('name')
	# print(res.decode(encoding='utf-8'))
	
	# 连着操作(转换成字符)
	# print(conn.get('name').decode(encoding='utf-8'))
	
	# 强转操作(转换成字符)
	res = conn.get('name')
	print(str(res,encoding='utf-8'))
	
	# 4.设置值
	# 默认字符串形式存储是以 utf-8 形式存储
	conn.set('gender','male')  # key:value形式
	# 因为是k:v形式所以第二次执行同一个key的时候只会替换掉之前的value值
	'前提还是同一个库中,还是字符串类型的操作'
	conn.set('gender','female')
	
	# 5.关闭
	conn.close()

三、Redis连接池链接

	-django中操作mysql,默认是没有链接池
		#一个请求就是一个mysql连接。可能会有问题,并发数过高,导致mysql连接数过高,影响mysql性能;
	    #使用django连接池:https://blog.51cto.com/liangdongchang/5140039
	
	    
	-redis默认是自带链接池的
	'''
	redis使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池
	'''
	
	
	-1.直接使用(基础)
	import redis  # 只导入到包那层
	# 创建池
	pool = redis.ConnectionPool(max_connections=3,  # 最大链接数3
	                          host="localhost",port=6379,
	                            decode_responses=True)  # 解码  是以链接池作为链接,所以解码在这里
	# max_connections = max_connections or 2**31 默认的最大链接数是2的31次方
	
	# # 每次从池中取出一个链接
	conn = redis.Redis(connection_pool=pool)
	print(conn.get('name'))
	conn.close()  # 关闭,把链接放回到连接池
	
	'========================================================'
	
	-2.直接使用(起线程)
	import redis
	from threading import Thread  # 起线程
	# 创建池
	pool = redis.ConnectionPool(max_connections=3,  # 最大链接数3
	                          host="localhost",port=6379,
	                            decode_responses=True)
	# 起10个线程
	def task():
	    # 每次执行都实例化一个对象
	    conn = redis.Redis(connection_pool=pool)
	    print(conn.get('name'))
	    conn.close()  # 关闭,把链接放回到连接池
	
	l = []
	for i in range(10):  
	    # 因为起了十个线程,但是连接池最大3个,所以会有7个报错
	    t = Thread(target=task)
	    t.start()
	    l.append(t)
	
	for t in l:
	    t.join()
	
	print('结束执行')
	
	# 直接使用情况,项目中其他地方要使用,那么每次都需要实例化一个新的对象
	# 这样太麻烦了,所以我们可以做成一个包的形式,导入使用,这样想要使用的地方就直接导入即可(单例)
	'========================================================'
	
	-3.python中如何实现单例设计模式
	'将连接池做成一个单例模式,后续使用直接导入即可'
	# 使用模块导入的方式,无论导入多少次,使用的都是同一个
	
	--pool.py
	import redis  # 只导入到包那层
	# 创建池
	POOL = redis.ConnectionPool(max_connections=3,host="localhost",port=6379,decode_responses=True)
	
	
	--xxx.py
	import redis  # 只导入到包那层
	from threading import Thread  # 起线程
	from pool import POOL  # 导入
	def task():
	    # 创建一个连接池,保证它是单例,全局只有一个pool对象:使用模块导入方式实现单例
	    # 做成模块后,导入,无论导入多少次,导入的都那一个POOL对象
	    conn = redis.Redis(connection_pool=POOL)
	    print(conn.get('name'))
	    conn.close()  # 关闭,把链接放回到连接池
	
	l = []
	for i in range(3):
	    t = Thread(target=task)
	    t.start()
	    l.append(t)
	
	for t in l:
	    t.join()
	
	print('结束执行')

1.小测试案例

	1.直接方式  test.py
	class Person:
	    pass
	
	p1 = Person()
	p2 = Person()
	print(p1==p2)
	# False  结果为不同的Person类
	
	
	2.导入方式 ll.py
	from test import p1
	from test import p1 as p2
	print(p1==p2) # True

四、Redis之字符串操作

	'字符串类型的基本方法使用'

	'''redis字符串操作'''
	import redis
	# 这里我就不导入池操作了,这里也只有一个链接用,池现在设置了3个链接,没有必要
	pool = redis.ConnectionPool(max_connections=1,decode_responses=True)
	# pool = redis.ConnectionPool(max_connections=1,)
	
	conn = redis.Redis(connection_pool=pool)
	
	1 set(name, value, ex=None, px=None, nx=False, xx=False)
	'''
	    ex,过期时间(秒)
	    px,过期时间(毫秒)
	    nx,如果设置为True,则只有name不存在时,当前set操作才执行, 值存在,就修改不了,执行没效果
	    xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值
	'''
	conn.set('hobby','rap')  # set设置值
	conn.set('age',19,ex=3)  # 设置值存放在redis数据库中3秒后清除
	conn.set('age',19,px=3000)  # 设置值存放在redis数据库中3秒后清除
	conn.set('age',19,px=3000)  # 设置值存放在redis数据库中3秒后清除
	conn.set('age',16,nx=True)  # 当name不存在,可执行设置,否则无法设置,无效
	conn.set('xxxx',16,xx=True)  # 当name存在,可执行设置,否则无法设置,无效
	
	
	2 setnx(name, value) # 等同于conn.set('age',19,nx=True)
	conn.setnx('age',19)
	
	
	3 setex(name,time,value) # 等同于conn.set('age',19,ex=3)
	conn.setex('age',3,22)
	
	4 psetex(name, time_ms, value)  # 存在多少毫秒清除
	conn.psetex('xxx',3000,'six')   和setex相同,只是时间单位不同
	
	
	5 mset(*args, **kwargs)  # 批量设置  字典形式{}也可以=号
	conn.mset({'xxx': 'six', 'yyy': 'seven'}) # 已存在的会替换value
	
	
	6 get(name)  获取值
	print(conn.get('name'))  # 默认redis是bytes格式,我上面池以及解码了
	
	7 mget(keys, *args) # 批量获取  列表形式[],也可以直接逗号分割
	print(conn.mget(['name','gender']))
	print(conn.mget('name','gender'))
	
	8 getset(name, value)  # 先获取再设置,先获取存在的值(老),在设置新值
	print(conn.getset('name','tom'))  # oscar
	print(conn.getset('age',23))  # None 不存在值是返回None,并设置
	
	
	
	9 getrange(key, start, end) # 取的是字节,前闭后闭区间
	'''
	    utf-8:中文3个字节,英文1个字节
	    字节:8个比特位一个字节,一个字母字符一个字节就够了
	    字符:a,b,国  ,中文utf-8编码需要3个字节一个字符
	'''
	# 取3bytes 因为是取的bytes,前面设置了解码需要去掉,不然只要是中文就得是3的倍数,否则报错
	print(conn.getrange('name',0,1))  # 因为是中文,只拿到2个字节,所以报错
	print(conn.getrange('name',0,2).decode(encoding='utf-8'))  # 去掉解码自己在这里加
	print(conn.getrange('name',0,2))
	
	
	
	10 setrange(name, offset, value)  # 从某个起始位置开始替换字符串
	# 注意这个也是取的bytes格式,如果有中文没有设置3的倍数截断,会变乱
	conn.setrange('name',2,66)
	
	'''比特位---操作'''
	11 setbit(name, offset, value)
	12 getbit(name, offset)
	print(conn.getbit('name',0))  # 只能获取到0,1
	13 bitcount(key, start=None, end=None)
	14 bitop(operation, dest, *keys)
	'''比特位---操作'''
	
	
	
	15 strlen(name) # 统计字节长度
	print(conn.strlen('name'))
	print(len('彭于晏'))  # 统计字符长度
	
	
	
	16 incr(self, name, amount=1) # incrby  不写默认自增1
	# 自增,不会出并发安全问题,单线程架构,并发量高
	conn.incr('age',amount=1)  # 计数器  amount是单次所需加的数量
	conn.incrby('age',amount=1)  # 和incr一模一样
	
	
	
	17 incrbyfloat(self, name, amount=1.0)  自增浮点数
	conn.incrbyfloat('age',amount=1.0)
	
	
	18 decr(self, name, amount=1) 不写默认自减1
	conn.decr('age',amount=2)
	
	
	19 append(key, value)  # 追加,如果不存在key则新增,否则在当前value值后面追加
	conn.append('tian','xxxx')
	conn.append('age',6)  # 数字也是在后面加,不会有运算
	conn.append('name','nb')
	
	
	conn.close()  # 关闭链接
	
	'常用的get、setsetex、getrange、setrange、strlen、append'

五、Redis之hash操作

	'''redis哈希(字典)操作'''
	import redis
	pool = redis.ConnectionPool(max_connections=1, decode_responses=True)
	# pool = redis.ConnectionPool(max_connections=1,)
	conn = redis.Redis(connection_pool=pool)
	
	1 hset(name, key, value)
	'''
	# name,redis的name
	# name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
	# key,name对应的hash中的key
	# value,name对应的hash中的value
	'''
	conn.hset('userinfo','name','oscar')
	conn.hset('userinfo','age',19)
	# 相当于[userinfo:{'name':'oscar','age':19}]
	
	
	2 hmset(name, mapping) 批量设置
	# 在name对应的hash中批量设置键值对
	# 批量设置,被弃用了,以后都使用hset
	conn.hmset('userinfo',mapping={'hobby':'rap','xxx':66})
	
	3 hget(name,key)
	# 在name对应的hash中获取根据key获取value
	print(conn.hget('userinfo','age'))
	
	
	4 hmget(name, keys, *args) 批量获取
	# 在name对应的hash中获取多个key的值
	print(conn.hmget('userinfo',['age','xxx']))  # 设置[]的都会给keys
	print(conn.hmget('userinfo','age','xxx'))  # 不设置的第二个以后都会给*args
	
	
	5 hgetall(name)
	# 获取name对应hash的所有键值   慎用
	# 可能会造成 阻塞 尽量不要在生产代码中执行它
	print(conn.hgetall('userinfo'))
	
	
	6 hlen(name)
	# 获取name对应的hash中键值对的个数
	print(conn.hlen('userinfo'))
	
	
	7 hkeys(name)
	# 获取name对应的hash中所有的key的值
	print(conn.hkeys('userinfo'))
	
	
	8 hvals(name)
	# 获取name对应的hash中所有的value的值
	print(conn.hvals('userinfo'))
	
	
	9 hexists(name, key)
	# 检查name对应的hash是否存在当前传入的key  存在True否则False
	print(conn.hexists('userinfo','age'))
	
	
	10 hdel(name,*keys)
	# 将name对应的hash中指定key的键值对删除
	print(conn.hdel('userinfo', 'age', 'hobby'))
	print(conn.hdel('userinfo','xxx'))  # 会返回影响的行数
	
	
	11 hincrby(name, key, amount=1)  # 不写amount默认为1
	# 自增name对应的hash中的指定key的值,不存在则创建key=amount
	conn.hincrby('userinfo','age',amount=1)
	
	
	12 hincrbyfloat(name, key, amount=1.0)  # 不写amount默认为1.0 浮点数
	conn.hincrbyfloat('userinfo','age',amount=1.2)  #会有偏差
	
	
	13 hscan(name, cursor=0, match=None, count=None)
	'''
	# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,
	# 并非一次性将数据全部获取完,从而放置内存被撑爆
	# name,redis的name
	# cursor,游标(基于游标分批取获取数据)
	# match,匹配指定key,默认None 表示所有的key
	# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
	'''
	# hgetall  会一次性全取出,效率低,可以能占内存很多
	# hscan分批获取,hash类型是无序
	# 插入一批数据
	# for i in range(1000):
	#     conn.hset('hast_count', i, f"鸡蛋{i}")
	
	'''当数据量小的时候,hash是有序排列的,这个时候,使用hscan设置count则无效'''
	#HSCAN是为了处理大量数据而设计的,可能也是因为这个原因,在数据量较少的情况下count参数并不会生效
	# for i in range(500):
	#     conn.hset('xxxx_test', i, f"鸡蛋{i}")
	
	
	# print(conn.hgetall('map_demo'))  # 一次性取全部数据
	
	# 一点点的取
	# 它不单独使用,拿的数据,不是特别准备
	# count 是要取的条数,但是不准确,有点上下浮动
	# res = conn.hscan('map_demo',cursor=5,count=20)
	# res = conn.hscan('hast_text',cursor=0,count=20)
	# print(res)  # (数字,{拿出来的5条数据})
	
	'''
	当遍历只包含Integer值的Set集合(也称为intsets),或者ziplists类型编码的Hash或者Sorted Set集合(
	说明这些集合里面的元素占用的空间足够小),那么SCAN命令会返回集合中的所有元素,直接忽略COUNT属性。
	'''
	
	
	
	14 hscan_iter(name, match=None, count=None)  # 取出所有数据,但是是一点点取(count值),一点点用
	# 内部是一个生成器  ,它内部封装了hscan,做成了生成器,分批取hash类型所有数据
	# res = conn.hscan_iter('xxxx_test',count=10)  # 这个数字并不是取10条,而是每次取10条,一直到所有取尽
	res = conn.hscan_iter('hast_count',count=10)
	print(res) # <generator object ScanCommands.hscan_iter at 0x0000019266AFF660>
	# generator 只要函数中有yield关键字,这个函数执行的结果就是生成器 ,生成器就是迭代器,可以被for循环
	for item in res:
	    print(item)
	
	
	conn.close()
	
	'hset、hget、hmget、hlen、hincr、hscan_iter'

六、Redis之list操作

	'''redis列表操作'''
	import redis
	
	pool = redis.ConnectionPool(max_connections=1, decode_responses=True)
	# pool = redis.ConnectionPool(max_connections=1,)
	
	conn = redis.Redis(connection_pool=pool)
	
	1 lpush(name, values)  # 当不存在此name时会直接创建
	# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边  (上左下右)
	conn.lpush('girls','刘亦菲')  # 从左侧插入值
	conn.lpush('girls','迪丽热巴')
	
	2 rpush(name, values) 表示从右向左操作
	conn.rpush('girls','赵美延')  # 从右侧插入值
	
	3 lpushx(name, value)
	# 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
	conn.lpushx('girls','金玟庭')
	conn.lpushx('boys','彭于晏')  # 当name不存在时无效
	
	4 rpushx(name, value) 表示从右向左操作
	# 只有name已经存在时,值添加到表示从右向左操作
	conn.rpushx('girls','裴珠泫')
	conn.lpushx('boys','金城武')  # 当name不存在时无效
	
	5 llen(name)
	# name对应的list元素的个数
	print(conn.llen('girls'))
	
	
	6 linsert(name, where, refvalue, value))
	# 在name对应的列表的某一个值前或后插入一个新值
	# where 条件  before是什么前面,after是什么后面
	# refvalue 是指定在那个value值,之前或之后的(where条件)
	# value值
	conn.linsert('girls', where='before', refvalue='赵美延', value='田振姬')  # before什么之前
	conn.linsert('girls',where='after',refvalue='赵美延',value='柳智敏')  # after什么之后
	conn.linsert('girls',where='before',refvalue='xxx',value='xxxx')  # 到name对应的列表中无此value则无效,返回值为-1
	
	
	7 lset(name, index, value) # 按照列表的索引来计算从0开始
	# 对name对应的list中的某一个索引位置重新赋值
	conn.lset('boys',0,'彭于晏')
	conn.lset('boys',2,'金城武')  # 当name中不存在此索引就报错
	
	8 lrem(name, num(count),value)  按关键字传递则无需对应位置,否则需要对应位置
	# 在name对应的list中删除指定的值
	'''
	 # name,redis的name
	# value,要删除的值
	# count,count=0,删除列表中所有的指定值;
	# count=2,从前到后,删除2个;
	# count=-2,从后向前,删除2个
	count表示删除对应的value的个数,正数为从上到下找,负数为从下往上
	注意redis的列表是可以重复名的
	'''
	conn.lrem('boys',0,'郭富城')  # 当name对应的列表不存在此value则无效
	conn.lrem('boys',1,'吴彦祖')  # 正数为从左往右删除对应value的第一个值
	conn.lrem('boys',-1,'金城武')  # 负数为从右往左删除对应的value的第一个值
	conn.lrem('boys',0,'彭于晏')  # 0表示删除对应的value的所有值
	
	9 lpop(name)
	# 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
	print(conn.lpop('boys'))  # 弹出对应name列表中最上面的一个元素,并且返回出来
	
	10 rpop(name) 表示从右向左操作
	print(conn.rpop('boys'))  # 弹出对应的name列表中的最下面的一个元素,并返回出来
	
	
	11 lindex(name, index)  # 列表索引从0开始
	# 在name对应的列表中根据索引获取列表元素
	print(conn.lindex('boys',0))  # 获取对应name的列表索引所对应的元素
	print(conn.lindex('xxx',0)) # 当获取的name没有时返回None
	
	12 lrange(name, start, end)
	'''
	# 在name对应的列表分片获取数据  获得到是闭合区间
	# name,redis的name
	# start,索引的起始位置
	# end,索引结束位置 
	'''
	print(conn.lrange('girls',2,4)) # 从索引2到4获取
	# 当对应的name所在的列表中没有相对应的索引数时,只获取有的索引数的元素,不会报错
	print(conn.lrange('boys',2,4)) ['金城武']
	print(conn.lrange('xxx',2,4))  # 当无对应的name时,返回空列表[]
	
	
	13 ltrim(name, start, end) # 从0开始的索引
	# 在name对应的列表中移除没有在start-end索引之间的值
	# 只要是打印,所有无效或者有对应name都,返回True
	conn.ltrim('boys',3,5) # name对应的列表中,只保留设置的start-end索引之间的值,其他移除
	print(conn.ltrim('xxx',3,5))  # 当不存对应的name时无效,不会报错
	
	14 rpoplpush(src, dst)
	# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
	# src,要取数据的列表的name
	# dst,要添加数据的列表的name
	# 当第一次执行的时候,会把第一个列表(src)的元素翻转后拿最下面的一个,本质还是拿原初的最上面的第一个
	# 如果按照队列的说法就是弹出最后进入的值,在redis中默认添加值是放在上面
	# print(conn.rpoplpush('boys','test'))
	# 当第二个name对应列表不存在时(dst),第一个存在会创建出第二个列表对应的name,并把第一个列表的值传入进去
	print(conn.rpoplpush('boys','lll'))  会返回src取出的元素
	print(conn.rpoplpush('yyy','lll'))  # 当src对应的的列表不存在时无效,返回None
	
	
	15 blpop(keys, timeout)  # 阻塞式弹出,可以做消息队列,分布式
	# 将多个列表排列,按照从左到右去pop对应列表的元素
	'''
	# keys,redis的name的集合
	# timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞
	# 从左到右弹出,当对应的name中没有值了就会阻塞在这里
	# 在另一个程序中如果在创建出这个name就会把它弹出,这样就结束阻塞
	'''
	print(conn.blpop('boys'))  # 不设置tiemout默认为0,表示永远阻塞,直到重新设置此name结束或者直接中断程序
	print(conn.blpop('yyy',timeout=10))  # 当没有对应的name列表只阻塞10秒就结束运行,并返回None
	print(conn.blpop('yyy'))
	
	
	16 brpop(keys, timeout),从右向左获取数据
	print(conn.brpop('boys')) # 和blpop一样也是阻塞式,不过是从下到上弹出
	
	
	
	17 brpoplpush(src, dst, timeout=0)
	'''
	# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
	# 参数:
	# src,取出并要移除元素的列表对应的name
	# dst,要插入元素的列表对应的name
	# timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
	'''
	
	conn.close()  # 关闭链接
	
	
	'常用:lpush、llen、lpop、lindex、lrange、blpop'

请添加图片描述

七、Redis其他操作

	'''redis通用操作'''
	import redis
	pool = redis.ConnectionPool(max_connections=1, decode_responses=True)
	# pool = redis.ConnectionPool(max_connections=1,)
	
	conn = redis.Redis(connection_pool=pool)
	'''通用操作,不指定类型,所有类型都支持'''
	
	1.delete(*names)
	# name根据删除redis中的任意数据类型
	conn.delete('name','age')  # 直接删除name,age
	
	2.exists(name)
	# name检测redis中的name是否存在 存在返回1否则0
	print(conn.exists('name')) # 0
	print(conn.exists('gender')) # 1
	
	3.keys(pattern='*')  # 不写pattern默认把所有key都拿出来
	# pattern=* 根据模型获取redis的name
	# ?表示一个字符,   * 表示多个字符
	print(conn.keys(pattern='h*'))
	print(conn.keys('?e'))
	
	
	4.expire(name ,time)
	# name,time为某个redis的某个name设置超时时间
	conn.expire('yyy',3)
	
	5.rename(src, dst)
	# src,dst, 对redis的name重命名为
	conn.rename('xxx','yyy')
	
	
	6.move(name, db))
	# name,db,将redis的某个值移动到指定的db下
	# 默认操作都是0 库,总共默认有16个库
	conn.move('yyy',2)
	
	
	7.randomkey() # 随机获取键值
	# 随机获取一个redis的name(不删除)
	print(conn.randomkey())
	
	
	8.type(name)
	# 获取name对应值的类型
	print(conn.type('gender'))  # string
	print(conn.type('userinfo'))  # hash
	
	conn.close()

八、Django中使用Redis

1.方式一:通用方式

	1.创建一个pool.py文件
	import redis  # 导入redis模块
	
	# 创建池
	POOL = redis.ConnectionPool(max_connections=3, decode_responses=True)
	
	
	2.配置路由
	from django.urls import path
	from . import views
	from rest_framework.routers import SimpleRouter
	router = SimpleRouter()
	router.register('redis',views.RedisView,'redis')
	urlpatterns = []
	urlpatterns += router.urls
	
	
	2.在任意视图中使用,想用就导入即可
	import redis
	from utils.pool import POOL  # 导入创建的池
	class RedisView(ViewSet):
	    def list(self, request, *args, **kwargs):
	        conn = redis.Redis(connection_pool=POOL)
	        conn.incrby('count')
	        count = conn.get('count')
	        return APIResponse(message=f"您是第{count}个访问的")

请添加图片描述

2.方式二:django-redis

	1.安装django-redis
	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",
	        }
	    }
	}
	
	
	# 3.在想用的地方直接导入使用即可
	from django_redis import get_redis_connection
	class RedisView(ViewSet):
	    def list(self, request, *args, **kwargs):
	        conn = get_redis_connection() # 从池中获取一个链接
	        conn.incrby('count')
	        count = conn.get('count')
	        return APIResponse(message=f"您是第{count}个访问的")

3.方式三:django的缓存使用redis

	'使用django内置的cache缓存,然后配置好使用缓存的位置例如内存中,等等'
	'但是放在内存中,重启项目数据就没有了'
	'后期我们要把缓存数据放到redis中,因为redis可以持久化,项目重启,但是redis还在运行,数据就不会丢失'
	
	
	1.在配置文件中配置如下,以后cache.set和cache.get 统统都是去redis中
	'如果不这样配置cache也可以直接使用cache.set/cache.get,看设置在哪里存储缓存,会丢失'
	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",
	        }
	    }
	}
	
	# 强大之处在于,可以直接缓存任意的python对象,底层使用pickle实现的
	
	
	2.在想使用的地方,导入使用即可
	from django.core.cache import cache
	class RedisView(ViewSet):
	    def list(self, request, *args, **kwargs):
	        try:
	            count = cache.get('count') + 1
	            cache.set('count', count)
	        except Exception as e:
	            cache.set('count', 1)
	            count = 1
	        return APIResponse(message=f"您是第{count}个访问的")
	    
	    
	重点:
		-优势:redis 分数据类型, 只能设置5种数据类型
	   	-django的缓存来讲 ,不限制类型,可以放python的任意类型
	    
	    -django的缓存来讲,cache.set('redis的key','不区分类型:放python的任意类型')  
	    # 放个对象,字典什么的都可以,这样到时候使用cache.get取出来还是原来的
	    -cache.get('userinfo')
	    
	    -django cache 底层是基于: 把你存储的类型---》使用pickle序列化--bytes格式---》当redis的字符串形式存到redis中
	    
	    -以后咱们做redis的操作,可以直接使用django的缓存, 不需要考虑类型

九、接口缓存

	以我们现在的项目首页轮播图接口,现在只要有一个用户访问一次首页,就会去数据库查询
	这样用户量大了之后,同时来访问首页,就会不停的查询banner表
	'''
	我们现在可以使用django的cache来优化,第一次去数据库查询,然后把数据取出来放到缓存中,
	以后只要访问轮播图接口,都会从缓存中取出来,这样接口的响应速度就会非常快
	'''
	
	
	# 使用轮播图加缓存
	from rest_framework.viewsets import GenericViewSet
	from utils.mixins import CommonListModelMixin
	from . import models
	from django.conf import settings
	from .serializer import BannerSerializer
	from rest_framework.mixins import ListModelMixin
	from django.core.cache import cache
	from utils.common_response import APIResponse
	from utils.common_logger import logger
	class BannerView(GenericViewSet, CommonListModelMixin):
	    queryset = models.Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
	    serializer_class = BannerSerializer
	
	    def list(self, request, *args, **kwargs):
	        '''基础版'''
	        # # 第一次缓存中是没有,就需要去数据库中查询,然后放入到缓存中
	        # banner_list = cache.get('banner_list')  # 是一个列表套字典格式
	        # if banner_list:
	        #     return APIResponse(result=banner_list)
	        # else:
	        #     # 如果缓存中有了,那么就去缓存中取出来
	        #     # 因为我想要用ListModelMixin,所以直接知名道姓的使用,然后把自己传入进去self(因为我继承的是自己封装的,如果直接使用super就是调用封装的,而不是原本的)
	        #     res = ListModelMixin.list(self, request, *args, **kwargs)
	        #     banner_list=res.data
	        #     cache.set('banner_list', banner_list)
	        #     return APIResponse(result=banner_list)
	        '''优化版'''
	        # 第一次缓存中是没有,就需要去数据库中查询,然后放入到缓存中
	        banner_list = cache.get('banner_list')  # 是一个列表套字典格式
	        if not banner_list:
	            logger.info('这里去数据库中查询了')
	            # 如果缓存中有了,那么就去缓存中取出来
	            # 因为我想要用自己封装的ListModelMixin,所以直接知名道姓的使用,然后把自己传入进去self
	            res = ListModelMixin.list(self, request, *args, **kwargs)
	            banner_list = res.data
	            cache.set('banner_list', banner_list)
	
	        return APIResponse(result=banner_list)
	
	
	'但是这种如果后期数据库的banner表数据变了,它也不会去查询,而是一直取换成中的数据,这样会导致数据上的不一致'
	# 我们得解决这个问题
		-这个问题有个学名(专业的叫法):双写一致性
	    	mysql,redis要同步写
	
	-封装一个mixin的类,不需要重写list方法,只需要配置一下,就能使用缓存
	'utils/mixins.py'
	'''缓存版'''
	from django.core.cache import cache
	from utils.common_logger import logger
	class CacheListModelMixin(ListModelMixin):
	    cache_key = None
	    def list(self, request, *args, **kwargs):
	        if self.cache_key:
	            res = cache.get(self.cache_key)
	            if not res:
	                logger.info('执行了去数据库中查询')
	                response = super().list(request, *args, **kwargs)
	                cache.set(self.cache_key,response.data)
	                res = response.data
	            return APIResponse(result=res)
	        else:
	            logger.info('用户没配置缓存的key,都是查询的数据库')
	            res = super().list(request, *args, **kwargs)
	            return APIResponse(result=res.data)
	
	
	        
	'''最终版,使用django缓存'''
	from utils.mixins import CacheListModelMixin
	class BannerView(GenericViewSet, CacheListModelMixin):
	    cache_key = 'banner_list'
	    queryset = models.Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
	    serializer_class = BannerSerializer
	        
	# 后期,所有带有查询所有或单条的接口,都可以加上缓存
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要使用Redis连接池的方式实现,需要先安装redis-py模块,并在代码导入redis模块和连接池模块。下面是一个示例代码,可以用于写入指定节点的配置: ```python import redis from redis import ConnectionPool # 创建Redis连接池 pool = ConnectionPool(host='localhost', port=6379, db=0) def writeinifile(section, key, value): # 创建Redis连接对象 r = redis.Redis(connection_pool=pool) # 使用hset命令将键值对写入指定的节点 r.hset(section, key, value) ``` 这个函数接受三个参数:节点名(section)、键(key)和值(value),它们都应该是字符串类型。首先,创建一个Redis连接池对象,然后在函数创建一个Redis连接对象,使用`hset`命令将指定的键值对写入指定的节点。最后,关闭连接。 需要注意的是,以上代码只是一个示例,实际应用可能需要根据具体情况进行调整。 ### 回答2: 可以使用PythonRedis模块来连接Redis数据库,并使用连接池来管理连接。下面是一个使用连接池的方式实现将key-value写入Redis某个节点的函数writeinifile(section, key, value)的示例代码: ```python import redis from redis import ConnectionPool # 创建Redis连接池 pool = ConnectionPool(host='localhost', port=6379, db=0) def writeinifile(section, key, value): redis_conn = redis.Redis(connection_pool=pool) # 从连接池获取一个连接 # 将key-value写入Redis指定节点下的Hash map redis_conn.hset(section, key, value) # 释放连接回连接池 redis_conn.connection_pool.release(redis_conn) ``` 使用示例: ```python writeinifile('config', 'username', 'admin') # 将键值对写入config节点下 writeinifile('config', 'password', '123456') writeinifile('config', 'host', 'localhost') ``` 上述代码会将`username`、`password`和`host`三个键值对写入名为`config`的节点。 ### 回答3: 可以使用Pythonredis模块实现题目要求的函数writeinifile(section, key, value)。具体实现如下: 1. 首先,需要导入redis模块和连接池模块: ```python import redis from redis import ConnectionPool ``` 2. 创建一个连接池对象,用于连接redis数据库: ```python pool = ConnectionPool(host='localhost', port=6379, db=0) ``` 这里的host和port分别指定了连接的redis数据库的主机和端口,可以根据实际情况进行修改。 3. 定义writeinifile函数,用于将key-value写入指定的节点section下: ```python def writeinifile(section, key, value): r = redis.Redis(connection_pool=pool) r.hset(section, key, value) ``` 这里使用hset方法将key-value写入到指定的节点section下。 4. 最后可以通过调用writeinifile函数来实现将key-value写入redis的功能: ```python writeinifile('section1', 'key1', 'value1') ``` 这里将键值对key1-value1写入到节点section1下。 需要注意的是,写入redis的key-value时,redis的数据结构使用的是hash,所以将key-value写入到指定的节点使用的是hset方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值