golang[101]-redis

Redis is an “advanced key-value store”

Redis Data Types

1
2
3
4
5
Strings
Lists
Sets
Sorted/Scored Sets
Hashes

type

1
2
3
4
5
 string:                 page:index.html   -> <html><head>[...]   login_count -> 7464
set: users_logged_in_today -> { 1, 2, 3, 4, 5 }
List: latest_post_ids -> [201, 204, 209,..]
Hashes user:123:session : time => 10927353 username => joe
Sorted Set joe ~ 1.3483 bert ~ 93.4 fred ~ 283.22 chris ~ 23774.17

golang redis helloworld

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"github.com/go-redis/redis"
)

func main() {
fmt.Println("Go Redis Tutorial")

client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})

pong, err := client.Ping().Result()
fmt.Println(pong, err)

}

使用 Docker 配置 Redis 主从复制

配置 master 节点
编辑 redis-master.conf,修改下列配置:

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
# 监听来自外部的连接
bind 0.0.0.0

# 启用保护模式
# 即在没有使用bind指令绑定具体地址时
# 或在没有设定密码时
# Redis将拒绝来自外部的连接
protected-mode yes

# 监听端口
port 6379

# 启动时不打印logo
# 这个不重要,想看logo就打开它
always-show-logo no

# 设定密码认证
requirepass redis

# 禁用KEYS命令
# 一方面 KEYS * 命令可以列出所有的键,会影响数据安全
# 另一方面 KEYS 命令会阻塞数据库,在数据库中存储了大量数据时,该命令会消耗很长时间
# 期间对Redis的访问也会被阻塞,而当锁释放的一瞬间,大量请求涌入Redis,会造成Redis直接崩溃
rename-command KEYS ""

# 此外还应禁止 FLUSHALL 和 FLUSHDB 命令
# 这两个命令会清空数据,并且不会失败

配置 slave 节点
创建 redis-slave1.conf,修改下列配置:

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
# 监听来自外部的连接
bind 0.0.0.0

# 启用保护模式
# 即在没有使用bind指令绑定具体地址时
# 或在没有设定密码时
# Redis将拒绝来自外部的连接
protected-mode yes

# 监听端口
port 6380

# 启动时不打印logo
# 这个不重要,想看logo就打开它
always-show-logo no

# 设定密码认证
requirepass redis

# 禁用KEYS命令
# 一方面 KEYS * 命令可以列出所有的键,会影响数据安全
# 另一方面 KEYS 命令会阻塞数据库,在数据库中存储了大量数据时,该命令会消耗很长时间
# 期间对Redis的访问也会被阻塞,而当锁释放的一瞬间,大量请求涌入Redis,会造成Redis直接崩溃
rename-command KEYS ""

# 此外还应禁止 FLUSHALL 和 FLUSHDB 命令
# 这两个命令会清空数据,并且不会失败

# 配置master节点信息
# 格式:
#slaveof <masterip> <masterport>
# 此处masterip所指定的redis-server-master是运行master节点的容器名
# Docker容器间可以使用容器名代替实际的IP地址来通信
slaveof redis-server-master 6379

# 设定连接主节点所使用的密码
masterauth "redis"

创建 redis-slave2.conf,修改监听端口号为 6381,其余配置与 redis-slave1.conf 相同。

编写 docker-compose.yml

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
---

version: '3'

services:
# 主节点的容器
redis-server-master:
image: redis
container_name: redis-server-master
restart: always
ports:
- 6379:6379
networks:
redis-cluster:
# 为容器指定一个静态IP
ipv4_address: 10.1.0.2
environment:
TZ: "Asia/Shanghai"
volumes:
# 映射配置文件和数据目录
- ./redis-master.conf:/usr/local/etc/redis/redis.conf
- ../data/redis-master:/data
sysctls:
# 必要的内核参数
net.core.somaxconn: '511'
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 从节点1的容器
redis-server-slave-1:
image: redis
container_name: redis-server-slave-1
restart: always
depends_on:
- redis-server-master
ports:
- 6380:6380
networks:
redis-cluster:
ipv4_address: 10.1.0.3
environment:
TZ: "Asia/Shanghai"
volumes:
- ./redis-slave1.conf:/usr/local/etc/redis/redis.conf
- ../data/redis-slave-1:/data
sysctls:
net.core.somaxconn: '511'
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 从节点2的容器
redis-server-slave-2:
image: redis
container_name: redis-server-slave-2
restart: always
depends_on:
- redis-server-master
ports:
- 6381:6381
networks:
redis-cluster:
ipv4_address: 10.1.0.4
environment:
TZ: "Asia/Shanghai"
volumes:
- ./redis-slave2.conf:/usr/local/etc/redis/redis.conf
- ../data/redis-slave-2:/data
sysctls:
net.core.somaxconn: '511'
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]

