文章目录
前面的课程通过压测发现了很多问题,并进行了多次优化,包括一下方案:
- 动静分离,把静态资源存储在nginx,缩短资源请求链路
- 开启thymeleaf缓存
- 调整jvm参数,扩大对内存及Eden区内存占比
- 优化业务代码,减少查询数据库的次数
经过了这些优化措施之后,压测性能有了显著的提升,但距离压测通过标准还有不小差距。
对于三级分类数据,因为其改动较少,变化不大,数据一致性要求也不高,所以还可以通过缓存的方式尽量避免从数据库中读取数据,这会大幅提升性能。
缓存分为两种:
- 本地缓存
- 分布式缓存
一,本地缓存
1,定义
本地缓存(Local Cache)通常指的是在单个应用实例的内存中存储的缓存数据。这种缓存方式简单直接,通常使用编程语言内置的数据结构如哈希表(HashMap)来实现。
2,优点
- 访问速度快:由于数据存储在本地内存中,访问本地缓存几乎无网络延迟,可以快速响应数据请求。
- 简单易实现:大多数编程语言都提供了易于使用的集合类,如Java中的HashMap,Python中的dict,可以快速实现本地缓存。
3,代码示例(Java)
以下是一个简单的本地缓存实现示例,使用Java中的HashMap:
java
import java.util.HashMap;
import java.util.Map;
public class LocalCacheExample {
private Map<String, Object> cache = new HashMap<>();
public Object getFromCache(String key) {
// 尝试从本地缓存获取数据
return cache.get(key);
}
public void addToCache(String key, Object value) {
// 将数据放入本地缓存
cache.put(key, value);
}
public static void main(String[] args) {
LocalCacheExample localCache = new LocalCacheExample();
// 假设我们缓存一个商品信息
String productId = "123";
Product product = getProductFromDatabase(productId); // 从数据库获取商品信息
localCache.addToCache(productId, product);
// 后续请求可以直接从缓存获取
product = (Product) localCache.getFromCache(productId);
if (product != null) {
System.out.println("Product found in local cache.");
} else {
System.out.println("Product not found in local cache, fetching from DB.");
}
}
private Product getProductFromDatabase(String id) {
// 模拟数据库查询
return new Product(id, "Sample Product");
}
}
class Product {
private String id;
private String name;
public Product(String id, String name) {
this.id = id;
this.name = name;
}
// Getters and Setters
}
4,缺点
- 数据不一致:在多实例部署的情况下,每个实例可能拥有不同的本地缓存副本,导致数据不一致。
- 容量有限:缓存容量受限于单台服务器的内存大小,不适合存储大量数据。
- 扩展性差 :本地缓存难以水平扩展,当系统负载增加时,可能需要更复杂的解决方案。
,
二,分布式缓存
在分布式系统中,数据的快速访问和一致性是至关重要的,这正是分布式缓存发挥作用的地方。以下是对分布式缓存的概述,包括其定义、优点、缺点以及代码示例。
1,定义
分布式缓存是一种将缓存数据分布在多个服务器或节点上的机制,这些服务器可以是物理机或虚拟机。它允许系统通过多个缓存节点共享数据,从而提高数据访问速度和系统扩展性。
2,优点
- 数据一致性:所有应用实例共享同一份缓存数据,确保了数据的一致性。
- 高可用性:通过冗余和故障转移机制,即使部分节点失败,缓存服务仍可继续提供服务。
- 扩展性强:可以通过增加更多的缓存节点来水平扩展,以应对不断增长的数据量和访问压力。
- 负载均衡:分布式缓存系统通常内置或支持负载均衡机制,可以均匀分配请求到各个节点。
3,缺点
- 访问速度相对较慢:与本地缓存相比,分布式缓存可能因为网络延迟而稍慢。
- 实现复杂:需要处理网络通信、数据分片、一致性协议等复杂问题。
- 成本较高:需要投资更多的硬件资源,以及维护分布式系统的复杂性。
4,代码示例
以下是使用Redis作为分布式缓存的一个简单示例。Redis是一个开源的内存数据结构存储系统,常用作分布式缓存解决方案。
java
import redis.clients.jedis.Jedis;
public class RedisCacheExample {
private static final String CACHE_HOST = "localhost";
private static final int CACHE_PORT = 6379;
public static void main(String[] args) {
// 连接到Redis缓存服务器
try (Jedis jedis = new Jedis(CACHE_HOST, CACHE_PORT)) {
// 将数据放入缓存
jedis.set("product:123", "Product Name");
// 从缓存中获取数据
String productName = jedis.get("product:123");
System.out.println("Cached Product Name: " + productName);
// 如果需要更新缓存中的数据
jedis.set("product:123", "Updated Product Name");
}
}
}
在这个示例中,我使用Java的Jedis客户端连接到本地运行的Redis服务器,并将一个产品名称作为字符串存储在缓存中。之后,从缓存中检索并打印这个名称。如果产品信息更新了,也可以简单地更新缓存中的值。
三,本地缓存与分布式缓存的适用场景
- 本地缓存:适用于单实例部署,或者对缓存数据一致性要求不高的场景。
- 分布式缓存:适用于多实例部署,对数据一致性和高可用性有较高要求的场景。
四,缓存一致性的挑战与解决方案
- 缓存穿透:通过设置合理的超时策略和使用布隆过滤器来防止。
- 缓存雪崩:通过缓存数据的分布式过期策略来避免大量缓存同时过期。
- 缓存击穿:对于热点数据,使用互斥锁或分布式锁来保证更新操作的原子性。