Redis缓存集成到Web集群配置教程:从部署到优化(附Ansible自动化配置)

在Nginx+PHP/Java+MySQL的Web集群中集成Redis缓存,是提升系统并发能力、降低数据库压力的核心手段。

一、集成背景与架构设计

1.1 集成目标

  • 为Web集群提供统一的缓存层,避免单节点Redis成为瓶颈;
  • 实现缓存与数据库的数据一致性,解决分布式场景下的缓存穿透/击穿问题;
  • 适配Nginx反向代理的负载均衡,保证多应用节点缓存访问一致。

1.2 集群架构(基于前文Web集群扩展)

markdown 复制代码
客户端 → Nginx反向代理(192.168.1.10) 
        → 2台PHP/Java应用服务器(192.168.1.20/21) 
            → Redis集群(192.168.1.40:7000/7001/7002 主从+3主3从)
            → MySQL数据库(192.168.1.30)

核心设计:

  • Redis采用Cluster集群(3主3从),保证高可用和分片存储;
  • 所有应用节点连接同一Redis集群,避免缓存数据孤岛;
  • 缓存逻辑统一封装,确保多应用节点缓存策略一致。

二、前置准备

  1. Web集群已部署完成(Nginx+PHP/Java+MySQL);
  2. 控制节点已安装Ansible,目标主机SSH免密登录配置完成;
  3. Redis集群服务器(推荐3台及以上,本文用1台模拟3主3从,生产环境建议独立节点)。

三、第一步:Redis集群部署(Ansible自动化配置)

3.1 目录结构(整合到原Web集群Playbook)

python 复制代码
web_cluster_deploy/
├── inventory.ini        # 新增redis_servers分组
├── site.yml             # 新增Redis角色执行步骤
├── roles/
│   ├── redis/           # 新增Redis集群角色
│   │   ├── tasks/
│   │   │   └── main.yml # Redis集群部署任务
│   │   ├── vars/
│   │   │   └── main.yml # Redis集群变量
│   │   ├── templates/
│   │   │   └── redis.conf.j2 # Redis节点配置模板
│   │   └── handlers/
│   │       └── main.yml # Redis重启触发器
│   ├── common/          # 原公共角色
│   ├── mysql/           # 原MySQL角色
│   ├── php/             # 原PHP角色(后续改造)
│   └── nginx/           # 原Nginx角色

3.2 Inventory配置(inventory.ini)

新增Redis集群节点分组:

ini 复制代码
# 原有分组
[common]
192.168.1.10  # Nginx
192.168.1.20  # PHP1
192.168.1.21  # PHP2
192.168.1.30  # MySQL
192.168.1.40  # Redis集群节点(新增)

[nginx_servers]
192.168.1.10

[php_servers]
192.168.1.20
192.168.1.21

[mysql_servers]
192.168.1.30

# 新增Redis集群分组
[redis_servers]
192.168.1.40

# 全局变量
[all:vars]
ansible_ssh_user=root
ansible_ssh_port=22
ansible_ssh_private_key_file=/root/.ssh/id_rsa

3.3 Redis变量配置(roles/redis/vars/main.yml)

yaml 复制代码
# Redis基础配置
redis_version: "6.2.14"
redis_base_dir: "/data/redis"
redis_ports: [7000, 7001, 7002, 7003, 7004, 7005] # 3主3从端口
redis_password: "Redis@123456"
redis_cluster_replicas: 1 # 每个主节点1个从节点
redis_max_memory: "4gb"   # 单节点内存限制
redis_max_memory_policy: "allkeys-lru" # 内存淘汰策略

3.4 Redis配置模板(roles/redis/templates/redis.conf.j2)

ini 复制代码
# 基础配置
port {{ port }}
daemonize yes
pidfile /var/run/redis-{{ port }}.pid
logfile {{ redis_base_dir }}/{{ port }}/redis.log
dir {{ redis_base_dir }}/{{ port }}

