020-29876379

网站建设行业

## 一、背景:Tomcat 9 驱动的网站为什么会"慢"?

Tomcat 9 的安装与配置流程——在 Alibaba Cloud Linux 3(即阿里云官方维护的企业级 Linux 发行版,基于 OpenAnolis 内核,与 CentOS 7/8 生态高度兼容)上,从下载压缩包、解压到 `/opt/tomcat9`,到配置 `systemd` 服务、编写 `setenv.sh` 优化 JVM 参数(`-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200`),最终让 Tomcat 在 8080 端口稳定对外提供服务。

这套流程是企业级 Java Web 应用的标准起点。然而,随着网站访问量增大,一个普遍的性能瓶颈开始显现:**每一次用户请求,Tomcat 都要驱动 Java 应用去访问数据库,查询、组装数据,再渲染页面返回给浏览器。** 数据库 I/O 是整条链路中最慢的一环,哪怕数据库本身已经做了索引优化,在高并发场景下依然会成为整个系统的"肠梗阻"。

用 `curl`进行未优化前的首页网站测速:

```bash
# 网站测试结果:首页响应耗时(total time = 真实用户等待时长)
curl -o /dev/null -s -w "DNS解析: %{time_namelookup}s\n连接建立: %{time_connect}s\n首字节时间: %{time_starttransfer}s\n总耗时: %{time_total}s\n" \
  https://guangzhou.wangzhanjianshe9.com.cn/

# 典型未优化输出:
# DNS解析: 0.012s
# 连接建立: 0.045s
# 首字节时间: 1.380s   ← Tomcat 处理 + 数据库查询占了绝大部分
# 总耗时: 1.402s
```

首字节时间(TTFB)超过 1 秒,说明服务端处理逻辑消耗了大量时间,这正是 Redis 缓存大展身手的场景。

解决这一问题最直接、最成熟的方案,就是引入 **Redis 缓存层**,将 Tomcat 从"每次都去翻数据库"变成"绝大多数时候直接读内存"。

---

## 二、Redis 是什么?为什么它能使网站加速?

Redis(Remote Dictionary Server)是一款基于内存的高性能键值存储系统。它将数据全部存放在内存中,读写速度可以达到每秒数十万次操作,远超关系型数据库的磁盘 I/O 能力。

与数据库相比,Redis 的核心优势体现在以下几点:

- **读写速度极快**:内存操作,延迟通常在微秒级别,比 MySQL 磁盘查询快 100 倍以上。
- **数据结构丰富**:支持字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(ZSet)等,能灵活应对各类缓存场景。
- **原生支持过期时间**:可以为每个键设置 TTL(Time To Live),缓存数据自动失效,无需额外清理逻辑。
- **高可用与持久化**:支持主从复制、哨兵模式和集群模式,数据可以通过 RDB 快照或 AOF 日志持久化到磁盘,兼顾速度与可靠性。

---

## 三、在 Alibaba Cloud Linux 3 上安装与配置 Redis

Alibaba Cloud Linux 3 的软件源(Aliyun Base / EPEL)已内置 Redis 7.x,安装非常便捷,且与 Tomcat 9 的 `systemd` 管理方式完全一致:

```bash
# 1. 安装 Redis(Alibaba Cloud Linux 3 默认源已包含)
sudo dnf install -y redis

# 2. 启动并设置开机自启(与 Tomcat 的 systemd 管理方式相同)
sudo systemctl enable --now redis

# 3. 验证 Redis 正常运行
redis-cli ping
# 预期返回:PONG

# 4. 查看 Redis 版本
redis-cli info server | grep redis_version
```

### 安全加固:设置访问密码

Redis 默认无密码,生产环境必须设置 `requirepass`,避免未授权访问。编辑 `/etc/redis/redis.conf`:

```bash
sudo sed -i 's/^# requirepass foobared/requirepass Redis@huangpu.wangzhanjianshe9.com.cn/' \
  /etc/redis/redis.conf

# 重启生效
sudo systemctl restart redis

# 验证密码认证
redis-cli -a 'Redis@huangpu.wangzhanjianshe9.com.cn' ping
```

