笔记 笔记
首页
  • 开发工具
  • Java Web
  • Java 进阶
  • 容器化技术
  • Java 专栏

    • Java 核心技术面试精讲
    • Java 业务开发常见错误 100 例
  • 数据库专栏

    • MySQL 实战 45 讲
    • Redis 核心技术与实战
  • 安全专栏

    • OAuth 2.0 实战课
  • 计算机系统
  • 程序设计语言
  • 数据结构
  • 知识产权
  • 数据库
  • 面向对象
  • UML
  • 设计模式
  • 操作系统
  • 结构化开发
  • 软件工程
  • 计算机网络
  • 上午题错题
在线工具 (opens new window)

EasT-Duan

Java 开发
首页
  • 开发工具
  • Java Web
  • Java 进阶
  • 容器化技术
  • Java 专栏

    • Java 核心技术面试精讲
    • Java 业务开发常见错误 100 例
  • 数据库专栏

    • MySQL 实战 45 讲
    • Redis 核心技术与实战
  • 安全专栏

    • OAuth 2.0 实战课
  • 计算机系统
  • 程序设计语言
  • 数据结构
  • 知识产权
  • 数据库
  • 面向对象
  • UML
  • 设计模式
  • 操作系统
  • 结构化开发
  • 软件工程
  • 计算机网络
  • 上午题错题
在线工具 (opens new window)

购买兑换码请添加

添加时候请写好备注,否则无法通过。

  • 设计模式

  • JVM 详解

  • Linux

  • Redis

    • Redis 入门

    • Redis 进阶

      • Redis 事务—锁机制
      • Redis 事务—秒杀案例
      • Redis 持久化—RDB
      • Redis 持久化—AOF
      • Redis 主从复制
        • 是什么
        • 能干嘛
        • 安装
          • 新建 redis6379.conf,填写以下内容
          • 新建 redis6380.conf,填写以下内容
          • 新建 redis6381.conf,填写以下内容
          • 启动三台 Redis 服务器
          • 查看系统进程
          • 查看三台主机运行情况
          • 配从(库)不配主(库)
        • 安装(Docker)
        • 常用 3 招
          • 一主二仆
          • 薪火相传
          • 反客为主
        • 复制原理
        • 哨兵模式(sentinel)
          • 是什么
          • 使用步骤
          • 复制延时
          • 故障恢复
        • 代码修改
      • Redis 集群
      • Redis 应用问题解决
      • Redis6 新功能
  • 分布式锁

  • Shiro

  • Gradle

  • Java 进阶
  • Redis
  • Redis 进阶
EasT-Duan
2024-08-10
目录

Redis 主从复制

欢迎来到我的 ChatGPT 中转站,极具性价比,为付费不方便的朋友提供便利,有需求的可以添加左侧 QQ 二维码,另外,邀请新用户能获取余额哦!最后说一句,那啥:请自觉遵守《生成式人工智能服务管理暂行办法》。

# 是什么

主机数据更新后根据配置和策略, 自动同步到备机的 master/slaver 机制,Master 以写为主,Slave 以读为主。

# 能干嘛

  • 读写分离,性能扩展
  • 容灾快速恢复

# 安装

  1. 拷贝多个 redis.conf 文件 include (写绝对路径)
  2. 开启 daemonize yes
  3. Pid 文件名字 pidfile
  4. 指定端口 port
  5. Log 文件名字
  6. dump.rdb 名字 dbfilename
  7. Appendonly 关掉或者换名字

# 新建 redis6379.conf,填写以下内容

include /root/myredis/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb
1
2
3
4

# 新建 redis6380.conf,填写以下内容

include /root/myredis/redis.conf
pidfile /var/run/redis_6380.pid
port 6380
dbfilename dump6380.rdb
slave-priority 10
slaveof 127.0.0.1 6379
1
2
3
4
5
6

# 新建 redis6381.conf,填写以下内容

include /root/myredis/redis.conf
pidfile /var/run/redis_6381.pid
port 6381
dbfilename dump6381.rdb
slaveof 127.0.0.1 6379
1
2
3
4
5

