当前位置: 首页 > article >正文

提升接口性能之缓存

缓存策略: 从本地到分布式, 再到 HTTP 缓存的全面解析

引言

在当今的软件开发中, 缓存技术扮演着至关重要的角色. 它能够显著提升系统性能, 降低数据库负载, 提高用户体验. 本文将详细介绍三种常见的缓存策略: 本地缓存, 分布式缓存以及 HTTP 缓存, 并探讨如何在实际应用中合理运用它们.

一, 本地缓存

1.1 Caffeine

Caffeine 是一个基于 Java 8 的高性能本地缓存库. 它结合了多种优秀的缓存算法, 具有极高的性能和可扩展性.

1.1.1 引入依赖

在 Maven 项目中, 只需添加以下依赖:

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.3</version>
</dependency>
1.1.2 使用示例
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;

public class CaffeineExample {
    public static void main (String [] args) {
        Cache<String, String> cache = Caffeine.newBuilder ()
              .expireAfterWrite (10, TimeUnit.MINUTES)
              .maximumSize (100)
              .build ();

        cache.put ("key1", "value1");
        String value = cache.getIfPresent ("key1");
        System.out.println (value);
    }
}

在上述代码中, 我们设置了缓存项在写入 10 分钟后过期, 并设置了最大缓存容量为 100. 当缓存达到最大容量时, 会根据 LRU (最近最少使用) 策略淘汰旧的缓存项.

1.2 Guava Cache

Guava Cache 是 Google Guava 库中的一部分, 提供了功能强大的本地缓存实现.

1.2.1 引入依赖
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>
1.2.2 使用示例
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;

public class GuavaCacheExample {
    public static void main (String [] args) {
        Cache<String, String> cache = CacheBuilder.newBuilder ()
              .expireAfterWrite (5, TimeUnit.MINUTES)
              .maximumSize (50)
              .build ();

        cache.put ("key2", "value2");
        String value = cache.getIfPresent ("key2");
        System.out.println (value);
    }
}

这里同样设置了缓存项在写入 5 分钟后过期, 最大缓存容量为 50, 同样采用 LRU 淘汰策略.

二, 分布式缓存

2.1 Redis

Redis 是一个开源的, 基于内存的数据结构存储系统, 常用作分布式缓存. 它支持多种数据结构, 如字符串, 哈希表, 列表等, 并且具有高可用性和高性能.

2.1.1 缓存预热

在系统启动时, 可以通过编写脚本将热点数据提前加载到 Redis 中. 例如, 使用 Python 的 redis - py 库:

import redis

r = redis.StrictRedis (host='localhost', port=6379, db = 0)

# 假设热点数据存储在一个列表中
hot_data = [('key3', 'value3'), ('key4', 'value4')]

for key, value in hot_data:
    r.set (key, value)
2.1.2 防雪崩策略

为了防止大量缓存同时过期导致数据库压力瞬间增大, 可以为每个缓存项设置随机的过期时间. 例如, 在 Java 中使用 Jedis 操作 Redis:

import redis.clients.jedis.Jedis;
import java.util.Random;

public class RedisAntiAvalancheExample {
    public static void main (String [] args) {
        Jedis jedis = new Jedis ("localhost", 6379);
        Random random = new Random ();
        int baseExpireTime = 3600; // 基础过期时间 1 小时
        int maxRandomOffset = 600; // 最大随机偏移 10 分钟

        for (int i = 0; i < 10; i++) {
            String key = "key" + i;
            String value = "value" + i;
            int expireTime = baseExpireTime + random.nextInt (maxRandomOffset);
            jedis.setex (key, expireTime, value);
        }
        jedis.close ();
    }
}

2.2 Memcached

Memcached 是一个高性能的分布式内存对象缓存系统, 主要用于动态 Web 应用以减轻数据库负载. 它的使用方式与 Redis 类似, 但数据结构相对简单, 主要以键值对形式存储.

2.2.1 缓存预热

在 Python 中, 可以使用 pymemcache 库进行缓存预热:

from pymemcache.client import base

client = base.Client (('localhost', 11211))

hot_data = [('key5', 'value5'), ('key6', 'value6')]

for key, value in hot_data:
    client.set (key, value)
2.2.2 防雪崩策略

在 Java 中使用 XMemcached 设置随机过期时间:

import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.utils.AddrUtil;
import java.util.Random;