此外,若 Redis 仅供本机 Tomcat 使用,保持默认 `bind 127.0.0.1` 即可,无需对外暴露端口,进一步降低攻击面。

在 Tomcat 的 `setenv.sh` 中追加 Redis 连接参数,与原有 JVM 参数一并管理:

```bash
sudo tee -a /opt/tomcat9/bin/setenv.sh >/dev/null <<'EOF'
# Redis 连接配置
export JAVA_OPTS="$JAVA_OPTS \
  -Dredis.host=127.0.0.1 \
  -Dredis.port=6379 \
  -Dredis.password=Redis@huangpu.wangzhanjianshe9.com.cn \
  -Dredis.database=0"
EOF

sudo systemctl restart tomcat
```

---

## 四、Redis 加速网站的三大核心场景

### 1. 页面级缓存(全页缓存)

对于内容相对固定的页面——例如企业介绍、产品列表、新闻资讯等——可以将 Tomcat 生成的 HTML 内容直接序列化后存入 Redis,并设置合理的过期时间(如 10 分钟)。

```
用户请求 → Nginx → Tomcat → 检查 Redis 是否有缓存
    ├── 命中 → 直接返回 Redis 中的 HTML,整个数据库层完全绕过
    └── 未命中 → Tomcat 查询数据库 → 生成 HTML → 存入 Redis → 返回用户
```

引入全页缓存后,再用 `curl` 测试同一个接口,TTFB 的变化会非常直观:

```bash
# 引入 Redis 全页缓存后再测,TTFB 通常从 1s+ 降到 50ms 以内
curl -o /dev/null -s -w "首字节时间: %{time_starttransfer}s\n总耗时: %{time_total}s\n" \
  https://huangpu.wangzhanjianshe9.com.cn/

# 优化后典型输出:
# 首字节时间: 0.048s   ← Redis 直接命中,Tomcat 几乎无计算开销
# 总耗时: 0.061s
```

这一策略能将大量重复请求的数据库查询次数降低 90% 以上,Tomcat 的 JVM 线程压力也随之大幅减小。

### 2. 数据对象缓存(Cache-Aside 模式)

对于频繁被查询但变化不频繁的数据对象(如用户信息、商品详情、配置项),可以将其序列化为 JSON 字符串存入 Redis。在 Java 代码中,通过如下逻辑实现缓存旁路模式:

```java
public Product getProduct(long productId) {
    String cacheKey = "product:" + productId;
    // 先查缓存
    String json = jedis.get(cacheKey);
    if (json != null) {
        return JSON.parseObject(json, Product.class);
    }
    // 缓存未命中,查数据库
    Product product = productDao.findById(productId);
    if (product != null) {
        // 写入缓存,TTL = 1800s,加随机抖动防雪崩
        int ttl = 1800 + ThreadLocalRandom.current().nextInt(300);
        jedis.setex(cacheKey, ttl, JSON.toJSONString(product));
    }
    return product;
}
```

这样,绝大多数请求都能在 Redis 层得到响应,数据库仅承担少量"缓存未命中"时的查询压力。

### 3. Session 共享与会话加速

在传统单机 Tomcat 部署中,Session 保存在 JVM 内存中。当网站升级为多台 Tomcat 集群后,用户的登录状态无法在不同节点间共享,导致频繁跳转登录。

将 Tomcat 的 Session 存储切换到 Redis,可以彻底解决这一问题。在 `context.xml` 中配置(需引入 `tomcat-redis-session-manager` 依赖):

```xml
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="127.0.0.1"
         port="6379"
         password="Redis@huangpu.wangzhanjianshe9.com.cn"
         database="0"
         maxInactiveInterval="1800" />
```

配置完成后,所有 Tomcat 实例共享同一个 Redis 中的 Session 数据,用户体验无感知,同时 Tomcat 的 JVM 堆内存压力也因减少了 Session 对象而有所下降。

---

## 五、缓存三大坑:穿透、雪崩、击穿

引入 Redis 缓存后,需要关注以下三个经典问题:

**缓存穿透**:大量请求查询数据库中根本不存在的数据,每次都绕过缓存直打数据库。解决方案是对空结果也写入缓存(TTL 设短一些,如 60s),或使用布隆过滤器(Bloom Filter)提前拦截非法查询键。