# 集群配置
cluster-enabled yes
cluster-config-file nodes-{{ port }}.conf
cluster-node-timeout 5000
cluster-announce-ip {{ ansible_default_ipv4.address }}
cluster-announce-port {{ port }}
cluster-announce-bus-port {{ port + 10000 }}

# 网络配置
bind 0.0.0.0
protected-mode no
requirepass {{ redis_password }}
masterauth {{ redis_password }}

# 内存配置
maxmemory {{ redis_max_memory }}
maxmemory-policy {{ redis_max_memory_policy }}

# 持久化配置(生产环境必开)
rdbcompression yes
rdbchecksum yes
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec

3.5 Redis部署任务(roles/redis/tasks/main.yml)

yaml 复制代码
# 任务1:安装Redis依赖
- name: 安装Redis编译依赖
  yum:
    name:
      - gcc
      - gcc-c++
      - make
      - wget
    state: present

# 任务2:下载并解压Redis源码
- name: 下载Redis {{ redis_version }}
  get_url:
    url: https://download.redis.io/releases/redis-{{ redis_version }}.tar.gz
    dest: /tmp/redis-{{ redis_version }}.tar.gz
    mode: 0644

- name: 解压Redis源码
  unarchive:
    src: /tmp/redis-{{ redis_version }}.tar.gz
    dest: /tmp/
    remote_src: yes

# 任务3:编译安装Redis
- name: 编译Redis
  command: make MALLOC=libc
  args:
    chdir: /tmp/redis-{{ redis_version }}
  changed_when: false

- name: 安装Redis
  command: make install
  args:
    chdir: /tmp/redis-{{ redis_version }}
  changed_when: false

# 任务4:创建Redis节点目录
- name: 创建Redis节点目录
  file:
    path: "{{ redis_base_dir }}/{{ item }}"
    state: directory
    mode: 0755
    recurse: yes
  loop: "{{ redis_ports }}"

# 任务5:生成每个节点的配置文件
- name: 部署Redis节点配置文件
  template:
    src: templates/redis.conf.j2
    dest: "{{ redis_base_dir }}/{{ item }}/redis.conf"
    mode: 0644
  loop: "{{ redis_ports }}"
  vars:
    port: "{{ item }}"
  notify: restart redis

# 任务6:启动所有Redis节点
- name: 启动Redis节点
  command: redis-server {{ redis_base_dir }}/{{ item }}/redis.conf
  loop: "{{ redis_ports }}"
  changed_when: false

# 任务7:创建Redis集群(需redis-cli支持--cluster参数)
- name: 创建Redis Cluster集群
  command: >
    redis-cli -a {{ redis_password }} --cluster create
    {% for port in redis_ports %}
    {{ ansible_default_ipv4.address }}:{{ port }} {% endfor %}
    --cluster-replicas {{ redis_cluster_replicas }}
    --cluster-yes
  args:
    chdir: /usr/local/bin
  register: cluster_create
  changed_when: "'OK' in cluster_create.stdout"

# 任务8:开放Redis端口(集群节点通信)
- name: 开放Redis端口
  firewalld:
    port: "{{ item }}/tcp"
    zone: public
    permanent: yes
    state: enabled
  loop: "{{ redis_ports + [7000+10000, 7001+10000, 7002+10000, 7003+10000, 7004+10000, 7005+10000] }}"
  notify: restart firewalld

3.6 Redis触发器(roles/redis/handlers/main.yml)

yaml 复制代码
# 重启单个Redis节点(生产环境集群不建议批量重启)
- name: restart redis
  command: >
    redis-cli -a {{ redis_password }} -p {{ item }} shutdown &&
    redis-server {{ redis_base_dir }}/{{ item }}/redis.conf
  loop: "{{ redis_ports }}"
  changed_when: false

- name: restart firewalld
  service:
    name: firewalld
    state: restarted

3.7 主Playbook新增Redis执行步骤(site.yml)

