缓存穿透,缓存击穿,缓存雪崩

一. 前言

NoSQL技术: redis就是NoSQL的一种, 它是一种基于内存的数据库. 为了解决从硬盘读取数据到内存再到数据交互的缓慢过程, 那么使用一种基于内存的数据库redis就是不可或缺的了的, 但是它同时也会引发三个至命的问题那就是: 缓存穿透, 缓存击穿, 缓存雪崩.

二. 缓存穿透

# 问题: 
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

# 解决:
1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击

解决缓存穿透伪代码示例:

from redis import Redis
from django.contrib.auth.models import User

redis_obj = Redis(decode_responses=True)


def cache_shorter(user_id, user_data=None):
    """缓存短点"""
    # 都没有查到. 那么就在缓存中设置短点有效时间
    redis_obj.set(user_id, user_data, ex=30)  # user_data=None


def interface_checking(user_id: str):
    """接口校验"""
    if user_id.isdigit():
        user_id = int(user_id)
        if user_id > 0:
            return user_id
    return False


def query_data(user_id):
    """查询数据"""
    # 解决方式一: 接口校验
    user_id = interface_checking(user_id)
    if not user_id:
        return 'error'

    # 查缓存
    user_data = redis_obj.get(user_id)
    if user_data:
        return user_data
    # 查数据库
    user_data = User.objects.filter(id=user_id).first()
    if user_data:
        redis_obj.set(user_id, user_data, ex=60 * 5)
        return user_data

    # 解决方式二: 缓存短点
    cache_shorter(user_id, user_data)
    return 'error'


res = query_data('缓存和数据库中都没有的数据')
print(res)

三. 缓存击穿

# 问题: 
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

# 解决:
1. 设置热点数据永远不过期。
2. 加互斥锁

解决缓存击穿伪代码示例:

from redis import Redis
from threading import Lock
from django.contrib.auth.models import User

redis_obj = Redis(decode_responses=True)
mutex = Lock()


def interface_checking(user_id: str):
    """接口校验"""
    if user_id.isdigit():
        user_id = int(user_id)
        if user_id > 0:
            return user_id
    return False


def cache_shorter(user_id, user_data=None):
    """缓存短点"""
    # 都没有查到. 那么就在缓存中设置短点有效时间
    redis_obj.set(user_id, user_data, ex=30)  # user_data=None


def query_data(user_id):
    """查询数据"""
    # 缓存穿透解决方式一: 接口校验
    user_id = interface_checking(user_id)
    if not user_id:
        return 'error'

    # 查缓存
    user_data = redis_obj.get(user_id)
    if user_data:
        return user_data

    # 缓存击穿解决方式一: 互斥锁
    with mutex:
        # 查数据库
        user_data = User.objects.filter(id=user_id).first()

    if user_data:
        # 缓存击穿解决方式二: 设置热点数据永远不过期
        if user_data == '热点数据':
            redis_obj.set(user_id, user_data)
        else:
            redis_obj.set(user_id, user_data, ex=60 * 5)
        return user_data

    # 缓存穿透解决方式二: 缓存短点
    cache_shorter(user_id, user_data)
    return 'error'

四. 缓存雪崩

缓存正常从Redis中获取,示意图如下:

这里写图片描述

缓存失效瞬间示意图如下:

这里写图片描述

# 问题: 
 缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库

# 解决:
1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3. 设置热点数据永远不过期。

加锁处理解决缓存雪崩:

import random
from redis import Redis
from threading import Lock
from django.contrib.auth.models import User

redis_obj = Redis(decode_responses=True)
mutex = Lock()


def interface_checking(user_id: str):
    """接口校验"""
    if user_id.isdigit():
        user_id = int(user_id)
        if user_id > 0:
            return user_id
    return False


def cache_shorter(user_id, user_data=None):
    """缓存短点"""
    # 都没有查到. 那么就在缓存中设置短点有效时间
    redis_obj.set(user_id, user_data, ex=30)  # user_data=None


def query_data(user_id):
    """查询数据"""
    # 缓存穿透解决方式一: 接口校验
    user_id = interface_checking(user_id)
    if not user_id:
        return 'error'

    # 查缓存
    user_data = redis_obj.get(user_id)
    if user_data:
        return user_data

    # 缓存击穿解决方式一: 互斥锁
    with mutex:
        # 查数据库
        user_data = User.objects.filter(id=user_id).first()

    if user_data:
        # 缓存击穿解决方式二: 设置热点数据永远不过期
        if user_data == '热点数据':
            redis_obj.set(user_id, user_data)
        else:
            # 缓存雪崩解决方式: 缓存数据的过期时间设置随机
            random_time = random.uniform(60, 60 * 3)
            redis_obj.set(user_id, user_data, ex=60 * 5 + random_time)
        return user_data

    # 缓存穿透解决方式二: 缓存短点
    cache_shorter(user_id, user_data)
    return 'error'

文章参考来源: https://www.cnblogs.com/sbj-dawn/p/11116673.html

图参考来源: https://blog.csdn.net/xlgen157387/article/details/79530877

posted @ 2020-07-26 23:29  给你加马桶唱疏通  阅读(116)  评论(0编辑  收藏  举报