您好!欢迎访问家园网-www.jy.wang!

家园网

应用层缓存

网络 作者:本站 点击:

// Java 示例:先缓存所有 ID,在应用层随机选择
List<Integer> ids = jdbcTemplate.queryForList("SELECT goods_id FROM goods", Integer.class);
int randId = ids.get(new Random().nextInt(ids.size()));
Map<String, Object> row = jdbcTemplate.queryForMap("SELECT * FROM goods WHERE goods_id = ?", randId);

这段代码确实一下子出现了很多新概念。让我从零开始,用最通俗的方式讲清楚方案四的核心思想。

🎯 核心思想:把"随机"这件事从数据库搬到代码里

方案四的本质

  1. 先查出所有商品的ID(这是一个很轻量的查询)

  2. 在Java代码里随机选一个ID

  3. 再用这个ID去数据库查出完整的商品信息

为什么这样更快?

  • 避免了数据库做全表排序

  • 两次查询都用上了索引(主键查询极快)

📚 逐行拆解(从零开始)

第0步:先搞懂 Java 基础概念

术语通俗解释类比
List<Integer>一个整数列表,可以存多个数字像一个购物清单,上面写着1,2,3...
jdbcTemplateJava操作数据库的工具像数据库的"电话接线员"
new Random()创建随机数生成器像摇号机
Map<String, Object>键值对集合像一张表,列名→值

第1行:查询所有ID

java

List<Integer> ids = jdbcTemplate.queryForList("SELECT goods_id FROM goods", Integer.class);

这行代码做了什么?

执行SQL:

sql

SELECT goods_id FROM goods;

假设 goods 表有5条数据:

text

goods_id
1
2
3
4
5

jdbcTemplate.queryForList() 会把这些ID变成一个Java列表:

java

ids = [1, 2, 3, 4, 5]  // 这是一个 List<Integer> 对象

关键词解释
jdbcTemplateSpring框架提供的数据库工具类
queryForList()查询返回多行数据的方法
"SELECT goods_id FROM goods"要执行的SQL
Integer.class告诉Java:把查询结果转成整数类型

第2行:随机选一个ID

java

int randId = ids.get(new Random().nextInt(ids.size()));

这行代码拆解成3步:

步骤1:ids.size()

java

ids.size()  // 返回 5(列表中有5个元素)

步骤2:new Random().nextInt(5)

java

new Random()              // 创建随机数生成器.nextInt(5)              // 生成 0 到 4 之间的随机整数// 可能返回: 0, 1, 2, 3, 4 中的任意一个

关键词解释
new Random()创建随机数生成器对象
nextInt(n)生成 0 到 n-1 之间的随机整数

步骤3:ids.get(随机数字)

java

// 假设随机数生成了 2ids.get(2)  // 从列表中取出索引为2的元素// ids = [1, 2, 3, 4, 5]// 索引:  0  1  2  3  4// ids.get(2) = 3

完整例子

text

假设随机数生成器返回 2
ids.get(2) → 取出列表中第3个元素(索引从0开始)→ 3
randId = 3

第3行:根据ID查询完整数据

java

Map<String, Object> row = jdbcTemplate.queryForMap("SELECT * FROM goods WHERE goods_id = ?", randId);

这行代码做了什么?

执行SQL(? 会被 randId 替换):

sql

SELECT * FROM goods WHERE goods_id = 3;

假设查询结果:

goods_id

cat_id

goods_name

shop_price

3

10

iPhone 15

5999



jdbcTemplate.queryForMap() 把这一行数据变成一个Map:

java

row = {
    "goods_id" → 3,
    "cat_id" → 10,
    "goods_name" → "iPhone 15",
    "shop_price" → 5999}

关键词解释
Map<String, Object>键值对集合,键是列名,值是对应的数据
queryForMap()查询返回单行数据的方法
"SELECT ... WHERE goods_id = ?"SQL中的 ? 是占位符
randId替换 ? 的具体值

🔄 完整流程演示

假设 goods 表有5条数据:

text

goods_id | goods_name
1        | 华为手机
2        | 小米手机
3        | iPhone
4        | 三星手机
5        | OPPO手机

执行过程

java

// 第1步:查出所有IDList<Integer> ids = jdbcTemplate.queryForList("SELECT goods_id FROM goods", Integer.class);// ids = [1, 2, 3, 4, 5]// 第2步:随机选一个int randId = ids.get(new Random().nextInt(ids.size()));// 假设随机生成 2 → ids.get(2) = 3// randId = 3// 第3步:查询完整信息Map<String, Object> row = jdbcTemplate.queryForMap(
    "SELECT * FROM goods WHERE goods_id = ?", randId);// row = {goods_id=3, goods_name="iPhone", ...}

最终结果:随机返回了 goods_id = 3 的商品信息。

🆚 对比数据库直接随机

方案

数据库工作量

Java工作量

总耗时

ORDER BY RAND()

全表扫描 + 排序

几乎没有

~0.5秒

方案四

两次索引查询

随机选一个数

~0.001秒


为什么方案四快100倍?

数据库直接随机:

text

扫描100万行 → 生成100万个随机数 → 排序100万行 → 取1行
(累死数据库了)

方案四:

text

查询1:索引扫描100万行ID(但只取ID列,数据量小)
Java:摇个号
查询2:主键查询1行(极快)
(数据库轻松搞定)

📦 完整的Java类示例

java

import org.springframework.jdbc.core.JdbcTemplate;import java.util.List;import java.util.Map;import java.util.Random;public class RandomGoodsService {
    
    private JdbcTemplate jdbcTemplate;  // 数据库连接工具
    
    public Map<String, Object> getRandomGoods() {
        // 1. 查询所有商品ID
        List<Integer> ids = jdbcTemplate.queryForList(
            "SELECT goods_id FROM goods", 
            Integer.class
        );
        
        // 2. 如果表是空的,返回null
        if (ids.isEmpty()) {
            return null;
        }
        
        // 3. 随机选一个ID
        Random random = new Random();
        int randomIndex = random.nextInt(ids.size());  // 0 到 size-1
        int randomId = ids.get(randomIndex);
        
        // 4. 根据ID查询完整信息
        Map<String, Object> goods = jdbcTemplate.queryForMap(
            "SELECT * FROM goods WHERE goods_id = ?",
            randomId        );
        
        return goods;
    }}

💡 方案四的优缺点

优点

优点说明
⚡ 极快两次查询都用索引,大数据量下性能稳定
🎲 真正随机每个记录被选中的概率完全相等
🔧 可控可以在Java层加缓存、限流等逻辑

缺点

缺点说明解决方案
📡 两次查询比一次查询多一次网络往返可以接受(耗时<1ms)
💾 内存占用缓存所有ID(100万条 ≈ 4MB)完全可以接受
🔄 数据变化新增/删除商品时缓存可能过期每次实时查询ID列表

🎯 总结

关键词一句话解释
List<Integer>装了一堆整数的盒子
jdbcTemplateJava操作数据库的工具
queryForList()查询返回多行数据
Random随机数生成器
nextInt(n)生成0到n-1的随机数
Map<String, Object>装了一行数据的盒子(列名→值)
queryForMap()查询返回一行数据

方案四的本质:用两次简单查询(都是索引查询)代替一次复杂查询(全表排序),用空间换时间,用代码逻辑换数据库压力。这是处理"大表随机取一行"的最佳实践


标签: