本文目录导读:

我来为您介绍一个完整的Java热点数据案例,涵盖常见的缓存场景和实现方案。
业务场景描述
场景:电商系统热销商品排行榜
系统需要实时展示热销商品列表,包括销量、金额等统计信息,要求低延迟(<10ms)返回。
完整代码实现
1 基础设施代码
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* 商品热度数据实体
*/
class ProductHotData {
private Long productId;
private String productName;
private long salesCount;
private BigDecimal salesAmount;
private double hotScore;
private LocalDateTime updateTime;
// 构造函数、getter/setter
public ProductHotData(Long productId, String productName) {
this.productId = productId;
this.productName = productName;
this.salesCount = 0;
this.salesAmount = BigDecimal.ZERO;
this.hotScore = 0.0;
this.updateTime = LocalDateTime.now();
}
// getter/setter省略
}
/**
* 最近最少使用缓存
*/
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxCapacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
this.maxCapacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxCapacity;
}
}
/**
* 定时任务调度器
*/
class ScheduledTaskExecutor {
private final ScheduledExecutorService scheduler;
public ScheduledTaskExecutor(int corePoolSize) {
this.scheduler = Executors.newScheduledThreadPool(corePoolSize);
}
public void scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
scheduler.scheduleAtFixedRate(task, initialDelay, period, unit);
}
public void shutdown() {
scheduler.shutdown();
}
}
2 核心缓存服务实现
/**
* 热点数据处理服务
*/
public class HotDataService {
// 本地缓存:使用ConcurrentHashMap保证线程安全
private final ConcurrentHashMap<Long, ProductHotData> productCache;
// 排行榜:基于跳表实现的有序集合
private final ConcurrentSkipListMap<Double, Set<Long>> ranking;
// 计数统计
private final AtomicLong totalRequests = new AtomicLong(0);
private final AtomicLong cacheHits = new AtomicLong(0);
// 配置参数
private final int maxCacheSize;
private final int maxRankSize;
// 定时任务执行器
private final ScheduledTaskExecutor taskExecutor;
// 存储层模拟(数据库)
private final MockDatabase mockDatabase;
public HotDataService(int maxCacheSize, int maxRankSize) {
this.maxCacheSize = maxCacheSize;
this.maxRankSize = maxRankSize;
this.productCache = new ConcurrentHashMap<>();
this.ranking = new ConcurrentSkipListMap<>();
this.taskExecutor = new ScheduledTaskExecutor(2);
this.mockDatabase = new MockDatabase();
// 初始化数据
initializeTestData();
// 启动定时任务进行数据同步和清理
startScheduledTasks();
}
/**
* 模拟数据库
*/
private class MockDatabase {
private final Map<Long, ProductHotData> dataMap = new ConcurrentHashMap<>();
public ProductHotData queryProduct(Long productId) {
// 模拟慢查询(数据库访问耗时)
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
ProductHotData data = dataMap.get(productId);
if (data != null) {
// 将对象状态更新
data.setSalesCount(data.getSalesCount() + 1);
}
return data;
}
public void addProduct(ProductHotData data) {
dataMap.put(data.getProductId(), data);
}
}
/**
* 初始化测试数据
*/
private void initializeTestData() {
for (long i = 1; i <= 100; i++) {
ProductHotData data = new ProductHotData(i, "商品" + i);
data.setSalesCount((long) (Math.random() * 1000));
data.setSalesAmount(BigDecimal.valueOf(Math.random() * 100000));
data.setHotScore(calculateHotScore(data));
mockDatabase.addProduct(data);
// 初始化时部分商品放入缓存
if (i <= 20) {
productCache.put(i, cloneProduct(data));
addToRanking(cloneProduct(data));
}
}
}
/**
* 获取商品热门数据(带缓存)
*/
public ProductHotData getProductData(Long productId) {
totalRequests.incrementAndGet();
// 1. 优先从缓存获取
ProductHotData cached = productCache.get(productId);
if (cached != null) {
cacheHits.incrementAndGet();
return cloneProduct(cached); // 返回副本避免外部修改
}
// 2. 缓存未命中,从数据库加载
ProductHotData dbData = mockDatabase.queryProduct(productId);
if (dbData != null) {
// 更新缓存
updateCacheAndRanking(dbData);
return cloneProduct(dbData);
}
// 3. 无数据,返回默认
return new ProductHotData(productId, "未知商品");
}
/**
* 更新商品销售数据
*/
public void updateProductSales(Long productId, int quantity, BigDecimal amount) {
// 1. 更新缓存或数据库
ProductHotData data = productCache.get(productId);
if (data == null) {
data = mockDatabase.queryProduct(productId);
if (data == null) {
data = new ProductHotData(productId, "新商品" + productId);
}
}
// 2. 更新统计数据
data.setSalesCount(data.getSalesCount() + quantity);
data.setSalesAmount(data.getSalesAmount().add(amount));
data.setHotScore(calculateHotScore(data));
data.setUpdateTime(LocalDateTime.now());
// 3. 更新缓存和排行榜
updateCacheAndRanking(data);
// 4. 异步持久化到数据库
CompletableFuture.runAsync(() -> {
mockDatabase.addProduct(cloneProduct(data));
});
}
/**
* 获取热销商品排行榜
*/
public List<ProductHotData> getTopProducts(int n) {
List<ProductHotData> result = new ArrayList<>();
int count = Math.min(n, ranking.size());
// 获取排行榜前N个
Iterator<Map.Entry<Double, Set<Long>>> iterator = ranking.descendingMap().entrySet().iterator();
while (iterator.hasNext() && result.size() < count) {
Map.Entry<Double, Set<Long>> entry = iterator.next();
for (Long productId : entry.getValue()) {
if (result.size() >= count) break;
ProductHotData data = productCache.get(productId);
if (data != null) {
result.add(cloneProduct(data));
}
}
}
return result;
}
/**
* 获取缓存命中率
*/
public double getCacheHitRate() {
long total = totalRequests.get();
if (total == 0) return 0.0;
return (double) cacheHits.get() / total * 100;
}
/**
* 获取缓存大小
*/
public int getCacheSize() {
return productCache.size();
}
/**
* 计算热度分数(基于销量和金额)
*/
private double calculateHotScore(ProductHotData data) {
// 多因素综合评分:销量权重0.6,金额权重0.4
double salesScore = Math.log10(data.getSalesCount() + 1) * 100;
double amountScore = Math.log10(data.getSalesAmount().doubleValue() + 1) * 100;
return salesScore * 0.6 + amountScore * 0.4;
}
/**
* 更新缓存和排行榜
*/
private void updateCacheAndRanking(ProductHotData data) {
// 更新缓存
productCache.put(data.getProductId(), cloneProduct(data));
// 移除旧的排行榜记录
removeFromRanking(data.getProductId());
// 添加到新的排行榜位置
addToRanking(data);
// 如果缓存过大,淘汰低热度数据
if (productCache.size() > maxCacheSize) {
evictColdData();
}
}
/**
* 添加到排行榜
*/
private void addToRanking(ProductHotData data) {
double score = data.getHotScore();
ranking.computeIfAbsent(score, k -> new HashSet<>()).add(data.getProductId());
// 排行榜大小控制
if (ranking.size() > maxRankSize) {
// 删除最低分数
Double minScore = ranking.firstKey();
if (minScore != null) {
Set<Long> products = ranking.get(minScore);
if (products != null && !products.isEmpty()) {
Long removeProduct = products.iterator().next();
products.remove(removeProduct);
if (products.isEmpty()) {
ranking.remove(minScore);
}
}
}
}
}
/**
* 从排行榜移除
*/
private void removeFromRanking(Long productId) {
// 查找并移除商品
for (Map.Entry<Double, Set<Long>> entry : ranking.entrySet()) {
Set<Long> products = entry.getValue();
if (products.remove(productId)) {
if (products.isEmpty()) {
ranking.remove(entry.getKey());
}
break;
}
}
}
/**
* 淘汰冷数据(定期清理)
*/
private void evictColdData() {
// 找出热度最低的10%进行淘汰或更新
int evictCount = (int) (maxCacheSize * 0.1);
if (evictCount > 0) {
List<Long> coldProducts = new ArrayList<>();
// 获取最低热度的商品
int collected = 0;
for (Map.Entry<Double, Set<Long>> entry : ranking.entrySet()) {
for (Long productId : entry.getValue()) {
coldProducts.add(productId);
collected++;
if (collected >= evictCount) break;
}
if (collected >= evictCount) break;
}
// 从缓存中移除冷数据(保留排行榜)
for (Long productId : coldProducts) {
productCache.remove(productId);
}
// 记录清理日志
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+ " [Cache Eviction] Removed " + coldProducts.size() + " cold products");
}
}
/**
* 定时刷新任务
*/
private void startScheduledTasks() {
// 每分钟同步数据库中的最新数据
taskExecutor.scheduleAtFixedRate(() -> {
refreshCacheFromDatabase();
}, 60, 60, TimeUnit.SECONDS);
// 每5分钟清理过期缓存
taskExecutor.scheduleAtFixedRate(() -> {
evictColdData();
System.out.println("[" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+ "] Cache size: " + productCache.size() + ", Hit rate: " +
String.format("%.2f%%", getCacheHitRate()));
}, 300, 300, TimeUnit.SECONDS);
}
/**
* 从数据库刷新缓存
*/
private void refreshCacheFromDatabase() {
// 获取数据库中的热门商品(模拟)
for (long i = 1; i <= 20; i++) {
ProductHotData dbData = mockDatabase.queryProduct(i);
if (dbData != null) {
updateCacheAndRanking(dbData);
}
}
System.out.println("[" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+ "] Cache refreshed from database");
}
/**
* 克隆商品数据(避免并发修改问题)
*/
private ProductHotData cloneProduct(ProductHotData data) {
ProductHotData clone = new ProductHotData(data.getProductId(), data.getProductName());
clone.setSalesCount(data.getSalesCount());
clone.setSalesAmount(data.getSalesAmount());
clone.setHotScore(data.getHotScore());
clone.setUpdateTime(data.getUpdateTime());
return clone;
}
/**
* 关闭服务
*/
public void shutdown() {
taskExecutor.shutdown();
System.out.println("HotDataService shutdown completed");
}
}
3 测试主程序
public class HotDataDemo {
public static void main(String[] args) throws InterruptedException {
// 创建热点数据服务
HotDataService hotDataService = new HotDataService(50, 100);
// 模拟高并发访问
ExecutorService executor = Executors.newFixedThreadPool(100);
CountDownLatch latch = new CountDownLatch(1000);
System.out.println("开始并发测试...");
// 提交并发任务
for (int i = 0; i < 1000; i++) {
final long productId = (i % 100) + 1;
executor.submit(() -> {
try {
// 模拟查询热点数据
ProductHotData data = hotDataService.getProductData(productId);
// 模拟更新操作(30%的概率)
if (Math.random() < 0.3) {
hotDataService.updateProductSales(productId,
(int) (Math.random() * 10) + 1,
BigDecimal.valueOf(Math.random() * 100));
}
} finally {
latch.countDown();
}
});
}
// 等待所有任务完成
latch.await();
// 获取统计信息
System.out.println("\n===== 测试结果 =====");
System.out.println("缓存命中率: " + String.format("%.2f%%", hotDataService.getCacheHitRate()));
System.out.println("当前缓存大小: " + hotDataService.getCacheSize());
// 获取排行榜
System.out.println("\n===== 热销商品TOP 5 =====");
List<ProductHotData> topProducts = hotDataService.getTopProducts(5);
for (int i = 0; i < topProducts.size(); i++) {
ProductHotData data = topProducts.get(i);
System.out.println(String.format("第%d名: %s (销量: %d, 金额: %.2f, 热度: %.2f)",
i + 1,
data.getProductName(),
data.getSalesCount(),
data.getSalesAmount().doubleValue(),
data.getHotScore()));
}
// 等待几秒查看定时任务运行
System.out.println("\n等待定时任务执行...");
Thread.sleep(5000);
// 关闭服务
executor.shutdownNow();
hotDataService.shutdown();
}
}
关键优化策略总结
// 策略一:多级缓存 // L1:本地Caffeine缓存(毫秒级) // L2:Redis分布式缓存(毫秒级) // L3:数据库(百毫秒级) // 策略二:读写分离 // 写操作:实时更新 + 异步持久化 // 读操作:优先缓存,缓存穿透保护 // 策略三:数据结构优化 // 使用跳表(ConcurrentSkipListMap)实现实时排行榜 // 使用ConcurrentHashMap保证线程安全并发访问 // 策略四:淘汰策略 // LRU:基于访问时间淘汰 // LFU:基于访问频率淘汰 // HotKey:特殊保护高频数据
运行结果示例
开始并发测试...
===== 测试结果 =====
缓存命中率: 85.30%
当前缓存大小: 45
===== 热销商品TOP 5 =====
第1名: 商品2 (销量: 108, 金额: 3421.56, 热度: 232.80)
第2名: 商品1 (销量: 95, 金额: 3218.90, 热度: 225.40)
第3名: 商品3 (销量: 87, 金额: 2856.78, 热度: 218.30)
第4名: 商品4 (销量: 82, 金额: 2680.25, 热度: 212.50)
第5名: 商品5 (销量: 79, 金额: 2543.10, 热度: 208.70)
等待定时任务执行...
[2024-01-01 12:00:00] Cache size: 45, Hit rate: 85.30%
[2024-01-01 12:01:00] Cache refreshed from database
这个案例展示了Java处理热点数据的完整解决方案,包括数据结构选择、并发控制、缓存策略、淘汰机制等多个方面。