主从模式
哨兵模式
Cluster模式
主从模式和哨兵模式暂不介绍,主要介绍 cluster 模式
前置准备工作
拉取镜像
docker pull redis:7
创建网络
docker network create -d bridge my-net
查看创建得网络列表 docker network ls
查看网络详细信息 docker network inspect my-net
准备 redis.conf 文件
一个容器准备一个配置文件,这里用脚本创建,直接运行如下命令即可
for port in $(seq 6380 6385);
do
mkdir -p /home/shuxiaoyuan/docker/redis/node-${port}/conf
touch /home/shuxiaoyuan/docker/redis/node-${port}/conf/redis.conf
cat << EOF > /home/shuxiaoyuan/docker/redis/node-${port}/conf/redis.conf
# 节点端口
port ${port}
# 密码
requirepass 123456
# 保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
protected-mode no
# 是否以守护线程的方式启动(后台启动),默认 no;
daemonize no
# 是否开启 AOF 持久化模式,默认 no;
appendonly yes
# 是否开启集群模式,默认 no;
cluster-enabled yes
# 集群节点信息文件,自动在/data目录中生成
cluster-config-file nodes.conf
# 集群节点连接超时时间;
cluster-node-timeout 5000
EOF
done
批量重置脚本
当发生错误的时候,批量停止、删除容器并清理相关文件,方便重来
停止容器,删除容器,删除容器产生的数据文件
for port in $(seq 6380 6385);
do
docker stop redis-${port}
docker rm redis-${port}
sudo rm -rf /home/shuxiaoyuan/docker/redis/node-${port}/data
done
采用 docker compose
# 定义服务,可以多个
services:
# 服务名称
redis-6380:
# 采用 image 指定镜像
image: "redis:7"
# 指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式。
container_name: redis-6380
# 指定容器退出后的重启策略为始终重启。
# 该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped
restart: always
# 配置日志
logging:
# 日志驱动类型:json-file、syslog、none
driver: "json-file"
# 其他选项
options:
max-size: "1g"
# 配置容器连接的网络
network_mode: "my-net"
# 暴露端口信息
ports:
- "6380:6380"
# 数据卷所挂载路径设置
volumes:
- /home/shuxiaoyuan/docker/redis/node-6380/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /home/shuxiaoyuan/docker/redis/node-6380/data:/data
# 覆盖容器启动后默认执行的命令。
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 服务名称
redis-6381:
# 采用 image 指定镜像
image: "redis:7"
# 指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式。
container_name: redis-6381
# 指定容器退出后的重启策略为始终重启。
# 该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped
restart: always
# 配置日志
logging:
# 日志驱动类型:json-file、syslog、none
driver: "json-file"
# 其他选项
options:
max-size: "1g"
# 配置容器连接的网络
network_mode: "my-net"
# 暴露端口信息,使用 host 模式后,这个不需要
ports:
- "6381:6381"
# 数据卷所挂载路径设置
volumes:
- /home/shuxiaoyuan/docker/redis/node-6381/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /home/shuxiaoyuan/docker/redis/node-6381/data:/data
# 覆盖容器启动后默认执行的命令。
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 服务名称
redis-6382:
# 采用 image 指定镜像
image: "redis:7"
# 指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式。
container_name: redis-6382
# 指定容器退出后的重启策略为始终重启。
# 该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped
restart: always
# 配置日志
logging:
# 日志驱动类型:json-file、syslog、none
driver: "json-file"
# 其他选项
options:
max-size: "1g"
# 配置容器连接的网络
network_mode: "my-net"
# 暴露端口信息
ports:
- "6382:6382"
# 数据卷所挂载路径设置
volumes:
- /home/shuxiaoyuan/docker/redis/node-6382/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /home/shuxiaoyuan/docker/redis/node-6382/data:/data
# 覆盖容器启动后默认执行的命令。
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 服务名称
redis-6383:
# 采用 image 指定镜像
image: "redis:7"
# 指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式。
container_name: redis-6383
# 指定容器退出后的重启策略为始终重启。
# 该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped
restart: always
# 配置日志
logging:
# 日志驱动类型:json-file、syslog、none
driver: "json-file"
# 其他选项
options:
max-size: "1g"
# 配置容器连接的网络
network_mode: "my-net"
# 暴露端口信息
ports:
- "6383:6383"
# 数据卷所挂载路径设置
volumes:
- /home/shuxiaoyuan/docker/redis/node-6383/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /home/shuxiaoyuan/docker/redis/node-6383/data:/data
# 覆盖容器启动后默认执行的命令。
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 服务名称
redis-6384:
# 采用 image 指定镜像
image: "redis:7"
# 指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式。
container_name: redis-6384
# 指定容器退出后的重启策略为始终重启。
# 该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped
restart: always
# 配置日志
logging:
# 日志驱动类型:json-file、syslog、none
driver: "json-file"
# 其他选项
options:
max-size: "1g"
# 配置容器连接的网络
network_mode: "my-net"
# 暴露端口信息
ports:
- "6384:6384"
# 数据卷所挂载路径设置
volumes:
- /home/shuxiaoyuan/docker/redis/node-6384/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /home/shuxiaoyuan/docker/redis/node-6384/data:/data
# 覆盖容器启动后默认执行的命令。
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 服务名称
redis-6385:
# 采用 image 指定镜像
image: "redis:7"
# 指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式。
container_name: redis-6385
# 指定容器退出后的重启策略为始终重启。
# 该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped
restart: always
# 配置日志
logging:
# 日志驱动类型:json-file、syslog、none
driver: "json-file"
# 其他选项
options:
max-size: "1g"
# 配置容器连接的网络
network_mode: "my-net"
# 暴露端口信息
ports:
- "6385:6385"
# 数据卷所挂载路径设置
volumes:
- /home/shuxiaoyuan/docker/redis/node-6385/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /home/shuxiaoyuan/docker/redis/node-6385/data:/data
# 覆盖容器启动后默认执行的命令。
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
运行 docker compose
# 后台运行
docker compose up -d
# 停止但不删除
docker compose stop
# 删除
docker compose down
进入容器配置集群
docker exec -it redis-6380 bash
# cluster-replicas 1 意思是主从配置 1:1
redis-cli -a 123456 --cluster create redis-6380:6380 redis-6381:6381 redis-6382:6382 redis-6383:6383 redis-6384:6384 redis-6385:6385 --cluster-replicas 1
采用 Docker 方式
在 Windows 上采用docker方式,启动多个容器来模拟集群
脚本批量启动 Redis 容器
for port in $(seq 6380 6385); \
do \
docker run -it -d -p ${port}:${port} -p 1${port}:1${port} \
--restart always \
--mount type=bind,source=/home/shuxiaoyuan/docker/redis/node-${port}/conf/redis.conf,target=/usr/local/etc/redis/redis.conf \
--mount type=bind,source=/home/shuxiaoyuan/docker/redis/node-${port}/data,target=/data \
--name redis-${port} --net my-net \
redis:7 redis-server /usr/local/etc/redis/redis.conf
done
进入容器配置集群
docker exec -it redis-6380 bash
# cluster-replicas 1 意思是主从配置 1:1
redis-cli -a 123456 --cluster create redis-6380:6380 redis-6381:6381 redis-6382:6382 redis-6383:6383 redis-6384:6384 redis-6385:6385 --cluster-replicas 1
连接 Redis 查看信息
redis-cli -c -p 6380 -a 123456
cluster info
cluster nodes
测试数据
可以看到,每次 set 操作的时候,会将相应的键分配到对应的卡槽,然后自动转到相应的机器上,get 的时候,同理
Windows下安装多个 Redis 实例
去 GitHub
上下载 Windows 版的 Redis(3.2.100 的 zip 版),点击此处
创建多个 Redis 实例
根据你需要启动的实例个数,创建相应的 conf
和 log
文件,文件名可以随便取,不过为了更加直观,这里统一采用带端口的文件名形式取名
conf
文件内容,根据你的端口,将配置文件中的端口号都替换掉
port 6380
loglevel notice
logfile "E:/Redis-x64-3.2.100/logs/redis6380_log.txt"
appendonly yes
appendfilename "appendonly.6380.aof"
cluster-enabled yes
cluster-config-file nodes.6380.conf
cluster-node-timeout 15000
cluster-slave-validity-factor 10
cluster-migration-barrier 1
cluster-require-full-coverage yes
启动多个 Redis 服务
安装 Redis 系统服务 redis-server.exe --service-install redis.6380.conf --service-name redis6380
查看 Redis 服务,可以在此配置手动启动或者自动启动等
连接 Redis
删除 Redis 服务
运行 regedit
打开注册表编辑器
找到:计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\
找到对应的 Redis 服务,右键删除即可
下载并安装Ruby
下载 Ruby
:http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.2.4-x64.exe
其他版本:http://dl.bintray.com/oneclick/rubyinstaller/
安装:略
安装 gem 驱动
下载 Ruby
环境下 Redis
的驱动:确保 gem
命令可用
redis-3.2.2.gem
下载地址(https://rubygems.org/downloads/redis-3.2.2.gem)
安装,下图的 --local 后面跟下载好的 gem 文件路径,自行替换即可
搭建集群
下载 Redis 官方提供的创建 Redis集群
的 ruby 脚本文件 redis-trib.rb
下载地址:https://raw.githubusercontent.com/MSOpenTech/redis/3.0/src/redis-trib.rb
如果无法下载,本文最后有附录,如果打开后是一个页面,就将页面上的内容全部不保存到本地
搭建 redis-trib.rb create --replicas 0 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
测试
在 127.0.0.1:6380
中添加一个 key,刷新其他实例,可以发现,该 key 已同步过来
使用一个实例的不同数据库模拟
<?php
/**
* 请提供简单说明
*
* User: 舒孝元
* Date: 2020/8/19 15:01
* Mail: sxy@shuxiaoyuan.com
* Website: https://www.shuxiaoyuan.com
*/
namespace App\Common;
class RedisHa {
protected static $name = 'redis_ha';
protected static $connections = [];
protected static function init() {
$hosts = explode(',', env('REDIS_HA_HOSTS'));
$databases = explode(',', env('REDIS_HA_DATABASES'));
foreach ($hosts as $index => $host) {
$redisManage = new \Illuminate\Redis\RedisManager('redis-cluster',config('database.redis.client'), [
'default' => [
'host' => $host,
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => $databases[$index],
]
]);
self::$connections[$index] = $redisManage->connection();
}
}
public static function count() {
if (empty(self::$connections)) {
self::init();
}
return count(self::$connections);
}
public static function connection($index) {
if (empty(self::$connections)) {
self::init();
}
if (!isset(self::$connections[$index]))
throw new Exception('未找到redis连接' . $index);
return self::$connections[$index];
}
public static function __callStatic($method, $parameters) {
if (empty(self::$connections)) {
self::init();
}
$result = [];
foreach (self::$connections as $connection) {
$result[] = $connection->{$method}(...$parameters);
}
return $result;
}
}
简单分配逻辑
/**
* 根据 openID 分配 Redis
*/
private function type4() {
for ($i = 0; $i < 10000; $i++) {
$openid = str_random(28);
$openid_crc32 = crc32($openid);
$redis_id = (int)$openid_crc32 % count(explode(',', env('REDIS_HA_HOSTS')));
$redis = RedisHa::connection($redis_id);
$redis->set('test:' . $openid, json_encode(['openid' => $openid, 'redis_id' => $redis_id]));
$redis->incr('number');
}
return 'success';
}