1, I can think of things about the cache

table of Contents

What is the cache?

Why use a cache?

We used caching algorithms, what does? LRU handwriting at the implementation of the code?

Common Common caching tools and frameworks What?

After using the cache, which frequently asked questions?

When the query cache error, how to improve the usability?

If you avoid the problem of cache "penetration" of?

How to avoid problems cache "avalanche" of?

If you avoid the problem of cache "breakdown" of?

How to ensure the consistency of cache and DB?

What is cache warming? How to achieve cache warming?

Out of policy cache data What?

How to store POJO Cache objects?

666. eggs


Web-based sorting, and their editors; with the record about that thing I can think of cache? If in doubt welcome the faithful to ask questions fat explore.

What is the cache?

This problem can be understood, the cache is the data exchange buffer for different (ie different hardware) can build the service object cache. Object, the slow read and write data stored in the medium read and write speed faster medium, thereby improving the access speed , reducing the time consumed. E.g:

  • cpu cache: read and write speed of the cache memory is much higher than

        When the CPU reads the data, if the data needed is found in the cache, you do not need to read memory

        When the CPU write data is written to the first cache memory is written to return.

  • Disk cache: In fact, put the disk cache frequently used disk data in memory, the memory read and write speed is much higher than the disk.

        Read data, read from memory.

        Write data can be written to memory first, timed or quantitative written back to disk, or synchronous write back.

Why use a cache?

Just as in  "What is the cache?"  Question can see, using the cache purpose is to improve read and write performance. The actual business scenarios, more is to improve the reading performance , resulting in better performance, higher concurrency.

Ordinary course of business, we use more database is MySQL, the cache is Redis. Take a look, Ali cloud provides the performance specifications:

Such a comparison, Redis much better than read and write performance of MySQL. So, we will MySQL hot data in the cache to Redis, improve read performance, but also of MySQL reduced pressure reading. for example:

  • Forum posts frequently accessed and updated in real time to the amount of reading, using Redis Messages amount of reading, you can enhance performance and concurrency.
  • Product information, data update frequency is not high, but the high frequency read, especially hot commodity.

We used caching algorithms, what does? LRU handwriting at the implementation of the code?

? Caching algorithms

( Briefly explain cache miss:

1. If there is a cache of space, then, did not hit the object will be stored in the cache to.

2. If the cache is full, but did not hit the cache, it will follow a certain kind of strategy, the cache of old objects kicked out, and the new object is added to the buffer pool. These strategies collectively referred to as alternative strategies (caching algorithm), these strategies will in the end determine which objects should be kicked out. )

Caching algorithms, more common are three types:

  • LRU (least recently used, least recently used)
  • LFU (Least Frequently used, least frequently used)
  • FIFO (first in first out, FIFO)
Tactics description
volatile-lru Pick and choose out of the least recently used data from the data set time expired
volatile-ttl From the set expiration time of the selection of the data set to expire data out of
volatile-random Out of the expiration time has been set to select data from any data set
allkeys-lru Centralized data out of the selection of the least recently used data from all
allkeys-random Be arbitrarily selected out of all the data from the data set
non-envicition Prohibits the expulsion data

Complete words, Pang You can look at the  "caching, caching algorithms and profile caching framework"  of  "caching algorithms"  section.

Handwriting LRU code

LRU handwritten code, there are a variety of ways. Wherein, the simplest is implemented based on a LinkedHashMap, code is as follows:

package guojing.test;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * LRU(Latest recently used最近最少使用策略):
 * 如果缓存满了,而又没有命中缓存,那么就会按照某一种策略,把缓存中的旧对象踢出,而把新的对象加入缓存池。
 * lru是指,我把最近最少使用的缓存对象给踢走
 *
 * @author: guojing
 * @date: 2019-04-01
 * @time: 下午11:45
 */
public class LRUCache<K, V> extends LinkedHashMap<K, V> {

    private final int CACHE_SIZE;