在公共配置后、MySQL部署前执行Redis集群部署:

yaml 复制代码
- name: 执行所有主机公共配置
  hosts: common
  roles:
    - common

# 新增Redis集群部署步骤
- name: 部署Redis集群服务器
  hosts: redis_servers
  roles:
    - redis

- name: 部署MySQL数据库服务器
  hosts: mysql_servers
  roles:
    - mysql

- name: 部署PHP应用服务器
  hosts: php_servers
  roles:
    - php

- name: 部署Nginx反向代理服务器
  hosts: nginx_servers
  roles:
    - nginx

# 原有健康检查步骤...

3.8 执行Redis集群部署

bash 复制代码
# 干跑验证
ansible-playbook -i inventory.ini site.yml --check -v --tags redis

# 实际部署
ansible-playbook -i inventory.ini site.yml -v --tags redis

3.9 验证Redis集群

在Redis节点执行:

bash 复制代码
# 登录任意节点
redis-cli -h 192.168.1.40 -p 7000 -a Redis@123456
# 查看集群状态
192.168.1.40:7000> cluster info
# 查看节点信息
192.168.1.40:7000> cluster nodes

输出包含cluster_state:ok即为集群部署成功。

四、第二步:Web应用节点集成Redis集群

4.1 PHP应用节点改造(以PHP为例,Java同理)

4.1.1 安装PHP Redis扩展(Ansible自动化)

修改roles/php/tasks/main.yml,新增扩展安装任务:

yaml 复制代码
# 原有PHP安装任务...

# 新增:安装phpredis扩展(支持集群)
- name: 安装phpredis扩展依赖
  yum:
    name:
      - php-devel
      - gcc
      - make
    state: present

- name: 安装phpredis扩展
  pecl:
    name: redis
    state: present

- name: 启用phpredis扩展
  lineinfile:
    path: /etc/php.ini
    line: "extension=redis.so"
    regexp: "^extension=redis.so"
    state: present
  notify: restart php-fpm

4.1.2 PHP应用配置Redis集群(模板化)

  1. 创建Redis配置模板roles/php/templates/redis_config.php.j2
php 复制代码
<?php
// Redis集群配置
$redis_cluster_nodes = [
    {% for host in groups['redis_servers'] %}
    "{{ host }}:7000",
    "{{ host }}:7001",
    "{{ host }}:7002",
    {% endfor %}
];
$redis_password = "{{ hostvars[groups['redis_servers'][0]]['redis_password'] }}";
$redis_timeout = 3;
$redis_retry_interval = 100;

// 初始化Redis集群客户端
function get_redis_client() {
    global $redis_cluster_nodes, $redis_password, $redis_timeout, $redis_retry_interval;
    $redis = new RedisCluster(null, $redis_cluster_nodes, $redis_timeout, $redis_timeout, false, $redis_password);
    $redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_ERROR);
    return $redis;
}
?>
  1. 修改PHP应用部署任务,添加配置文件分发:
yaml 复制代码
# roles/php/tasks/main.yml 新增
- name: 部署Redis配置文件
  template:
    src: templates/redis_config.php.j2
    dest: "{{ app_deploy_path }}/redis_config.php"
    mode: 0644
    owner: app
    group: app

4.1.3 PHP业务代码集成缓存逻辑

修改应用核心业务文件(如product_service.php),实现缓存+DB双查:

php 复制代码
<?php
require_once __DIR__ . '/redis_config.php';

class ProductService {
    private $redis;
    private $cachePrefix = "product:";
    private $cacheExpire = 1800; // 30分钟

    public function __construct() {
        $this->redis = get_redis_client();
    }