networks:
redis-cluster:
# IP Address Management
ipam:
config:
# 为容器分配一个独立的子网,用来方便为容器指定静态IP
# 使用独立的子网可以避免IP地址冲突的问题
- subnet: 10.1.0.0/16

启动容器
在 docker-compose.yml 所在位置执行 docker-compose up 即可启动上述三个容器,docker-compose 会将容器日志打印到终端,在日志中可以看到三个 Redis 服务器在启动过程中的动作,以及从节点加入主节点的信息。

启动成功后,可以在本机使用 redis-cli 连接至主节点。连接成功后,可以使用 info replication 命令检查主从复制的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> auth redis
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.1.0.3,port=6380,state=online,offset=476,lag=1
slave1:ip=10.1.0.4,port=6381,state=online,offset=476,lag=0
master_replid:f29d9059a286deb4bbe5360f9c673a2484370205
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:476
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:476

启动容器
在 docker-compose.yml 所在位置执行 docker-compose up 即可启动上述三个容器,docker-compose 会将容器日志打印到终端,在日志中可以看到三个 Redis 服务器在启动过程中的动作,以及从节点加入主节点的信息。

启动成功后,可以在本机使用 redis-cli 连接至主节点。连接成功后,可以使用 info replication 命令检查主从复制的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379         > redis-cli -h 127.0.0.1  -p 6379 -a redis
127.0.0.1:6379> auth redis
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.1.0.3,port=6380,state=online,offset=476,lag=1
slave1:ip=10.1.0.4,port=6381,state=online,offset=476,lag=0
master_replid:f29d9059a286deb4bbe5360f9c673a2484370205
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:476
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:476
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
测试一下
光是启动成功还不够,还需要测试一下从节点是否能同步主节点的数据。

首先连接到主节点,新增一个 set:

127.0.0.1:6379> auth redis
OK
127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> get foo
"bar"
好的,在主节点里面成功添加了一条数据。那么接下来连接到 slave-1,看一下数据有没有同步过去:

127.0.0.1:6380> auth redis
OK
127.0.0.1:6380> get foo
"bar"
127.0.0.1:6380> set foo baz
(error) READONLY You can't write against a read only replica.
看来 slave-1 成功的从主节点同步了数据,并且这个节点也按照设定,是一个只读的节点。那么 slave-2 呢?

127.0.0.1:6381> auth redis
OK
127.0.0.1:6381> get foo
"bar"
127.0.0.1:6381> set foo baz
(error) READONLY You can't write against a read only replica.
OK,slave-2 也成功的同步了数据,并且正在作为一个只读节点运行着。

使用 Docker 配置 Redis 哨兵

编辑 redis-sentinel.conf,修改下列配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 接受来自外部的连接
bind 0.0.0.0

# 哨兵的端口号
# 因为各个哨兵节点会运行在单独的Docker容器中
# 所以无需担心端口重复使用
port 26379

# 配置哨兵的监控参数
# 格式:sentinel monitor <master-name> <ip> <redis-port> <quorum>
# master-name是为这个被监控的master起的名字
# ip是被监控的master的IP或主机名。因为Docker容器之间可以使用容器名访问,所以这里写master节点的容器名
# redis-port是被监控节点所监听的端口号
# quorom设定了当几个哨兵判定这个节点失效后,才认为这个节点真的失效了
sentinel monitor local-master redis-server-master 6379 2

# 连接主节点的密码
# 格式:sentinel auth-pass <master-name> <password>
sentinel auth-pass local-master redis

配置及启动容器
编写 docker-compose.yml
这里继续使用 docker-compose 管理容器。

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
---

version: '3'