    /**
     * 传递进来最多能缓存多少数据
     *
     * @param cacheSize 缓存大小
     */
    public LRUCache(int cacheSize) {
        // true 表示让 LinkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。
        super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
        CACHE_SIZE = cacheSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        // 当 map 中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
        return size() > CACHE_SIZE;
    }
}

Other more sophisticated, better reflect the LRU implementation of individual coding capability, you can take a look at the following two articles:

Common Common caching tools and frameworks What?

In the back-end Java development, common caching tools and frameworks are listed below:

  • Local cache: Guava LocalCache, Ehcache, Caffeine.
    • Ehcache of more feature-rich, Caffeine performance than Guava LocalCache good.
  • Distributed cache: Redis, MemCache, Tair.
    • Redis most mainstream and popular.

After using the cache, which frequently asked questions?

  • When the cache is written ? How to write and to avoid repetition of concurrent write?
  • How cache failure ?
  • And DB cache consistency of how to ensure that?
  • How to avoid cache penetrate the problem?
  • How to avoid caching breakdown problem?
  • If the cache to avoid avalanche problems?

Focus on bold, below, we each question, and gradually resolved.

When the query cache error, how to improve the usability?

Caching can greatly improve query performance, but the loss of cached data and caching is not available does not affect the normal operation of the application.

Therefore, under normal circumstances, if the cache is abnormal, you need to manually catch the exception, and logging, and returned to the user database query data from, and should not cause service unavailable.

Of course, doing so may cause problems cache avalanches. Specifically how to solve, you can see article " How to avoid the problem of cache" avalanche "of?"  Question.

If you avoid the problem of cache "penetration" of?

Cache penetration

Cache penetration, refers to a certain query data does not exist, due to the passive-write cache is not hit (  passive wrote, referring to data from DB queries, updates to the cache  ), and in consideration of fault tolerance, if from DB finding data is not written to the cache, which will result in a time that does not exist in the data request should go to the DB query, lost the meaning of the cache. When large flow, might DB hung up, if someone does not take advantage of the presence of frequent attacks our key applications, this is the loophole. As shown below:hacker attack

  • In  "Why use the cache?"  , We have already seen, MySQL performance is far better than Redis, if a large number of requests directly hit MySQL, will direct and hanging MySQL.

How to solve

There are two options to solve:

  • Option One, empty object cache: When the query data from DB is empty, we still have this empty cache results, specific values ​​need to use a special logo, can be separated and real cached data area. In addition, you need to set short expiration time, it is generally recommended not to exceed 5 minutes.
  • Scheme II, BloomFilter Bloom filter: caching service on the basis of, BloomFilter construct data structures, whether there is a corresponding KEY BloomFilter, if present, corresponding to the description of the KEY value is null. Then the entire logic is as follows:
    • 1, according KEY query cache. If a corresponding value exists, the direct return; if not, continue down.
    • 2. KEY value in the cache BloomFilter query. If the value exists, the corresponding description of the KEY value does not exist, direct return null; if the value does not exist, it goes on.
    • 3, a value corresponding to DB query, if present, to update the cache, and returns the value. If there is no value, to update the cache BloomFilter and returns empty.

Select

These two programs, each with its advantages and disadvantages.

  Null object cache BloomFilter Bloom filter
Applicable scene 1, the data do not hit high 
2 to ensure consistency
1, the data is not hit high 
2, relatively fixed data, real-time low
Maintenance costs 1, code maintenance simple 
2, require excessive buffer space 
3, data inconsistency
1, code maintenance complex 
2 cache occupies a small space
  • For BloomFilter Bloom filter, use scenes in the "relatively fixed data, real-time low", TODO does not support removing?

Practice, use of the program two more. Because, compared to a program, the more savings contents of the cache load is smaller. And, Redis cache frequently used data structure that supports building BloomFilter, concrete can see the following articles:

另外,推荐看下 《Redis架构之防雪崩设计:网站不宕机背后的兵法》 文章的 「一、缓存穿透预防及优化」 ,大神解释的更好,且提供相应的图和伪代码。

如何避免缓存”雪崩”的问题?

缓存雪崩

