redis系列之--缓存方案(python)

本文详细介绍了Redis作为缓存的方案,包括缓存介绍、一级缓存、缓存粒度、缓存格式、缓存机制(过期策略、淘汰策略、模式)以及缓存问题(更新、穿透、雪崩)的解决方案。通过Python实现了缓存类设计、有效期管理、异常处理等关键功能,强调了在实际项目中缓存的优化策略和注意事项。
摘要由CSDN通过智能技术生成

缓存方案


一缓存介绍

  • 作用

    减小数据库的访问压力, 提高并发能力

  • 多级缓存
    在这里插入图片描述

二.本地缓存/一级缓存

  • flask-cache

  • 项目中的应用

    • 一般不设置一级缓存
      • 内存缓存会影响web应用的运行效率
      • 对响应做缓存的性价比低
from flask import Flask
from flask_cache import Cache
import random

app = Flask(__name__)
# 设置配置
app.config['CACHE_TYPE'] = 'simple'  # 使用本地python字典进行存储, 一级缓存
app.config['CACHE_DEFAULT_TIMEOUT'] = 5 * 60  # 默认过期时间 5分钟

# 创建缓存对象
cache = Cache(app)


@app.route('/')
@cache.cached(timeout=60)  # 对无参数的路由响应进行缓存
def index():
    num = random.randint(0, 9)
    print(num)
    return str(num)

@app.route('/demo1/<user_id>')  # demo1/11   demo1/12 的缓存结果会做区分
@cache.memoize(timeout=30)
def demo1(user_id):
    num = random.randint(0, 9)
    print(num)
    return str(num)


# 对自定义函数设置缓存
@cache.cached(timeout=20)
def func1():
    num = random.randint(0, 9)
    print(num)
    return str(num)


@app.route('/demo2')
def demo2():
    print('demo2')
    # return func1()
    # 可以对指定的数据进行缓存
    cache.set('name', 'zs', timeout=30)
    return 'demo2'

@app.route('/demo3')
def demo3():
    # 取出缓存
    name = cache.get('name')
    print(name)
    return 'demo3'


if __name__ == '__main__':
    app.run(debug=True)

三.缓存粒度


  • 缓存某个数值
    • 一个键只保存一个值, 性价比较低, 使用率低
    • 场景 验证码

  • 缓存数据对象
    • 数据库记录对应的具体数据
    • 优点 可以多次复用
    • 场景 用户/文章数据

  • 缓存数据集合

    • 数据库查询对应的结果集

    • 场景 文章/关注列表

    • 可以和数据对象配合使用, 方便数据对象的重用


  • 缓存视图响应
    • 视图返回的响应数据
    • 缺点 复用性比较差
  • 项目中主要对 数据集合+数据对象 进行缓存, 优点 复用性强, 节省内存

四.缓存格式

  • 数值
    • 类型 string

  • 数据对象
    • hash
      • 不需要格式转换
      • 可以单独更新某一个字段
      • 占用的空间比较多
    • 结构化string (json字符串)
      • 需要进行转换 使用pickle模块提高性能
      • 无法单独更新某个字段
      • 占用的空间小
    • 头条项目中优先使用结构化string

  • 数据集合
    • article:top:30 / md5("select * from t_arcitle order by(create_time) desc limit 30;")
    • 值 list/zset/set/hash/结构化string
    • list
      • 有遍历的需要
      • 增加效率 追加数据速度很快 插入速度和插入的位置有关
      • 查询效率 和查询的区间有关
      • 如果添加顺序和不满足排序要求, 则无法排序
    • zset
      • 有排序的需要
      • 增加效率 和存储的数据量负相关, 数据量越大, 添加时间越长
      • 查询效率 和存储的数据量负相关, 并且和查询的结果集数据量有关
    • set
      • 有判断是否存在的需要
    • hash
      • 只能根据键取值, 无法遍历
      • 空间占用多
    • json字符串
      • 节省空间
    • 头条项目中主要使用了json字符串zset的形式

  • 视图响应
    • string
    • 键 请求URL
    • 值 响应结果对应的字符串 前端渲染json字符串/后端渲染html字符串