services:
# 哨兵的数量应是奇数,以便于哨兵通过投票来作出决策
redis-sentinel-1:
image: redis
container_name: redis-sentinel-1
restart: always
ports:
# 向外暴露26379端口
- 26379:26379
networks:
default:
ipv4_address: 10.1.0.5
volumes:
# 将哨兵配置文件和存放数据的文件夹挂载到容器内
- ./redis-sentinel.conf:/usr/local/etc/redis/redis-sentinel.conf
- ../data/redis-sentinel-1:/data
sysctls:
# 设定容器的内核参数,以消除Redis启动过程中的一个warning
net.core.somaxconn: '511'
# 根据指定的配置文件来启动Redis哨兵
command: ["redis-sentinel", "/usr/local/etc/redis/redis-sentinel.conf"]
redis-sentinel-2:
image: redis
container_name: redis-sentinel-2
restart: always
ports:
- 26380:26379
networks:
default:
ipv4_address: 10.1.0.6
volumes:
- ./redis-sentinel.conf:/usr/local/etc/redis/redis-sentinel.conf
- ../data/redis-sentinel-2:/data
sysctls:
net.core.somaxconn: '511'
command: ["redis-sentinel", "/usr/local/etc/redis/redis-sentinel.conf"]
redis-sentinel-3:
image: redis
container_name: redis-sentinel-3
restart: always
ports:
- 26381:26379
networks:
default:
ipv4_address: 10.1.0.7
volumes:
- ./redis-sentinel.conf:/usr/local/etc/redis/redis-sentinel.conf
- ../data/redis-sentinel-3:/data
sysctls:
net.core.somaxconn: '511'
command: ["redis-sentinel", "/usr/local/etc/redis/redis-sentinel.conf"]

networks:
default:
external:
# 因为哨兵容器需要与服务端容器运行在同一网络内
# 所以这里指定了一个外部的网络
# 网络名参考《使用Docker配置Redis主从复制》中的注释
name: server_redis-cluster

启动容器
这里同样使用 docker-compose up -d 启动容器,然后使用 redis-cli 连接到哨兵节点,连接成功后,可以使用 info sentinel 检查哨兵的信息。

1
2
3
4
5
6
7
8
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=local-master,status=ok,address=10.1.0.2:6379,slaves=2,sentinels=1

其中,sentinel_masters:1 说明这个哨兵在监控一个 master,最后一行中写明了 master0 这个节点别名为 local-master,状态为 OK,地址是 10.1.0.2:6379,有 2 个从节点。

测试一下

哨兵光是启动了还是不够的,还需要测试一下当被监控节点下线之后,哨兵是否能作出反应。

首先,停掉 master,slave-1,slave-2,然后观察哨兵的日志,在经过一段时间之后,哨兵宣布有节点下线:

1
2
3
4
5
6
7
8
9
redis-sentinel-3    | 1:X 19 Aug 2019 11:43:13.487 # +sdown slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:43:13.487 # +sdown slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:43:13.502 # +sdown slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:43:13.502 # +sdown slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:43:13.581 # +sdown slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:43:13.582 # +sdown slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:43:14.429 # +sdown master local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:43:14.439 # +sdown master local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:43:14.470 # +sdown master local-master 10.1.0.2 6379

然后再启动 master,slave-1,slave-2,并观察哨兵的日志,在节点上线之后,哨兵宣布了节点重启,并解除了节点的下线状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
redis-sentinel-2    | 1:X 19 Aug 2019 11:49:45.040 * +reboot slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:49:45.081 * +reboot slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:49:45.081 * +reboot slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:49:45.081 * +reboot slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:49:45.082 * +reboot slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:49:45.132 # -sdown slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:49:45.132 # -sdown slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:49:45.133 # -sdown slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:49:45.133 # -sdown slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:49:45.140 # -sdown slave 10.1.0.3:6380 10.1.0.3 6380 @ local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:49:45.141 * +reboot slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:49:45.231 # -sdown slave 10.1.0.4:6381 10.1.0.4 6381 @ local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:49:45.960 * +reboot master local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:49:45.963 * +reboot master local-master 10.1.0.2 6379
redis-sentinel-1 | 1:X 19 Aug 2019 11:49:46.043 # -sdown master local-master 10.1.0.2 6379
redis-sentinel-3 | 1:X 19 Aug 2019 11:49:46.046 # -sdown master local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:49:46.054 * +reboot master local-master 10.1.0.2 6379
redis-sentinel-2 | 1:X 19 Aug 2019 11:49:46.144 # -sdown master local-master 10.1.0.2 6379