缓存雪崩,是指缓存由于某些原因无法提供服务( 例如,缓存挂掉了 ),所有请求全部达到 DB 中,导致 DB 负荷大增,最终挂掉的情况。

如何解决

预防和解决缓存雪崩的问题,可以从以下多个方面进行共同着手。

1)缓存高可用

通过搭建缓存的高可用,避免缓存挂掉导致无法提供服务的情况,从而降低出现缓存雪崩的情况。

假设我们使用 Redis 作为缓存,则可以使用 Redis Sentinel 或 Redis Cluster 实现高可用。

2)本地缓存

如果使用本地缓存时,即使分布式缓存挂了,也可以将 DB 查询到的结果缓存到本地,避免后续请求全部到达 DB 中。

当然,引入本地缓存也会有相应的问题,例如说:

  • 本地缓存的实时性怎么保证?

    • 方案一,可以引入消息队列。在数据更新时,发布数据更新的消息;而进程中有相应的消费者消费该消息,从而更新本地缓存。

      也可以使用 Redis Pub / Sub 取代消息队列来实现,但是 T T 此时 Redis 可能已经挂了,所以也不一定合适。

    • 方案二,设置较短的过期时间,请求时从 DB 重新拉取。

    • 方案三,使用 「如果避免缓存”击穿”的问题?」 问题的【方案二】,手动过期。
  • 每个进程可能会本地缓存相同的数据,导致数据浪费?
    • 方案一,需要配置本地缓存的过期策略和缓存数量上限。

上述的几个方案写的有点笼统,可以交流。。。

如果我们使用 JVM ,则可以使用 Ehcache、Guava Cache 实现本地缓存的功能。

3)请求 DB 限流

通过限制 DB 的每秒请求数,避免把 DB 也打挂了。这样至少能有两个好处:

  1. 可能有一部分用户,还可以使用,系统还没死透。
  2. 未来缓存服务恢复后,系统立即就已经恢复,无需在处理 DB 也挂掉的情况。

当然,被限流的请求,我们最好也要有相应的处理,走4)服务降级 。

如果我们使用 Java ,则可以使用 Guava RateLimiter、Sentinel 实现限流的功能。

4)服务降级

如果请求被限流,或者请求 DB 超时,我们可以服务降级,提供一些默认的值,或者友情提示,甚至空白的值也行。

如果我们使用 Java ,则可以使用 Hystrix、Sentinel 实现限流的功能。

5)提前演练

在项目上线前,演练缓存宕掉后,应用以及后端的负载情况以及可能出现的问题,在此基础上做一些预案设定。


另外,推荐看下 《Redis架构之防雪崩设计:网站不宕机背后的兵法》 文章的 「二、缓存雪崩问题优化」 ,大神解释的更好,且提供相应的图和伪代码。

如果避免缓存”击穿”的问题?

? 缓存击穿

缓存击穿,是指某个极度“热点”数据在某个时间点过期时,恰好在这个时间点对这个 KEY 有大量的并发请求过来,这些请求发现缓存过期一般都会从 DB 加载数据并回设到缓存,但是这个时候大并发的请求可能会瞬间 DB 压垮。

  • 对于一些设置了过期时间的 KEY ,如果这些 KEY 可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑这个个问题。
  • 缓存被“击穿”的问题,和缓存“雪崩“”的区别在于,前者针对某一 KEY 缓存,后者则是很多 KEY 。
  • 缓存被“击穿”的问题,和缓存“穿透“”的区别在于,这个 KEY 是真实存在对应的值的。

如何解决

有两种方案可以解决:

  • 方案一,使用互斥锁:请求发现缓存不存在后,去查询 DB 前,使用分布式锁,保证有且只有一个线程去查询 DB ,并更新到缓存。流程如下:
    • 1、获取分布式锁,直到成功或超时。如果超时,则抛出异常,返回。如果成功,继续向下执行。
    • 2、再去缓存中。如果存在值,则直接返回;如果不存在,则继续往下执行。? 因为,获得到锁,可能已经被“那个”线程去查询过 DB ,并更新到缓存中了。
    • 3、查询 DB ,并更新到缓存中,返回值。
  • 方案二,手动过期:缓存上从不设置过期时间,功能上将过期时间存在 KEY 对应的 VALUE 里,如果发现要过期,通过一个后台的异步线程进行缓存的构建,也就是“手动”过期。通过后台的异步线程,保证有且只有一个线程去查询 DB。