    /**
     * 获取商品信息(缓存优先)
     */
    public function getProductById($productId) {
        $cacheKey = $this->cachePrefix . $productId;
        
        // 1. 查缓存
        try {
            $productJson = $this->redis->get($cacheKey);
            if ($productJson) {
                if ($productJson === "null") { // 空值缓存,防止穿透
                    return null;
                }
                return json_decode($productJson, true);
            }
        } catch (Exception $e) {
            // Redis集群异常,降级直接查DB
            error_log("Redis查询异常:" . $e->getMessage());
        }
        
        // 2. 查DB
        $product = $this->queryProductFromDB($productId);
        
        // 3. 存入缓存(Redis正常时)
        if ($product) {
            try {
                $this->redis->setex($cacheKey, $this->cacheExpire, json_encode($product));
            } catch (Exception $e) {
                error_log("Redis写入异常:" . $e->getMessage());
            }
        } else {
            // 空值缓存,5分钟过期
            try {
                $this->redis->setex($cacheKey, 300, "null");
            } catch (Exception $e) {
                error_log("Redis空值缓存写入异常:" . $e->getMessage());
            }
        }
        
        return $product;
    }

    /**
     * 更新商品信息(更新DB+删除缓存)
     */
    public function updateProduct($product) {
        // 1. 更新DB
        $this->updateProductToDB($product);
        
        // 2. 删除缓存(避免数据不一致)
        $cacheKey = $this->cachePrefix . $product['id'];
        try {
            $this->redis->del($cacheKey);
        } catch (Exception $e) {
            error_log("Redis删除缓存异常:" . $e->getMessage());
        }
    }

    // 模拟DB操作...
    private function queryProductFromDB($productId) {
        // 实际替换为MySQL查询
        return [
            'id' => $productId,
            'name' => '集群测试商品' . $productId,
            'price' => 199.9
        ];
    }

    private function updateProductToDB($product) {
        // 实际替换为MySQL更新
        error_log("更新商品:" . $product['id']);
    }
}

// 测试
$service = new ProductService();
$product = $service->getProductById(1001);
print_r($product);
?>

4.2 Java应用节点改造(补充)

若Web集群是Java(Spring Boot),核心改造如下:

  1. 新增Redis集群依赖(pom.xml):
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
  1. 配置Redis集群(application.yml):
yaml 复制代码
spring:
  redis:
    cluster:
      nodes: 192.168.1.40:7000,192.168.1.40:7001,192.168.1.40:7002
      max-redirects: 3
    password: Redis@123456
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 5
  1. 缓存逻辑封装(同单节点,Spring Cache注解或RedisTemplate):
java 复制代码
@Service
public class ProductService {
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    // 缓存逻辑同单节点,RedisTemplate自动适配集群
    // ...
}

五、第三步:Nginx层缓存优化(可选)

对于静态资源(如图片、CSS、JS),可在Nginx层集成Redis缓存,减少后端请求:

5.1 安装Nginx Redis模块

bash 复制代码
# 在Nginx节点执行(Ansible任务可封装到nginx角色)
yum install -y nginx-mod-http-redis2

5.2 修改Nginx配置(roles/nginx/templates/nginx.conf.j2)

nginx 复制代码
http {
    # 原有配置...
    
    # Redis集群配置
    upstream redis_cluster {
        server 192.168.1.40:7000;
        server 192.168.1.40:7001;
        server 192.168.1.40:7002;
    }

    server {
        # 原有配置...
        
        # 静态资源Redis缓存
        location ~* \.(jpg|png|css|js)$ {
            # 先查Redis缓存
            set $redis_key "static:$uri";
            redis2_query get $redis_key;
            redis2_pass redis_cluster;
            
            # 缓存未命中,读取本地文件并写入Redis
            error_page 404 = @static_file;
            expires 30d;
        }

        location @static_file {
            root {{ static_resource_path }};
            # 写入Redis缓存(1天过期)
            add_header X-Cache "MISS";
            post_action @write_redis;
        }

        location @write_redis {
            internal;
            set $redis_key "static:$uri";
            redis2_query setex $redis_key 86400 $request_body;
            redis2_pass redis_cluster;
        }
    }
}