public class MemcachedAntiAvalancheExample {
    public static void main (String [] args) throws Exception {
        MemcachedClientBuilder builder = new XMemcachedClientBuilder (AddrUtil.getAddresses ("localhost:11211"));
        MemcachedClient client = builder.build ();
        Random random = new Random ();
        int baseExpireTime = 3600;
        int maxRandomOffset = 600;

        for (int i = 0; i < 10; i++) {
            String key = "key" + i;
            String value = "value" + i;
            int expireTime = baseExpireTime + random.nextInt (maxRandomOffset);
            client.set (key, expireTime, value);
        }
        client.shutdown ();
    }
}

三, HTTP 缓存

3.1 Cache - Control

Cache - Control 是 HTTP/1.1 中用于控制缓存的通用首部字段. 通过设置不同的指令, 可以精确控制缓存行为.

3.1.1 示例

在 Java 的 Spring Boot 项目中, 可以通过如下方式设置 Cache - Control 头:

import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class HttpCacheExample {
    @GetMapping ("/staticResource")
    public ResponseEntity<String> getStaticResource () {
        CacheControl cacheControl = CacheControl.maxAge (3600, TimeUnit.SECONDS).cachePublic ();
        return ResponseEntity.ok ()
              .cacheControl (cacheControl)
              .body ("This is a static resource");
    }
}

上述代码设置了缓存有效期为 1 小时, 并且允许公共缓存 (如 CDN) 缓存该资源.

3.2 ETag

ETag (实体标签) 是一种用于缓存验证的机制. 服务器为每个资源生成一个唯一的标识符, 客户端在后续请求中携带该标识符, 服务器通过比较标识符来判断资源是否发生变化.

3.2.1 示例

在 Java 的 Servlet 中, 可以如下生成和验证 ETag:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

@WebServlet ("/etagResource")
public class ETagServlet extends HttpServlet {
    @Override
    protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String resource = "This is a resource with ETag";
        String etag = generateETag (resource);

        String ifNoneMatch = request.getHeader ("If - None - Match");
        if (ifNoneMatch!= null && ifNoneMatch.equals (etag)) {
            response.setStatus (HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        response.setHeader ("ETag", etag);
        PrintWriter out = response.getWriter ();
        out.println (resource);
    }

    private String generateETag (String resource) {
        try {
            MessageDigest digest = MessageDigest.getInstance ("MD5");
            byte [] hash = digest.digest (resource.getBytes ());
            StringBuilder hexString = new StringBuilder ();
            for (byte b : hash) {
                hexString.append (String.format ("%02x", b));
            }
            return hexString.toString ();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException (e);
        }
    }
}

四, 总结

本地缓存适用于减少单个应用内的重复计算和数据库查询, Caffeine 和 Guava Cache 提供了便捷的实现方式. 分布式缓存如 Redis 和 Memcached 则用于缓存全局共享数据, 通过缓存预热和防雪崩策略能有效降低数据库压力. HTTP 缓存则通过控制客户端和 CDN 对静态资源的缓存, 进一步提升了用户访问速度. 合理运用这三种缓存策略, 可以构建出高性能, 高可用的应用系统.


http://www.kler.cn/a/551961.html

相关文章:

  • Spring Boot项目的基本设计步骤和相关要点介绍
  • Win10环境使用零讯ZeroNews内网穿透实现Deepseek对外服务
  • UDP
  • 全平台搭载旭日5!科沃斯GOAT智能割草机器人全新系列正式开售
  • 轻松搭建本地大语言模型(一)Ollama安装与使用
  • 山石网科×阿里云通义灵码,开启研发“AI智造”新时代
  • 记一次Ngnix配置
  • 智能合约与区块链中的NLP应用:自动化法律文书分析与合同审查【附核心实战代码】
  • 【UE5 C++课程系列笔记】30——自动拷贝DLL及其他资源
  • vue3-03初学vue3中的配置项setup(Composition API (组合API组件中所用到的:数据、方法等,均要配置在setup中)
  • 大模型基础知识快问快答
  • 1、AI量化学习资料 - 用DEEPSEEK玩转PTrade策略开发.zip\AI量化学习资料 - 1、PTrade策略开发提示词(参考模板).md
  • 多线程编程的隐形陷阱:竞态、死锁与活锁的实战解决方案
  • ARM系统源码编译OpenCV 4.10.0(包含opencv_contrib)
  • 二十多年前的苹果电源Power Mac G4 Mdd 电源接口
  • 【Python项目】文本相似度计算系统
  • Android 通过 ADB 查看应用运行日志
  • ESP32 ESP-IDF TFT-LCD(ST7735 128x160)自定义组件驱动显示
  • 海康威视NVR DS-7816N-SN固件升级指南:安全、高效、无忧
  • 以下是一个使用 HTML、CSS 和 JavaScript 实现的登录弹窗效果示例