选择

这两个方案,各有其优缺点。

  使用互斥锁 手动过期
优点 1、思路简单 
2、保证一致性
1、性价最佳,用户无需等待
缺点 1、代码复杂度增大 
2、存在死锁的风险
1、无法保证缓存一致性

具体使用哪一种方案,胖友可以根据自己的业务场景去做选择。

另外,推荐看下 《Redis 架构之防雪崩设计:网站不宕机背后的兵法》 文章的 「三、缓存热点 key 重建优化」 ,大神解释的更好,且提供相应的图和伪代码。

缓存和 DB 的一致性如何保证?

产生原因

主要有两种情况,会导致缓存和 DB 的一致性问题:

  1. 并发的场景下,导致读取老的 DB 数据,更新到缓存中。
  2. 缓存和 DB 的操作,不在一个事务中,可能只有一个操作成功,而另一个操作失败,导致不一致。

当然,有一点我们要注意,缓存和 DB 的一致性,我们指的更多的是最终一致性。我们使用缓存主要是提高读操作的性能,真正在写操作的业务逻辑,还是以数据库为准。例如说,我们可能缓存用户钱包的余额在缓存中,在前端查询钱包余额时,读取缓存,在使用钱包余额时,读取数据库。

解决方案

在开始说解决方案之前,胖友先看看如下几篇文章,可能有一丢丢多,保持耐心。

下面,我们就来看看几种方案。当然无论哪种方案,比较重要的就是解决两个问题:

  • 1、将缓存可能存在的并行写,实现串行写。
  • 2、实现数据的最终一致性。

1)先淘汰缓存,再写数据库

因为先淘汰缓存,所以数据的最终一致性是可以得到有效的保证的。为什么呢?先淘汰缓存,即使写数据库发生异常,也就是下次缓存读取时,多读取一次数据库。

但是,这种方案会存在缓存和 DB 的数据会不一致的情况,《缓存与数据库一致性优化》 已经说了。

那么,我们需要解决缓存并行写,实现串行写。比较简单的方式,引入分布式锁。

  • 在写请求时,先淘汰缓存之前,获取该分布式锁。
  • 在读请求时,发现缓存不存在时,先获取分布式锁。

这样,缓存的并行写就成功的变成串行写落。实际上,就是 「如果避免缓存”击穿”的问题?」 的【方案一】互斥锁的加强版。

整体执行,如下草图:

èå¾

 

  • 写请求时,是否主动更新缓存,根据自己业务的需要,是否有,都没问题。

2)先写数据库,再更新缓存

按照“先写数据库,再更新缓存”,我们要保证 DB 和缓存的操作,能够在“同一个事务”中,从而实现最终一致性。

基于定时任务来实现

  • 首先,写入数据库。
  • 然后,在写入数据库所在的事务中,插入一条记录到任务表。该记录会存储需要更新的缓存 KEY 和 VALUE 。
  • 【异步】最后,定时任务每秒扫描任务表,更新到缓存中,之后删除该记录。

基于消息队列来实现

  • 首先,写入数据库。
  • 然后,发送带有缓存 KEY 和 VALUE 的事务消息。此时,需要有支持事务消息特性的消息队列,或者我们自己封装消息队列,支持事务消息。
  • 【异步】最后,消费者消费该消息,更新到缓存中。

这两种方式,可以进一步优化,可以先尝试更新缓存,如果失败,则插入任务表,或者事务消息。

另外,极端情况下,如果并发写执行时,先更新成功 DB 的,结果后更新缓存,如下图所示:

