黑马程序员Redis教程学习笔记(第3部分):负载均衡与缓存预热

2026-04-02
1
-
- 分钟
|

黑马程序员Redis教程学习笔记(第3部分):负载均衡与缓存预热

课程概述

本篇笔记涵盖黑马程序员Redis教程中关于多级缓存架构的高级内容,包括Tomcat集群负载均衡、Redis缓存预热、Nginx本地缓存以及缓存同步策略等核心知识点。

1. Tomcat集群负载均衡策略

问题背景

在多级缓存架构中,当有多台Tomcat服务器时,传统的轮询负载均衡算法会导致JVM进程缓存无法有效利用的问题。因为轮询算法会将相同商品ID的请求分配到不同的服务器上,导致缓存命中率降低。

基于URL的负载均衡

使用Nginx的hash算法实现基于请求URL的负载均衡:

upstream tomcat_cluster {
    hash $request_uri;  # 基于请求URL进行hash计算
    server 192.168.150.101:8081;
    server 192.168.150.101:8082;
}

实现原理

  • 对请求路径进行hash运算,得到一个数值
  • 对Tomcat服务器数量取模,确定目标服务器
  • 相同路径的请求总是路由到同一台服务器
  • 确保JVM进程缓存能够持续命中

2. Redis缓存预热

问题与解决方案

  • 问题:服务冷启动时Redis缓存为空,大量请求直接访问数据库
  • 解决方案:在项目启动时预先加载热点数据到Redis

实现方式

  1. 依赖引入
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置Redis连接
spring:
  redis:
    host: 192.168.150.101
    port: 6379
  1. 预热实现
@Component
public class RedisHandler implements InitializingBean {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ItemService itemService;
    
    @Autowired
    private ItemStockService stockService;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        // 查询所有商品信息
        List<Item> items = itemService.list();
        
        // 将商品信息存入Redis
        for (Item item : items) {
            // 序列化为JSON
            String json = mapper.writeValueAsString(item);
            // 存储到Redis,key格式为 item:id
            redisTemplate.opsForValue().set("item:" + item.getId(), json);
        }
        
        // 查询并存储库存信息
        List<ItemStock> stocks = stockService.list();
        for (ItemStock stock : stocks) {
            String json = mapper.writeValueAsString(stock);
            // 存储到Redis,key格式为 stock:id
            redisTemplate.opsForValue().set("stock:" + stock.getId(), json);
        }
    }
}

3. OpenResty查询Redis

Redis模块引入

OpenResty提供了Redis模块,使用方式:

-- 引入Redis模块
local redis = require "resty.redis"
local red = redis:new()

-- 设置连接超时时间(单位:毫秒)
red:set_timeout(1000) -- 连接超时
red:set_timeout(1000) -- 发送超时
red:set_timeout(1000) -- 接收超时

连接管理与数据操作

-- 连接Redis
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
    ngx.log(ngx.ERR, "failed to connect redis: ", err)
    return
end

-- 查询数据
local res, err = red:get(key)
if not res then
    ngx.log(ngx.ERR, "failed to query redis: ", err)
    return
end

if res == ngx.null then
    res = nil
end

-- 关闭连接(实际上是放回连接池)
local ok, err = red:set_keepalive(10000, 100) -- 最大空闲时间10秒,连接池大小100

封装Redis查询函数

-- 封装Redis查询函数
local function readRedis(ip, port, key) {
    local redis = require "resty.redis"
    local red = redis:new()
    
    red:set_timeout(1000)
    
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "failed to connect redis: ", err)
        return nil
    end
    
    local resp, err = red:get(key)
    if not resp then
        ngx.log(ngx.ERR, "failed to query redis: ", err)
        return nil
    end
    
    if resp == ngx.null then
        resp = nil
    end
    
    -- 放回连接池
    local ok, err = red:set_keepalive(10000, 100)
    return resp
}

4. OpenResty本地缓存

共享字典配置

在nginx.conf中配置共享字典:

# 开启本地缓存
lua_shared_dict item_cache 150m;

本地缓存操作

-- 获取本地缓存对象
local item_cache = ngx.shared.item_cache

-- 存储数据(带过期时间)
item_cache:set(key, value, expire_time)

-- 获取数据
local value = item_cache:get(key)