slave-priority 10

设置从机的优先级,值越小,优先级越高,用于选举主机时使用。默认 100

# 启动三台 Redis 服务器

redis-server ~/myredis/redis6379.conf
redis-server ~/myredis/redis6380.conf
redis-server ~/myredis/redis6381.conf
1
2
3

# 查看系统进程

# 查看三台主机运行情况

info replication :打印主从复制的相关信息。

# 配从 (库) 不配主 (库 **)**

slaveof <ip> <port> :成为某个实例的从服务器。

  1. 在 6380 和 6381 上执行: slaveof 127.0.0.1 6379
  2. 在主机上写,在从机上可以读取数据,无法写入

  1. 主机挂掉,重启就行,一切如初。
  2. 从机重启需重设: slaveof 127.0.0.1 6379

可以将配置增加到文件中。永久生效。

# 安装(Docker)

目录结构如下

.
├── docker-compose.yml
├── master
│   ├── conf
│   │   └── redis.conf
│   ├── data
│   └── logs
├── sentinel
│   ├── sentinel-1
│   │   ├── conf
│   │   │   └── sentinel.conf
│   │   └── data
│   ├── sentinel-2
│   │   ├── conf
│   │   │   └── sentinel.conf
│   │   └── data
│   └── sentinel-3
│       ├── conf
│       │   └── sentinel.conf
│       └── data
├── slave1
│   ├── conf
│   │   └── redis.conf
│   ├── data
│   └── logs
└── slave2
    ├── conf
    │   └── redis.conf
    ├── data
    └── logs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
version: "3.8"

services:
  master:
    image: redis:7.0.5
    container_name: redis-master
    hostname: redis-master
    restart: always
    volumes:
      - ./master/data:/data
      - ./master/conf/redis.conf:/etc/redis/redis.conf
      - ./master/logs:/logs
    ports:
      - 6379:6379
### 这里的slave-announce-ip、slave-announce-port是宿主机IP与端口,不设置的话哨兵模式下访问的是容器内IP端口,不能为外部应用提供服务,原因是由于端口或IP映射导致的无法连接的问题。例如 docker 这种容器,当你使用了端口映射后,运行在 docker 中的 redis 是不知道正真的端口或 IP 的,所以在哨兵模式下,哨兵就无法连接到 master 和 replica。当遇到这种情况后,其实还有另一种解决方式,就是直接使用 docker 的主机网络(启动时加上 --net=host)。
    command: ["redis-server","/etc/redis/redis.conf","--masterauth 111111","--slave-announce-ip 192.168.25.10","--slave-announce-port 6379"]

  slave1:
    image: redis:7.0.5
    container_name: redis-slave-1
    hostname: redis-slave-1
    restart: always
    volumes:
      - ./slave1/data:/data
      - ./slave1/conf/redis.conf:/etc/redis/redis.conf
      - ./slave1/logs:/logs      
    ports:
      - 6380:6380
    command: ["redis-server","/etc/redis/redis.conf","--slaveof 192.168.25.10 6379","--masterauth 111111","--slave-announce-ip 192.168.25.10","--slave-announce-port 6380"]
    depends_on:
      - master

  slave2:
    image: redis:7.0.5
    container_name: redis-slave-2
    hostname: redis-slave-2
    restart: always
    volumes:
      - ./slave2/data:/data
      - ./slave2/conf/redis.conf:/etc/redis/redis.conf
      - ./slave2/logs:/logs          
    ports:
      - 6381:6381
    command: ["redis-server","/etc/redis/redis.conf","--slaveof 192.168.25.10 6379","--masterauth 111111","--slave-announce-ip 192.168.25.10","--slave-announce-port 6381"]
    depends_on:
      - master

  redis-sentinel-1:
    image: redis:7.0.5
    container_name: redis-sentinel-1
    restart: always
    ports:
      - 26379:26379
    volumes:
      # - ./sentinel-1/data:/data
      - ./sentinel/sentinel-1/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf
    command: ["redis-sentinel","/usr/local/etc/redis/sentinel.conf"]
    depends_on:
      - master
      - slave1
      - slave2
  redis-sentinel-2:
    image: redis:7.0.5
    container_name: redis-sentinel-2
    restart: always
    ports:
      - 26380:26380
    volumes:
      # - ./sentinel-2/data:/data
      - ./sentinel/sentinel-2/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf
    command: ["redis-sentinel","/usr/local/etc/redis/sentinel.conf"]
    depends_on:
      - master
      - slave1
      - slave2

  redis-sentinel-3:
    image: redis:7.0.5
    container_name: redis-sentinel-3
    restart: always
    ports:
      - 26381:26381
    volumes:
      # - ./sentinel-3/data:/data
      - ./sentinel/sentinel-3/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf
    command: ["redis-sentinel","/usr/local/etc/redis/sentinel.conf"]
    depends_on:
      - master
      - slave1
      - slave2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#redis.conf