五.缓存机制


1.过期策略
  • 作用

    • 节省空间
    • 实现数据的弱一致性
  • 过期策略

    • 定时过期
      • 效率太低, 每个数据都需要设置定时器进行计数
    • 惰性过期
    • 查询时, 才去检查数据的有效期, 如果过期, 则返回nil, 并删除过期数据
      • 定期过期
        • 每隔100ms, 随机取出一部分数据进行过期校验, 如果过期, 删除数据
  • redis的选择

    • 惰性过期+定期过期(每100ms对设置了过期时间的数据随机查询并删除过期数据)

2.缓存淘汰
  • LRU

    • least recently use 优先淘汰不是最近使用的数据
  • LFU

    • least frequently use 优先淘汰不是频繁使用的数据
    • 采用了定期衰减的机制, 防止旧数据始终无法删除
  • 缺点
    - 需要每条数据维护一个使用计数
    - 还需要定期衰减

    • 面试问题
      • mysql中有100万数据, 要求redis中保留20万热点数据
      • 设置内存最大存储空间, 并且设置缓存策略为LFU
  • 淘汰配置

maxmemory 最大使用内存数量  如服务器内存10G, 最多给redis分配9G
maxmemory-policy volatile-lru 淘汰策略

3. 缓存的模式
  • 读缓存

    • 先读取缓存中的数据, 没有才会读取数据库中的数据

    • 解决数据库读取压力

    • 方式

      • cache aside 具体读写操作交给应用完成
      • read through 具体读写操作交给缓存层完成, 即使后期修改存储方案, 业务代码不需要修改, 有利于项目的重构和架构升级
  • 写缓存

    • 先写入缓存, 再写入数据库
    • 解决数据库的写入压力
    • 方式
      • write through 具体写操作交给缓存层完成, 立即更新数据库
      • write behind caching 具体读操作交给缓存层完成, 定时异步更新数据库

六.缓存问题

1 缓存更新
  • 问题
    • mysql和redis是两个独立的系统, 在并发环境下, 无法保证更新的一致性
  • 解决办法
    • 更新数据时, 先写入mysql, 再删除缓存 facebook
    • 设计分布式锁/使用消息队列串行处理
2 缓存穿透
  • 问题
    • 黑客会主动访问数据库不存在的数据, 缓存会被穿透, 直接访问数据库, 导致数据库的的访问压力变大
  • 解决办法
    • 对于数据库中不存在的数据, 也对其在缓存中设置默认值 一般过期时间会比较短
    • 可以设置一些过滤规则, 如布隆过滤器(算法, 用于判断数据是否包含在集合中), 将所有可能的值录入过滤器, 如果不包含直接返回None, 有误杀概率
3 缓存雪崩
  • 问题

    • 如果大量缓存数据都在同一个时间过期, 那么很可能出现缓存集体失效, 会导致所有的请求都直接访问数据库, 导致数据库压力过大
  • 解决办法

    • 设置过期时间时, 添加随机值, 让过期时间进行一定程度分散
    • 多级缓存的方式来处理
    • 利用锁/队列的形式

七.缓存层实现

1 缓存类设计
class UserProfileCache(object):
    """用户基本信息缓存类   每个缓存对象 对应 一条用户缓存数据"""
    def __init__(self, user_id):
        self.user_id = user_id   # 用户id
        self.key = 'user:{}:profile'.format(self.user_id)  # redis键

    def get(self):
        """获取缓存数据"""
        pass

    def clear(self):
        """清空缓存"""
        pass
2 获取缓存
class UserProfileCache(object):
    """用户基本信息缓存类   每个缓存对象 对应 一条用户缓存数据

    user:<用户id>:profile    string  '{"name": "zs", "age": 20}'
    """
    def __init__
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值