You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel中配置Redis主从复制并实现读写分离?

Laravel Redis主从复制(读写分离+Sticky)配置方案

一、修正主从配置(解决读操作全走主节点问题)

你当前的配置存在两个核心问题:

  1. options中的cluster设为redis是针对Redis原生分片集群的,不适用于主从复制场景;
  2. replicas的结构不符合Predis主从模式的识别要求。

替换成以下配置:

// config/database.php
'redis' => [
    'client' => 'predis',

    // 主节点配置
    'default' => [
        'host' => '127.0.0.1',
        'password' => env('REDIS_PASSWORD', null),
        'port' => 6379,
        'database' => 0,
    ],

    // 从节点集群(自动负载均衡)
    'replicas' => [
        'cluster' => true,
        'connections' => [
            [
                'host' => '127.0.0.1',
                'password' => env('REDIS_PASSWORD', null),
                'port' => 6380,
                'database' => 0,
            ],
            [
                'host' => '127.0.0.1',
                'password' => env('REDIS_PASSWORD', null),
                'port' => 6381,
                'database' => 0,
            ],
        ],
    ],

    'options' => [
        'replication' => true,
        'cluster' => 'predis', // 使用Predis的集群模式处理主从路由
    ],
],

配置说明:

  • replication => true开启Predis的主从复制路由功能,自动将SET/HSET/INCR等写命令发往主节点,GET/HGET等读命令发往从节点;
  • replicas下的connections定义从节点列表,Predis会自动在从节点间做轮询负载均衡;
  • cluster => 'predis'指定用Predis内置的集群逻辑,而非Redis原生集群。

二、验证读写分离是否生效

可以通过打印当前连接的端口,或者查看Redis节点日志来验证:

// 写操作(强制走主节点6379)
Redis::connection('default')->set('test_key', 'hello');

// 读操作(自动路由到6380/6381)
$value = Redis::connection('replicas')->get('test_key');

// 打印当前读连接的端口,确认是否为从节点端口
$connection = Redis::connection('replicas');
echo $connection->getConnection()->getParameters()->port;

如果不想每次手动指定连接,也可以自定义一个Facade,自动路由读写命令:

// app/Facades/RedisDB.php
namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class RedisDB extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'redis.db';
    }
}

// app/Providers/AppServiceProvider.php
public function register()
{
    $this->app->bind('redis.db', function ($app) {
        return new class {
            public function __call($method, $arguments)
            {
                // 判断是否为写命令
                $writeCommands = ['set', 'hset', 'incr', 'decr', 'del', 'flushdb'];
                if (in_array(strtolower($method), $writeCommands)) {
                    return app('redis')->connection('default')->$method(...$arguments);
                }
                return app('redis')->connection('replicas')->$method(...$arguments);
            }
        };
    });
}

使用时直接调用RedisDB::get('key')或RedisDB::set('key', 'value')即可自动路由。

三、实现Sticky功能(请求内写后立即读)

Laravel Redis原生没有sticky选项,但可以通过请求生命周期内的标记实现:

  1. 创建中间件RedisStickyMiddleware:
// app/Http/Middleware/RedisStickyMiddleware.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Redis;

class RedisStickyMiddleware
{
    public function handle($request, Closure $next)
    {
        // 初始化标记:当前请求是否执行过写操作
        $request->attributes->set('redis_has_write', false);

        // 监听所有Redis写命令,触发时标记为已写
        $originalSet = Redis::getFacadeRoot()->set;
        Redis::macro('set', function ($key, $value, $options = null) use ($originalSet, $request) {
            $request->attributes->set('redis_has_write', true);
            return $originalSet($key, $value, $options);
        });

        // 同理,覆盖其他写命令(hset/incr/del等)
        $originalHset = Redis::getFacadeRoot()->hset;
        Redis::macro('hset', function ($key, $field, $value) use ($originalHset, $request) {
            $request->attributes->set('redis_has_write', true);
            return $originalHset($key, $field, $value);
        });

        // 重写读命令,根据标记决定走主还是从
        $originalGet = Redis::getFacadeRoot()->get;
        Redis::macro('get', function ($key) use ($originalGet, $request) {
            if ($request->attributes->get('redis_has_write')) {
                return Redis::connection('default')->get($key);
            }
            return $originalGet($key);
        });

        $response = $next($request);

        // 请求结束后清理宏定义
        Redis::forgetMacro('set');
        Redis::forgetMacro('hset');
        Redis::forgetMacro('get');

        return $response;
    }
}
  1. 在app/Http/Kernel.php中注册中间件:
protected $middlewareGroups = [
    'web' => [
        // ...其他中间件
        \App\Http\Middleware\RedisStickyMiddleware::class,
    ],
];

这样,在同一个请求内执行写操作后,后续所有读操作都会强制走主节点,避免主从延迟导致的读不到最新数据问题。

四、常见问题排查

  • 确保从节点已正确配置主从复制(执行SLAVEOF 127.0.0.1 6379并确认同步正常);
  • 清除Laravel配置缓存:执行php artisan config:clear;
  • 检查Predis版本,建议使用1.x版本(Laravel 8及以下兼容更好)。

内容的提问来源于stack exchange,提问作者LF-DevJourney

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 08:25:59