###看清配置项母,牵扯到端口或者IP的请自行修改
#允许外部访问
bind 0.0.0.0
protected-mode yes
#端口
port 6380
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
pidfile /var/run/redis_6380.pid
loglevel notice
logfile ""
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
#指定宿主机的IP和端口
replica-announce-ip 192.168.25.10
replica-announce-port 6380
acllog-max-len 128
#密码
requirepass 111111
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
#RDB持久化
#只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了
save 900 1
#AOF持久化
appendonly yes
appendfilename "appendonly.aof"
appenddirname "appendonlydir"

appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# sentinel.conf
####  根据Docker配置修改端口号或者IP####
#哨兵的端口
port 26379
#工作路径
dir "/data"

protected-mode no
 
# 指明日志文件名,一旦开启需要修改Docker配置中的映射地址,开启后docker中无日志,日在data文件夹中
# logfile "/data/sentinel.log"
 
# master表示 哨兵监控master服务的别名
# 192.168.25.10 表示 master地址
#2 表示只需要2个sentinel投票即可故障转移
sentinel monitor mymaster 192.168.25.10 6379 2
 
#这两项配置非常重要,不同哨兵的节点通信的地址
sentinel announce-ip 192.168.25.10
sentinel announce-port 26379
 
# 用于验证主节点和副本的密码
sentinel auth-pass mymaster 111111

sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 常用 3 招

# 一主二仆

  • 切入点问题?slave1、slave2 是从头开始复制还是从切入点开始复制?比如从 k4 进来,那之前的 k1,k2,k3 是否也可以复制?从机是否可以写?set 可否?
    • 能复制。
    • 不可

主机 shutdown 后情况如何?从机是上位还是原地待命?

主机又回来了后,主机新增记录,从机还能否顺利复制?

其中一台从机 down 后情况如何?依照原有它能跟上大部队吗?

# 薪火相传

上一个 Slave 可以是下一个 slave 的 Master,Slave 同样可以接收其他 slaves 的连接和同步请求,那么该 slave 作为了链条中下一个的 master,可以有效减轻 master 的写压力,去中心化降低风险。

用 slaveof <ip> <port>

中途变更转向:会清除之前的数据,重新建立拷贝最新的。

风险是一旦某个 slave 宕机,后面的 slave 都没法备份。主机挂了,从机还是从机,无法写数据了。

# 反客为主

当一个 master 宕机后,后面的 slave 可以立刻升为 master,其后面的 slave 不用做任何修改。

# 复制原理

  • Slave 启动成功连接到 master 后会发送一个 sync 命令。
  • Master 接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master 将传送整个数据文件到 slave,以完成一次完全同步。
  • 全量复制:而 slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中。
  • 增量复制:Master 继续将新的所有收集到的修改命令依次传给 slave,完成同步。
  • 但是只要是重新连接 master,一次完全同步(全量复制)将被自动执行。

# 哨兵模式(sentinel)

# 是什么

反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