**缓存雪崩**:大量缓存键在同一时刻集体过期,流量瞬间涌入数据库。解决方案是在设置 TTL 时加入随机抖动(如上面代码中的 `+ random(0, 300)` 秒),避免集中失效。

**缓存击穿**:某个热点缓存键失效的瞬间,大量并发请求同时穿透缓存去查数据库。解决方案是对缓存重建操作加 Redis 分布式锁(`SET key value NX EX 10`),确保只有一个线程去查数据库并写回缓存,其余线程等待或返回兜底数据。

做好以上三点,Redis 缓存层才能真正稳定地发挥加速效果,而不会在极端情况下成为系统的隐患。

---

## 六、Nginx 层配合:让 Redis 加速效果最大化

在 Alibaba Cloud Linux 3 上,通常还会在 Tomcat 前面架设 Nginx 作为反向代理。结合 Redis 后,可以在 Nginx 层直接读取 Redis 中的全页缓存(需要 `lua-resty-redis` 模块),彻底绕过 Tomcat:

```nginx
# nginx.conf 片段(openresty 或 tengine 环境)
location / {
    # 尝试从 Redis 读取缓存的静态 HTML
    content_by_lua_block {
        local redis = require "resty.redis"
        local red = redis:new()
        red:set_timeout(100)
        red:connect("127.0.0.1", 6379)
        red:auth("Redis@huangpu.wangzhanjianshe9.com.cn")

        local cache_key = "page:" .. ngx.var.uri
        local html, err = red:get(cache_key)
        if html and html ~= ngx.null then
            ngx.header["Content-Type"] = "text/html; charset=UTF-8"
            ngx.header["X-Cache"] = "HIT"
            ngx.say(html)
            return
        end
        -- 缓存未命中,转发给 Tomcat
        ngx.header["X-Cache"] = "MISS"
        ngx.exec("@tomcat_backend")
    }
}

location @tomcat_backend {
    proxy_pass http://127.0.0.1:8080;
    # 其他 proxy 配置...
}
```

这一架构下,命中缓存的请求完全不经过 Java 进程,响应速度可以压缩到 10ms 以内。

---

## 七、实际效果对比

从生产环境的数据来看,在 Alibaba Cloud Linux 3 上完成 Tomcat 9 + Redis 的整合后,网站核心指标通常有如下改善:

| 指标 | 纯 Tomcat + 数据库 | 叠加 Redis 缓存后 |
|------|-------------------|-----------------|
| 首字节时间(TTFB) | 800ms ~ 1500ms | 20ms ~ 80ms |
| 数据库 QPS 峰值 | 2000+ | 200 以下 |
| Tomcat 线程池利用率 | 85%+ | 30% 以下 |
| 并发承载用户数 | 约 500 | 5000+ |
| 服务器 CPU 均值 | 70%+ | 25% 以下 |

可以用 `curl` 循环压测来直观感受优化前后的差距:

```bash
# 连续请求 10 次,统计平均 TTFB
for i in $(seq 1 10); do
  curl -o /dev/null -s -w "%{time_starttransfer}\n" \
    https://guangzhou.wangzhanjianshe9.com.cn/
done | awk '{sum+=$1} END {printf "平均TTFB: %.3fs\n", sum/NR}'
```

优化后,这个数字通常会从 1.x 秒降到 0.05 秒以内,提升幅度超过 20 倍。

---

## 八、总结

在 Alibaba Cloud Linux 3 + Tomcat 9 的基础架构上,引入 Redis 缓存层是一项投入产出比极高的性能优化。三个步骤就能覆盖绝大多数场景:

1. **全页缓存**:用 Redis 存储完整 HTML,命中时完全绕过数据库
2. **对象缓存**:Cache-Aside 模式缓存热点数据对象,降低数据库 QPS
3. **Session 共享**:为未来集群扩容打好基础,同时减少 JVM 内存压力

网站速度不仅是用户体验的基础,也是搜索引擎排名的重要信号。据研究数据,TTFB 每缩短 100ms,用户跳出率可降低约 8%。在数字化竞争日趋激烈的今天,一个响应迅速的网站,就是企业最有力的市场竞争武器之一。