缓存总结

本文探讨了在高并发场景下,如何通过缓存策略缓解数据库压力,防止缓存击穿、穿透和雪崩问题。提出了使用分布式锁、Redisson以及SpringCache框架的解决方案。在缓存一致性方面,介绍了失效模式和双写模式应对策略。
分析:
    百万并发下,如果所有的请求都落到db,那么db一定会承受不住,导致宕机。
    所以一定要加缓存。
    那么加了缓存就保证db不会因为大量并发而导致宕机嘛?缓存有什么问题?
        1)缓存击穿:
            大量并发请求一个不存在的数据。缓存一定不会命中,然后都落到db。
          解决:加null值。如果第一个请求在db返回null,那么也把他加到缓存中,这样后边的请求就会在缓存中命中null值返回。

        2)缓存穿透:
            大量并发请求一个数据,这个数据在缓存中刚好过期,那么所有的请求就会落到db。
          解决:加锁,第一个进来的请求拿到锁之后,查db,查完db把数据放入缓存中,这样后边的数据就会命中缓存。

        3)缓存雪崩:
            大量缓存中的数据同时失效。
          解决:对缓存中的数据设置一个过期时间。

1. 用“SET key value [EX seconds]  [NX|XX]1)开始我加的本地锁,锁的粒度只能锁住当前服务。
   即用synchronized锁住当前服务,这样其实也没有什么问题,就算服务有50个的话,最多也就有50个请求落到db,也可以接收。
  2)然后用分布式锁,锁的粒度是锁住当前所有服务。
   用redis的“SET key value [EX seconds]  [NX|XX]”这个命令:意思就是往redis中存一个值只有在不存的时候,才存。
   就用这个特性,来锁住所有服务。
   大量请求进来,被网关负载均衡到各个服务。
   请求进来如果缓存命中->则直接返回。
   缓存没命中:
        第一个请求拿到分布式锁->第一个请求查db->查完db放入缓存中->释放锁,这样后边的请求就能从缓存命中。
        其他请求自旋。

   这样就达到了分布式锁的目的,即大量请求进来,只查一遍db。
   但是这样写工作量还是很大的,因为每个放入缓存中的数据都要这么做。

   还要考虑:锁的过期时间的设置,原子删锁(执行lua脚本)
2.用redisson
   上边的不会有问题,就是工作量太大了,所以我们引入了redisson。

    redisson的底层已经为我们考虑周到了,比如原子删锁,锁的过期时间,设置。

    伪代码就变成
       1. 锁住
       2. 查db
       3. 释放锁
    就这三步也可以保证分布式锁。

==========缓存一致性===========
redisson成功的实现分布式锁,并且能解决缓存击穿,穿透,雪崩问题。
但是如果要是考虑缓存一致性的话,就有些麻烦了。
    失效模式(写数据库,删除缓存)和双写模式(写数据库,更新缓存)来解决缓存一致性。
    这样只要有写的时候,就会操作缓存,对缓存写有压力。话说回来,经常写的数据也不会放入缓存。
    但是工作量还是比较大的。
3.
所有引入了spingCache框架,

    读模式:
    1)缓存击穿: ache-null-values=true
    2)缓存雪崩:spring.cache.redis.time-to-live=3600000
    3)缓存击穿:   @Cacheable开启缓存的这个注解只能加本地锁,但是对数据库的影响并没有多大。
    4)缓存一致性问题解决:
    用@CacheEvict注解来实现失效模式
     @CacheEvict删除掉我们指定的所有缓存
    用@CachePut注解来实现双写模式
     @CachePut查完数据,如果数据是最新数据就把他再一次放入缓存

     写模式:
     这个框架并没有处理,所以这个框架适合读多写少,即时性,一致性要求不高的数据。







### 浏览器缓存原理与总结 #### HTTP 缓存机制概述 HTTP 缩短了客户端和服务器之间的通信延迟,通过在本地存储资源副本以减少网络请求次数和数据传输量。这种机制主要分为强缓存和协商缓存两种类型[^1]。 #### 强缓存缓存直接利用本地缓存中的资源,无需与服务器进行额外的交互。它通过 `Cache-Control` 和 `Expires` 两个头部字段来实现。 - `Cache-Control` 是 HTTP/1.1 中引入的字段,优先级高于 `Expires`。它使用 `max-age` 参数指定资源的有效时间(单位为秒)。例如,`Cache-Control: max-age=3600` 表示资源在接下来的一小时内有效[^4]。 - `Expires` 是 HTTP/1.0 中使用的字段,表示资源的绝对过期时间。例如,`Expires: Wed, 21 Oct 2025 07:28:00 GMT` 表示资源在指定的时间点前有效[^4]。 当浏览器发起请求时,如果强缓存命中,则直接返回本地资源,并附带状态码 `200 from cache`。 #### 协商缓存 协商缓存通过验证资源是否更新来决定是否使用本地缓存。它依赖于 `ETag` 和 `Last-Modified` 两个头部字段。 - `Last-Modified` 表示资源最后修改的时间戳。客户端会在后续请求中通过 `If-Modified-Since` 字段告知服务器上次获取资源的时间。如果服务器确认资源未被修改,则返回状态码 `304 Not Modified`,并允许客户端继续使用本地缓存[^1]。 - `ETag` 是一种更精确的标识符,通常是一个哈希值或唯一字符串。客户端通过 `If-None-Match` 字段将上次获取的 `ETag` 值发送给服务器。如果服务器确认 `ETag` 未发生变化,则同样返回 `304 Not Modified`[^1]。 #### 强缓存与协商缓存的关系 强缓存和协商缓存通常是协作工作的。浏览器首先检查强缓存是否命中,若未命中则进入协商缓存阶段。若协商缓存命中,则服务器返回 `304 Not Modified`;否则返回新的资源内容和状态码 `200 OK`[^3]。 #### 浏览器缓存的位置 资源缓存的位置可以分为以下三种,按优先级从高到低排列: 1. **Service Worker**:运行在独立线程中,能够控制缓存策略并提供离线支持。即使未命中 Service Worker 缓存,也会根据优先级调用 `fetch` 函数获取数据[^2]。 2. **Memory Cache**:存储在内存中,访问速度最快,但缓存持续性较短,会随着页面关闭而释放[^2]。 3. **Disk Cache**:存储在硬盘中,容量大且持久性强,适用于长期缓存资源。它根据 HTTP 头部字段判断资源的缓存策略。 #### 浏览器缓存的意义 浏览器缓存的主要目的是优化性能和减少带宽消耗。通过复用已下载的资源,可以显著降低页面载时间,同时减轻服务器负载[^3]。 ```python # 示例代码:模拟缓存机制的简单实现 class Cache: def __init__(self): self.storage = {} def set(self, key, value, ttl=None): self.storage[key] = {"value": value, "ttl": ttl} def get(self, key): if key in self.storage: item = self.storage[key] if item["ttl"] is None or item["ttl"] > 0: return item["value"] return None cache = Cache() cache.set("example", "cached data", ttl=3600) # 设置缓存,TTL为1小时 print(cache.get("example")) # 获取缓存数据 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值