【NoSQL数据库】Redis知识小册

 一、缓存穿透

         缓存穿透是先查Redis,发现缓存中没有数据,再查数据库。然而,如果查询的数据在数据库中也不存在,那么每次查询都会绕过缓存,直接落到数据库上。

解决方案一、缓存空数据

  • 查询Redis缓存:首先查询Redis缓存,看看请求的数据是否已经缓存。
  • 缓存未命中:如果Redis缓存中没有该数据,则查询数据库。
  • 数据库查询结果为空:如果数据库中也没有该数据(即数据不存在),则将这个空结果缓存起来,并设置一个较短的过期时间。
  • 返回结果:下一次查询相同的数据时,直接从缓存中返回空结果。

布隆过滤器是一种高效的概率数据结构,用于测试一个元素是否在一个集合中。它能够快速判断某个元素是否可能存在于集合中,但不能准确地确定元素一定存在。布隆过滤器通过牺牲一定的准确性来大幅度节省空间。

布隆过滤器的原理

  1. 哈希函数:布隆过滤器使用多个哈希函数将输入元素映射到一个位数组中的多个位置。
  2. 位数组:布隆过滤器维护一个固定大小的位数组,初始时所有位都为0。
  3. 添加元素:将一个元素添加到布隆过滤器时,通过多个哈希函数计算出多个哈希值,并将位数组中对应位置的值设为1。
  4. 查询元素:查询一个元素是否在布隆过滤器中时,使用相同的哈希函数计算出多个哈希值,并检查位数组中对应位置的值是否全部为1。如果全部为1,则说明该元素可能在集合中;如果有任何一个位置的值为0,则说明该元素肯定不在集合中。

使用布隆过滤器解决缓存穿透

布隆过滤器可以用于防止缓存穿透,因为它能够高效地判断某个查询是否对应数据库中不存在的数据。如果布隆过滤器判断某个元素肯定不存在,就可以直接返回空结果,而不必查询数据库和缓存。

二、缓存击穿

        给一个key设置过期时间,当缓存中的热点数据(即频繁被访问的数据)过期时,意味着缓存系统中存储的这部分数据不再有效,并且会被移除或者需要重新加载。此时,如果有大量请求同时访问这个数据,而缓存中没有有效的数据存在,那么这些请求将直接转发到后端数据库进行查询。由于这些数据是热点数据,访问频繁,所以瞬间的大量请求可能会给后端数据库带来很大的压力,甚至导致数据库负载过高,性能下降,甚至崩溃。这种情况被称为缓存击穿。

        解决方案一:互斥锁

背景问题

想象你是一家餐馆的老板,平时店里有一个公告板,上面写着每日特价菜的信息(类似于缓存)。大家都可以直接看公告板获取信息,而不需要每个人都来问你(类似于访问数据库)。

但有时候,公告板上的信息过期了(缓存失效),需要更新。这时,很多客人都同时来问你今天的特价菜是什么(大量并发请求访问数据库)。如果所有人都来问你,你就会忙不过来(数据库压力骤增)。

互斥锁解决方案

为了防止这种情况,你决定用一个办法来解决这个问题——你安排一个锁(锁机制)。

具体过程

  1. 检查公告板:每个客人来餐馆时,首先会看看公告板(检查缓存)。
  2. 尝试获取锁:如果公告板上的信息过期了(缓存失效),第一个发现的客人会去找你更新信息。但你设立了一条规则:每次只能有一个客人来问你特价菜是什么(获取锁)。
  3. 锁获取成功:如果某个客人第一个来问你特价菜(成功获取锁),你会告诉他特价菜是什么(查询数据库),并让他把新的信息写到公告板上(更新缓存)。然后你会解除限制(释放锁),让其他客人可以直接看公告板。
  4. 锁获取失败:如果另外的客人也发现公告板的信息过期了,但发现已经有一个客人去问你了(获取锁失败),他们不会再来打扰你,而是等一会儿再看公告板,看看新信息是否已经更新(等待并重试)。

import time
import redis

# 初始化 Redis 连接,类似于你有一个能记录锁状态的工具
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

def get_special_dish_with_lock(key, query_database, lock_timeout=10, cache_timeout=60):
    # 先看看公告板上有没有特价菜的信息
    data = redis_client.get(key)
    
    if data is not None:
        return data
    
    # 如果公告板上没有信息,第一个客人会去找你问特价菜,并设立一个锁
    lock_key = f"lock:{key}"
    if redis_client.setnx(lock_key, 1):
        redis_client.expire(lock_key, lock_timeout)  # 设置锁的过期时间,防止有人占着不放
    
        try:
            # 再次检查公告板,因为可能有其他人已经更新了
            data = redis_client.get(key)
            if data is None:
                # 如果公告板上还是没有信息,你告诉第一个客人特价菜是什么
                data = query_database()
                
                # 第一个客人把新信息写到公告板上
                redis_client.set(key, data, ex=cache_timeout)
        finally:
            # 解除锁,其他客人可以直接看公告板了
            redis_client.delete(lock_key)
    else:
        # 如果有其他客人已经去问你了,其他客人等一会儿再看公告板
        while not data:
            time.sleep(0.1)
            data = redis_client.get(key)
    
    return data

# 模拟数据库查询函数
def query_database():
    return "Today's Special Dish"

# 使用互斥锁获取特价菜信息
key = "special_dish"
special_dish = get_special_dish_with_lock(key, query_database)
print(special_dish)
 

更新缓存的过程

  1. 缓存失效检测:每当有请求到来时,首先检查缓存中是否有需要的数据以及数据是否有效(未过期)。
  2. 缓存失效:如果缓存中没有数据或者数据已过期,就需要从数据库中获取最新的数据。
  3. 查询数据库:从数据库中查询最新的数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值