Java热点数据案例

wen java案例 1

本文目录导读:

Java热点数据案例

  1. 业务场景描述
  2. 完整代码实现
  3. 关键优化策略总结
  4. 运行结果示例

我来为您介绍一个完整的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处理热点数据的完整解决方案,包括数据结构选择、并发控制、缓存策略、淘汰机制等多个方面。

抱歉,评论功能暂时关闭!