èå¾

  • 理论来说,希望的更新缓存顺序是,线程 1 快于线程 2 ,但是实际线程1 晚于线程 2 ,导致数据不一致。
  • 可能胖友会说,图中不是基于定时任务或消息队列来实现异步更新缓存啊?答案是一直的,如果网络抖动,导致【插入任务表,或者事务消息】的顺序不一致。
  • 那么怎么解决呢?需要做如下三件事情:
    • 1、在缓存值中,拼接上数据版本号或者时间戳。例如说:value = {value: 原值, version: xxx} 。
    • 2、在任务表的记录,或者事务消息中,增加上数据版本号或者时间戳的字段。
    • 3、在定时任务或消息队列执行更新缓存时,先读取缓存,对比版本号或时间戳,大于才进行更新。? 当然,此处也会有并发问题,所以还是得引入分布式锁或 CAS 操作。

3)基于数据库的 binlog 日志

如下内容,引用自 《技术专题讨论第五期:论系统架构设计中缓存的重要性》 文章,超哥对这个问题的回答。

binlog program

  • 应用直接写数据到数据库中。
  • 数据库更新binlog日志。
  • 利用Canal中间件读取binlog日志。
  • Canal借助于限流组件按频率将数据发到MQ中。
  • 应用监控MQ通道,将MQ的数据更新到Redis缓存中。

可以看到这种方案对研发人员来说比较轻量,不用关心缓存层面,而且这个方案虽然比较重,但是却容易形成统一的解决方案。


当然,以上种种方案,各有其复杂性,如果胖友心里没底,还是仅仅使用如下任一方案:

  • 先淘汰缓存,再写数据库”的方案,并且无需引入分布式锁。
  • 先写数据库,再更新缓存”的方案,并且无需引入定时任务或者消息队列。

原因如下:

FROM 基友老梁的总结

使用缓存过程中,经常会遇到缓存数据的不一致性和脏读现象。一般情况下,采取缓存双淘汰机制,在更新数据库的淘汰缓存。此外,设定超时时间,例如三十分钟。

极端场景下,即使有脏数据进入缓存,这个脏数据也最存在一段时间后自动销毁。

  • 重点,是最后一句话哟。
  • 真的,和几个朋友沟通了下,真的出现不一致的情况,靠缓存过期后,重新从 DB 中读取即可。

另外,在 DB 主从架构下,方案会更加复杂。详细可以看看 《主从 DB 与 cache 一致性优化》 。

这是一道相对复杂的问题,重点在于理解为什么产生不一致的原因,然后针对这个原因去解决。

什么是缓存预热?如何实现缓存预热?

缓存预热

在刚启动的缓存系统中,如果缓存中没有任何数据,如果依靠用户请求的方式重建缓存数据,那么对数据库的压力非常大,而且系统的性能开销也是巨大的。

此时,最好的策略是启动时就把热点数据加载好。这样,用户请求时,直接读取的就是缓存的数据,而无需去读取 DB 重建缓存数据。

举个例子,热门的或者推荐的商品,需要提前预热到缓存中。

如何实现

一般来说,有如下几种方式来实现:

  1. 数据量不大时,项目启动时,自动进行初始化。
  2. 写个修复数据脚本,手动执行该脚本。
  3. 写个管理界面,可以手动点击,预热对应的数据到缓存中。

缓存数据的淘汰策略有哪些?

除了缓存服务器自带的缓存自动失效策略之外,我们还可以根据具体的业务需求进行自定义的“手动”缓存淘汰,常见的策略有两种:

  • 1、定时去清理过期的缓存。
  • 2、当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,第一种的缺点是维护大量缓存的 key 是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!
具体用哪种方案,大家可以根据自己的应用场景来权衡。

缓存如何存储 POJO 对象?

实际场景下,缓存值可能是一个 POJO 对象,就需要考虑如何 POJO 对象存储的问题。目前有两种方式:

666. eggs

Reference and recommend the following articles:

Published 12 original articles · won praise 1 · views 778

Guess you like

Origin blog.csdn.net/a1290123825/article/details/88960000