redis学习3
1.redis的应用场景
(1).利用redis中字符串类型完成,项目中手机验证码存储的实现;
(2).利用redis中字符串类型完成,具有失效性业务功能 比如:淘宝 12306 订单:30分钟;
(3). 利用redis分布式集群系统中 session共享 memcache 内存型数据存储有上限,数据类型简单 可以用redis 解决数据上限和数据类型单一问题;
(4) 利用redis中的zset类型 比如: 排行榜 销量排行 ;
(5)利用redis分布式缓存 实现 ;
(6)利用redis存储认证之后token信息 微信小程序 微信公众号 ---->令牌(token)
(7)利用redis解决分布式集群系统中分布式锁问题
2.分布式缓存实现
本地缓存:存在应用服务器内存中数据为本地服务器 (local cache)
分布式缓存:存储在当前应用服务器内存之外数据称为分布式缓存(distribute cache) 比如:redis缓存
集群:将同一种服务的多个节点共同放在一起,共同对系统提供服务过程称之为集群。
分布式:有多个不同服务集群功能对系统提供服务这个系统称为分布式系统(distribute system)
1.缓存优化
缓存优化策略:对放入redis中key进行优化:key的长度不能太长
尽可能将key设计简洁一些。
算法:md5 处理 加密
特点:1)一切文件经过md5处理后,都会生成32位的16进制字符串
2)不同内容文件经过md5处理之后,加密结果一定不一致。 aa.txt bb.txt ====>MD5加密比较
3) 相同内容文件多次经过md5处理,生成结果始终一致。
推荐:可以将key通过md5优化处理。
2.什么是缓存穿透?
客户端查询了一个数据库中没有的数据记录,导致缓存在这情况下,无法利用。称为缓存穿透或者缓存击穿。
如何解决?
1.缓存空值:在查询一个不存在的值时,将这个值对应的缓存设置为None或空字符串,避免没有结果的查询每次都去数据库查询。 2.布隆过滤器:布隆过滤器是一种数据结构,用于判断一个元素是否在一个集合中,它可以快速地判断一个查询是否被缓存过,从而避免缓存穿透。 3.限制查询频率:可以限制同一IP或同一用户在一定时间内的查询次数,避免频繁查询对缓存和数据库造成压力。 4.缓存预热:在服务器启动时,将常用的数据预先加载至缓存中,避免用户在第一次查询时触发缓存穿透。 5.使用异步IO:采用异步IO方式进行缓存查询,当遇到并发查询时,可以快速返回缓存结果,避免阻塞等待。
(2.1)缓存空值
imort redis r = redis.Redis(host='127.0.0.1',port=6379,db=0,decode_responses=True)
def get_data_from_cache(key):
# 从缓存中取出数据
value = r.get(key)
if value is None:
# 如果缓存为空,设置空字符串并缓存一段时间
r.setex(key,"",100)
return ''
return value
(2.2)缓存空值
pip install bitarray mmh3
import math import bitarray import mmh3 class BloomFilter: def __init__(self, capacity=100000, error_rate=0.001): self.capacity = capacity self.error_rate = error_rate self.num_hashes = self._get_num_hashes(capacity, error_rate) self.num_bits = self._get_num_bits(capacity, error_rate) self.bit_array = bitarray.bitarray(self.num_bits) self.bit_array.setall(False) def _get_num_hashes(self, capacity, error_rate): k = - (capacity * math.log(error_rate)) / (math.log(2) ** 2) return int(round(k)) def _get_num_bits(self, capacity, error_rate): m = - (capacity * math.log(error_rate)) / (math.log(2) ** 2) return int(math.ceil(m / 8)) * 8 def add(self, key): for i in range(self.num_hashes): hash_value = mmh3.hash(key, i) % self.num_bits self.bit_array[hash_value] = True def __contains__(self, key): for i in range(self.num_hashes): hash_value = mmh3.hash(key, i) % self.num_bits if not self.bit_array[hash_value]: return False return True
在上面的示例代码中,BloomFilter类封装了布隆过滤器的相关逻辑,包括计算哈希函数的数量、计算位数组大小、添加元素和判断元素是否存在等功能。我们可以用它来判断一个元素是否存在于一个集合中,例如:
在Django中使用布隆过滤器来防止Redis缓存穿透:
import redis from bloom_filter import BloomFilter # 创建Redis连接 redis_conn = redis.Redis(host='localhost', port=6379) # 创建布隆过滤器对象 bf = BloomFilter(capacity=100000, error_rate=0.001) def get_data_from_cache(key): # 判断Key是否存在于布隆过滤器中 if key not in bf: return None # 从Redis缓存中获取数据 value = redis_conn.get(key) # 如果Redis缓存不存在该数据,则返回None if value is None: return None return value.decode() def add_data_to_cache(key, value): # 添加Key到布隆过滤器 bf.add(key) # 添加数据到Redis缓存中 redis_conn.set(key, value)
上述代码中,我们在从Redis缓存中获取数据之前,先判断Key是否存在于布隆过滤器中,如果Key不存在,则说明该Key对应的数据不存在于Redis缓存中,直接返回None。如果Key存在,正常从Redis缓存中获取数据。
在添加数据到Redis缓存中时,先将Key添加到布隆过滤器中,然后再将数据缓存到Redis中。
这样通过布隆过滤器来防止Redis缓存穿透,可以在保证高效查询数据的同时,避免缓存穿透攻击。
(2.3)限流
可以使用drf框架中的限流方式,进行对用户区访问数据库进行限流
(2.4)缓存预热
在Python中,可以通过缓存预热来防止Redis缓存穿透。缓存预热是在应用启动时,将缓存中的热点数据提前加载到缓存里面,以提高应用的响应速度和稳定性。缓存预热可以避免应用启动后缓存为空,从而避免了缓存穿透攻击的发生。
定义一个函数,用于从数据库中查询热点数据:
from myapp.models import Item def get_hot_items(): return Item.objects.filter(hot=True)
在应用启动时,调用上面的函数,获取热点数据,并将其存储到Redis缓存中:
import redis def cache_hot_items(): redis_conn = redis.Redis(host='localhost', port=6379, db=0) hot_items = get_hot_items() for item in hot_items: redis_conn.set(item.id, item.serialize())
上述代码中,cache_hot_items()函数将从数据库中获取的热点数据写入到Redis缓存中,并使用item.id作为缓存的键,item.serialize()方法的返回值作为缓存的值。
在应用的启动脚本中,在应用启动之前,调用cache_hot_items()函数进行缓存预热:
#!/usr/bin/env python
# flask示例 import os import sys from myapp import app if __name__ == '__main__': # 缓存预热 cache_hot_items() app.run(host='0.0.0.0', port=5000)
上述代码中,在应用启动之前,先调用cache_hot_items()函数进行缓存预热,然后启动应用。
这样,如果有请求查询热点数据,Redis缓存中已经存在相应的缓存,就可以快速响应请求,从而避免了缓存穿透攻击的发生。
(2.5)异步IO
在Python中,我们可以使用异步IO来防止Redis中的缓存穿透。异步IO能够实现高并发处理,提高系统性能,避免了I/O阻塞,从而避免了缓存穿透。
以下是使用异步IO来防止Redis缓存穿透的实现代码:
import asyncio import aioredis async def get_cache(key): redis = await aioredis.create_redis_pool(('localhost', 6379), maxsize=10) value = await redis.get(key) redis.close() await redis.wait_closed() if value is None: return None async def cache_data(key, value): redis = await aioredis.create_redis_pool(('localhost', 6379), maxsize=10) await redis.set(key, value) redis.close() await redis.wait_closed() async def handle_request(request): key = request.args.get('key') if not key: return Response(status=400) value = await get_cache(key) if not value: value = await get_data_from_database(key) if not value: await cache_data(key, "") return Response(status=404) await cache_data(key, value) return Response(value)
上述代码中,我们使用了Python的异步IO库asyncio。我们通过定义两个异步函数get_cache和cache_data来处理Redis的缓存读和写操作,而handle_request函数则用于处理HTTP请求。当有请求到达的时候,会先尝试从Redis中读取缓存数据,如果读取到缓存数据,则直接返回,否则我们会从数据库中查询对应的数据,并将查询结果写入到Redis中。如果在数据库中也不存在对应的数据,则我们会将其作为Null值写入到Redis中。
通过使用异步IO来实现Redis缓存穿透功能可以提高系统的性能,高并发下能够保证系统的稳定性。
3.什么是缓存雪崩?
定义:系统运行时的某一个时刻,突然系统中的缓存全部失效,恰好在这个时期涌来大量客户端请求,导致所有模块缓存无法利用,大量请求涌向数据库导致的极端情况,数据库阻塞或者挂起。
缓存存储:业务系统非常大,模块多 业务数据不同,不同模块在放入缓存时,都会设置一个缓存超时时间。
如何解决? 1.永久存储(不推荐); 2. 针对不同的业务数据,设置不同的超时时间 (推荐)
浙公网安备 33010602011771号