# 使用步骤

  1. 调整为一主二仆模式,6379 带着 6380、6381。

  2. 自定义的 myredis 目录下新建 sentinel.conf 文件,名字绝不能错。

  3. 配置哨兵,填写内容。

    sentinel monitor mymaster 127.0.0.1 6379 1
    #其中mymaster为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。 
    
    1
    2
  4. 启动哨兵

    /usr/local/bin/redis-sentinel ~/myredis/sentinel.conf 
    
    1

  5. 当主机挂掉,从机选举中产生新的主机 (大概 10 秒左右可以看到哨兵窗口日志,切换了新的主机),哪个从机会被选举为主机呢?根据优先级别:slave-priority ,原主机重启后会变为从机。

  6. 通过图形化工具连接 26379 端口即可访问 sentinel

# 复制延时

由于所有的写操作都是先在 Master 上操作,然后同步更新到 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave 机器数量的增加也会使这个问题更加严重。

# 故障恢复

优先级在 redis.conf 中默认:slave-priority 100,值越小优先级越高,偏移量是指获得原主机数据最全的,每个 redis 实例启动后都会随机生成一个 40 位的 runid。

# 代码修改

// 在修改代码之前需要将sentinel.conf中的IP修改为真实IP,sentinel monitor mymaster 192.168.25.100 6381 1
// 否则会一直抛出Reason: redis.clients.jedis.exceptions.JedisConnectionException
public class SentinelJedisPool {

    private static JedisSentinelPool pool = null;

    //可用连接实例的最大数目,默认为8;
    //如果赋值为-1,则表示不限制,如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)
    private static Integer MAX_TOTAL = 1024;
    //控制一个pool最多有多少个状态为idle(空闲)的jedis实例,默认值是8
    private static Integer MAX_IDLE = 200;
    //等待可用连接的最大时间,单位是毫秒,默认值为-1,表示永不超时。
    //如果超过等待时间,则直接抛出JedisConnectionException
    private static Integer MAX_WAIT_MILLIS = 10000;
    //客户端超时时间配置
    private static Integer TIMEOUT = 10000;
    //在borrow(用)一个jedis实例时,是否提前进行validate(验证)操作;
    //如果为true,则得到的jedis实例均是可用的
    private static Boolean TEST_ON_BORROW = true;
    //在空闲时检查有效性, 默认false
    private static Boolean TEST_WHILE_IDLE = true;
    //是否进行有效性检查
    private static Boolean TEST_ON_RETURN = true;

    /**
     * 创建连接池
     */
    private static void createJedisPool() {
        JedisPoolConfig config = new JedisPoolConfig();
        /*注意:
            在高版本的jedis jar包,比如本版本2.9.0,JedisPoolConfig没有setMaxActive和setMaxWait属性了
            这是因为高版本中官方废弃了此方法,用以下两个属性替换。
            maxActive  ==>  maxTotal
            maxWait==>  maxWaitMillis
         */
        config.setMaxTotal(MAX_TOTAL);
        config.setMaxIdle(MAX_IDLE);
        config.setMaxWaitMillis(MAX_WAIT_MILLIS);
        config.setTestOnBorrow(TEST_ON_BORROW);
        config.setTestWhileIdle(TEST_WHILE_IDLE);
        config.setTestOnReturn(TEST_ON_RETURN);
        String masterName = "mymaster";
        Set<String> sentinels = new HashSet<String>();
        sentinels.add(new HostAndPort("192.168.25.100", 26379).toString());
//        String password = "1234@abcd";
//        pool = new JedisSentinelPool(masterName, sentinels, config, TIMEOUT,password);
        pool = new JedisSentinelPool(masterName, sentinels, config, TIMEOUT);
    }

    private static synchronized void poolInit() {
        if (pool == null)
            createJedisPool();
    }


    /**
     * 获取一个redis对象
     * @return
     */
    public static Jedis getJedis() {
        if (pool == null)
            poolInit();
        return pool.getResource();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#Redis
上次更新: 2025/04/12, 05:37:39
Redis 持久化—AOF
Redis 集群

← Redis 持久化—AOF Redis 集群→

最近更新
01
Reactor 核心
02-24
02
前置条件
10-30
03
计算机网络
09-13
更多文章>
Theme by Vdoing | Copyright © 2019-2025 powered by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式