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数、慢查询数。
相关推荐
图南随笔16 小时前
Spring Boot(二十三):RedisTemplate的Set和Sorted Set类型操作
java·spring boot·redis·后端·缓存
何中应16 小时前
Redis的两个小错误
数据库·redis·缓存
Overt0p17 小时前
抽奖系统(6)
java·spring boot·redis·设计模式·rabbitmq·状态模式
乐观主义现代人17 小时前
redis 源码学习笔记
redis·笔记·学习
jmxwzy17 小时前
Redis
数据库·redis·缓存
零叹17 小时前
Redis热Key——大厂是怎么解决的
数据库·redis·缓存·热key
王五周八17 小时前
基于 Redis+Redisson 实现分布式高可用编码生成器
数据库·redis·分布式
win x18 小时前
Redis事务
数据库·redis·缓存
论迹18 小时前
【Redis】-- 单线程模型
数据库·redis·缓存