六、第四步:集群缓存验证与优化

6.1 功能验证

  1. 缓存读写验证 : 访问PHP应用接口http://192.168.1.10/product/1001,首次访问查DB并写入Redis,二次访问直接读Redis:

    bash 复制代码
    # 在Redis节点查看缓存
    redis-cli -h 192.168.1.40 -p 7000 -a Redis@123456 get product:1001
  2. 集群高可用验证: 停止其中一个Redis节点(如7000),应用仍能正常访问缓存:

    bash 复制代码
    redis-cli -h 192.168.1.40 -p 7000 -a Redis@123456 shutdown
    # 再次访问接口,仍能返回数据
  3. 负载均衡验证: 查看Nginx访问日志,确认请求分发到不同PHP节点,且所有PHP节点共用同一Redis集群:

    bash 复制代码
    tail -f /var/log/nginx/access.log

6.2 性能优化

  1. 缓存命中率优化

    • 核心业务Key设置合理过期时间(30分钟~24小时);

    • 热点Key永不过期,通过定时任务更新;

    • 监控命中率(目标≥95%):

      bash 复制代码
      redis-cli -h 192.168.1.40 -p 7000 -a Redis@123456 info stats | grep keyspace_hits
  2. Redis集群优化

    • 生产环境Redis节点与应用节点物理隔离,避免资源竞争;

    • 开启Redis慢查询日志,优化大Key操作:

      ini 复制代码
      # redis.conf添加
      slowlog-log-slower-than 10000 # 记录10ms以上操作
      slowlog-max-len 1000
  3. 应用端优化

    • 添加Redis异常降级逻辑(Redis集群故障时直接查DB);

    • 批量操作使用pipeline减少网络开销(PHP示例):

      php 复制代码
      // 批量查询商品
      public function getProductsByIds($productIds) {
          $cacheKeys = array_map(function($id) {
              return $this->cachePrefix . $id;
          }, $productIds);
          
          // 批量查缓存
          $cacheResults = $this->redis->mget($cacheKeys);
          // 未命中的查DB,批量写入缓存
          // ...
      }

6.3 监控配置(Prometheus+Grafana)

  1. 部署Redis Exporter:

    bash 复制代码
    # Ansible任务封装
    docker run -d --name redis-exporter \
      -p 9121:9121 \
      -e REDIS_EXPORTER_PASSWORD={{ redis_password }} \
      -e REDIS_EXPORTER_CLUSTER=yes \
      oliver006/redis_exporter --redis.addr=192.168.1.40:7000
  2. Grafana导入Redis集群监控模板(ID:10750),监控指标:

    • 集群节点状态、内存使用率;
    • 缓存命中率、读写QPS;
    • 过期Key数、慢查询数。
相关推荐
YDS82910 小时前
大营销平台 —— 活动SKU库存扣减业务及其一致性处理
java·spring boot·redis·rabbitmq·ddd
爬也要爬着前进1 天前
redis主从搭建
数据库·redis·缓存
用户095367515831 天前
在系统的学习 redis 前的疑惑
redis·go
吴声子夜歌1 天前
Redis 3.x——集群故障转移
java·数据库·redis·集群
明豆1 天前
Redis 分布式缓存生产实战
redis·分布式·缓存
海兰1 天前
【高速缓存】RedisVL 指南:从向量搜索到 AI 应用落地
人工智能·redis
weixin_727535621 天前
双Token认证体系深度拆解:Spring Security + JWT + Redis
redis·spring·wpf
William Dawson1 天前
【华为云 MRS Redis 安全集群 Kerberos 认证接入实战(Spring Boot \+ Jedis)】
redis·安全·华为云
zhouhui0011 天前
AI帮我写了个Spring Boot校验,线上漏掉了这组边界条件
java·spring boot·redis·ai编程
吃饱了得干活1 天前
扫码登录如何实现?从原理到实战
java·spring boot·redis