多级缓存查询逻辑

-- 修改readData函数,实现多级缓存
local function readData(ip, port, key, path, params, expire_time) {
    -- 1. 优先查询本地缓存
    local value = item_cache:get(key)
    
    if not value then
        ngx.log(ngx.ERR, "本地缓存查询失败,尝试查询Redis")
        
        -- 2. 本地缓存未命中,查询Redis
        value = readRedis(ip, port, key)
        
        if not value then
            ngx.log(ngx.ERR, "Redis查询失败,尝试查询Tomcat")
            
            -- 3. Redis未命中,查询Tomcat
            local res = ngx.location.capture(
                path,
                {
                    method = ngx.HTTP_GET,
                    args = params
                }
            )
            
            value = res.body
        end
        
        -- 4. 查询成功后,写入本地缓存
        item_cache:set(key, value, expire_time)
    end
    
    return value
}

5. 缓存同步策略

常见同步策略对比

策略 优点 缺点 适用场景
有效期控制 简单方便,无代码入侵 实时性差 更新频率低、实时性要求低的业务
同步双写 实时性强,数据一致性高 代码侵入度高,耦合度高 对一致性要求极高的业务
异步通知 低耦合,代码侵入少 实时性相对较差 对实时性要求不高、多服务同步场景

Canal实现原理

MySQL主从同步原理

  • Master节点:执行数据增删改查操作,记录到binary log文件
  • Slave节点:开启线程不断读取binary log,重放操作以保持数据一致

Canal工作原理

Canal通过伪装成MySQL的Slave节点,监听MySQL的binary log变化:

# MySQL主从同步流程
Master -> binary log -> Slave (读取并重放操作)
              
         Canal (伪装成Slave读取binary log)

Canal安装配置

1. 开启MySQL主从同步

修改MySQL配置文件:

# binlog文件位置和名称
log-bin=mysql-bin
# 监控的数据库
binlog-do-db=hmdp

2. 创建Canal用户并授权

-- 创建Canal用户
CREATE USER 'canal'@'%' IDENTIFIED BY 'canal';
-- 授权
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
-- 刷新权限
FLUSH PRIVILEGES;

3. 创建Docker网络

# 创建网络
docker network create hmdp

# 将MySQL容器加入网络
docker network connect hmdp mysql

4. 运行Canal容器

docker run -p 11111:11111 --name canal \
  -e canal.destinations=hmdp \
  -e canal.instance.master.address=mysql:3306 \
  -e canal.instance.dbUsername=canal \
  -e canal.instance.dbPassword=canal \
  -e canal.instance.connectionCharset=UTF-8 \
  -e canal.instance.tsdb.enable=true \
  -e canal.instance.gtidon=false \
  --network=hmdp \
  -d canal/canal-server:v1.1.5

6. 缓存架构总结

完整架构流程

浏览器/客户端 -> Nginx静态资源服务器 -> OpenResty负载均衡 -> Tomcat集群
                                    -> OpenResty本地缓存
                                    -> Redis缓存
                                    -> Tomcat进程缓存
                                    -> MySQL数据库

各层缓存策略

  1. 浏览器客户端缓存:静态资源缓存,通过304状态码优化
  2. Nginx本地缓存:基于URL哈希的负载均衡,确保相同商品请求路由到同一服务器
  3. OpenResty本地缓存:使用shared_dict实现进程间共享缓存
  4. Redis缓存:分布式缓存,支持集群部署
  5. Tomcat进程缓存:JVM进程内缓存,使用Caffeine实现

缓存同步策略

  • OpenResty本地缓存:使用过期时间策略,适合更新频率较低的数据
  • Redis缓存:通过Canal监听MySQL binlog实现数据同步
  • Tomcat进程缓存:同样通过Canal实现同步

总结

本篇笔记涵盖了多级缓存架构的高级实现,包括: - 通过基于URL的负载均衡算法解决JVM进程缓存失效问题 - 通过Redis缓存预热避免冷启动时的数据库压力 - 通过OpenResty的Redis模块和本地缓存实现多级缓存体系 - 通过Canal实现数据库变更的实时监听和缓存同步

这些技术共同构成了一个高性能、高可用的多级缓存系统,能够有效提升系统性能并保证数据一致性。

评论交流

文章目录