Redis核心知识梳理

Redis核心知识梳理

一、Redis基础

1 Redis介绍

  • Redis是内存数据库
  • C语言编写
  • 单线程
    • 单线程如何保证并发性能?epoll
  • 键值对Key-Value方式存储数据
  • 持久化方式:RDB和AOF
  • Redis集群
  • 数据过期处理:LRU

1.1 Redis应用场景

Redis应用场景,面试中常被问到,“请问你在项目中哪些场景用到Redis,请举例说明。”

1.1.1 内存数据库

登录信息、购物车信息、用户浏览记录等

1.1.2 缓存数据库

商品数据、广告数据

1.1.3 Session共享

解决分布式集群架构中的Session分离问题

1.1.4 任务队列

秒杀、抢购、12306购票

1.1.5 消息发布订阅

支持发布订阅消息模式

1.1.6 应用排行榜
1.1.7 网站访问统计
1.1.8 数据过期处理
1.1.9 分布式锁实现

1.2 Redis单机安装

Redis官方网站

1.2.1 Redis安装包下载
  • 官网下载最新版稳定安装包,如下图所示,当前最新版为5.0.5,关于Redis版本还需要结合公司开发业务场景决定,不必追求最新版本,毕竟生产环境严格要求系统的稳定性。1568686851026
1.2.2 Redis安装环境

由于大多数企业服务器系统都是Linux,因此选择采用Linux环境安装,这里选择CentOS 7作为安装环境。

  • 第一步:安装C语言需要的GCC环境

    1
    2
    yum install -y gcc-c++
    yum install -y wget
  • 第二步:下载安装包或者windows环境下载后传到CentOS中,然后解压到安装路径

    1
    2
    3
    4
    wget http://download.redis.io/releases/redis-5.0.5.tar.gz
    tar -zxvf redis-5.0.5.tar.gz -C /usr/apps(安装路径)
    # 创建软链接
    ln -s /usr/apps/redis-5.0.5 /usr/apps/redis
  • 第三步:编译Redis源码,进入redis安装目录,执行编译命令

    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
    cd /usr/apps/redis
    make
    # 需要在/redis/src执行make install
    [root@localhost redis]# make install
    cd src && make install
    make[1]: Entering directory `/usr/apps/redis-5.0.5/src'
    CC Makefile.dep
    make[1]: Leaving directory `/usr/apps/redis-5.0.5/src'
    make[1]: Entering directory `/usr/apps/redis-5.0.5/src'

    Hint: It's a good idea to run 'make test' ;)

    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    make[1]: Leaving directory `/usr/apps/redis-5.0.5/src'
    [root@localhost redis]# cd src
    [root@localhost src]# make install

    Hint: It's a good idea to run 'make test' ;)

    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    # 建议先进行make test
    [root@localhost src]# make test
    # 此时发现需要安装依赖包tcl
    You need tcl 8.5 or newer in order to run the Redis test
    make: *** [test] Error 1
    # 安装tcl
    yum install tcl

    # 安装Redis自定义路径下,安装后自定义路径下会新增bin目录,目录下包含如下文件,其中redis.conf需要从解压包中拷贝一份到bin目录
    redis-benchmark # 性能测试工具
    redis-check-aof # aof文件检查工具
    redis-check-rdb # rdb文件检查工具
    redis-cli # 进入redis命令客户端
    redis.conf # redis配置文件
    redis-sentinel # 启动哨兵监控服务
    redis-server # 启动redis服务

    make install PREFIX=/usr/apps/redis
  • 第四步:

    守护进程启动,需要修改redis.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
    vim redis.conf
    # 将daemonize由no修改为yes
    daemonize yes
    # 注释掉bind,默认绑定的是回环地址,不能被其他机器访问
    bind 127.0.0.1
    # 是否开启保护模式,yes改为no
    protected-mode no

    # 修改后 :wq保存文件

    # 守护进程启动
    ./bin/redis-server redis.conf start
    # 查看redis是否正常启动
    [root@localhost bin]# lsof -i:6379
    COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
    redis-ser 20124 root 6u IPv6 58289 0t0 TCP *:6379 (LISTEN)
    redis-ser 20124 root 7u IPv4 58290 0t0 TCP *:6379 (LISTEN)

    [root@localhost apps]# ps -ef | grep redis
    root 20124 1 0 03:14 ? 00:00:10 redis-server *:6379
    root 20832 3597 0 05:51 pts/0 00:00:00 grep --color=auto redis

    # 关闭redis
    ./bin/redis-cli shutdown

    # 小技巧, 配置环境变量,可以在任意路径启动redis
    vim /etc/profile
    # env config
    export REDIS_HOME=/usr/apps/redis
    export PATH=$REDIS_HOME/bin:$PATH

    # 修改后
    source /etc/profile
  • 第五步:

    打开客户端

    1
    2
    3
    ./bin/redis-cli -h 192.168.1.10X -p 6379
    -h: #redis服务器ip地址
    -p: #redis实例的端口号

1.3 Redis基准测试

安装好单机版Redis,benchmark测试,复杂Redis命令QPS瞬间下滑

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
[root@centos-p160 bin]# redis-benchmark -h 192.168.1.160
====== PING_INLINE ======
100000 requests completed in 2.79 seconds
50 parallel clients
3 bytes payload
keep alive: 1

59.22% <= 1 milliseconds
97.27% <= 2 milliseconds
99.04% <= 3 milliseconds
99.59% <= 4 milliseconds
99.75% <= 5 milliseconds
99.82% <= 6 milliseconds
99.82% <= 7 milliseconds
99.95% <= 8 milliseconds
100.00% <= 8 milliseconds
35893.75 requests per second

====== PING_BULK ======
100000 requests completed in 2.55 seconds
50 parallel clients
3 bytes payload
keep alive: 1

67.92% <= 1 milliseconds
99.21% <= 2 milliseconds
99.63% <= 3 milliseconds
99.75% <= 4 milliseconds
99.87% <= 5 milliseconds
99.93% <= 6 milliseconds
100.00% <= 7 milliseconds
100.00% <= 7 milliseconds
39246.46 requests per second

====== SET ======
100000 requests completed in 2.96 seconds
50 parallel clients
3 bytes payload
keep alive: 1

54.11% <= 1 milliseconds
95.99% <= 2 milliseconds
98.30% <= 3 milliseconds
99.39% <= 4 milliseconds
99.68% <= 5 milliseconds
99.86% <= 6 milliseconds
99.94% <= 9 milliseconds
99.99% <= 23 milliseconds
100.00% <= 23 milliseconds
33772.38 requests per second

====== GET ======
100000 requests completed in 2.71 seconds
50 parallel clients
3 bytes payload
keep alive: 1

60.07% <= 1 milliseconds
98.15% <= 2 milliseconds
99.55% <= 3 milliseconds
99.75% <= 4 milliseconds
99.86% <= 5 milliseconds
99.94% <= 6 milliseconds
100.00% <= 6 milliseconds
36832.41 requests per second

====== INCR ======
100000 requests completed in 3.11 seconds
50 parallel clients
3 bytes payload
keep alive: 1

52.16% <= 1 milliseconds
95.01% <= 2 milliseconds
97.53% <= 3 milliseconds
98.49% <= 4 milliseconds
99.09% <= 5 milliseconds
99.42% <= 6 milliseconds
99.62% <= 7 milliseconds
99.73% <= 8 milliseconds
99.89% <= 9 milliseconds
100.00% <= 9 milliseconds
32164.68 requests per second

====== LPUSH ======
100000 requests completed in 2.71 seconds
50 parallel clients
3 bytes payload
keep alive: 1

56.81% <= 1 milliseconds
98.86% <= 2 milliseconds
99.70% <= 3 milliseconds
99.84% <= 4 milliseconds
99.95% <= 5 milliseconds
100.00% <= 5 milliseconds
36886.76 requests per second

====== RPUSH ======
100000 requests completed in 2.91 seconds
50 parallel clients
3 bytes payload
keep alive: 1

56.98% <= 1 milliseconds
96.43% <= 2 milliseconds
98.67% <= 3 milliseconds
99.28% <= 4 milliseconds
99.47% <= 5 milliseconds
99.73% <= 6 milliseconds
99.88% <= 8 milliseconds
99.91% <= 9 milliseconds
99.91% <= 10 milliseconds
99.94% <= 11 milliseconds
99.94% <= 14 milliseconds
99.95% <= 28 milliseconds
100.00% <= 28 milliseconds
34376.07 requests per second

====== LPOP ======
100000 requests completed in 2.77 seconds
50 parallel clients
3 bytes payload
keep alive: 1

55.57% <= 1 milliseconds
98.26% <= 2 milliseconds
99.50% <= 3 milliseconds
99.77% <= 4 milliseconds
99.90% <= 5 milliseconds
99.97% <= 6 milliseconds
100.00% <= 7 milliseconds
100.00% <= 7 milliseconds
36153.29 requests per second

====== RPOP ======
100000 requests completed in 2.95 seconds
50 parallel clients
3 bytes payload
keep alive: 1

55.48% <= 1 milliseconds
96.17% <= 2 milliseconds
98.37% <= 3 milliseconds
99.05% <= 4 milliseconds
99.42% <= 5 milliseconds
99.65% <= 6 milliseconds
99.80% <= 7 milliseconds
99.81% <= 9 milliseconds
99.85% <= 10 milliseconds
99.87% <= 11 milliseconds
99.92% <= 12 milliseconds
99.98% <= 13 milliseconds
99.99% <= 23 milliseconds
100.00% <= 23 milliseconds
33944.33 requests per second

====== SADD ======
100000 requests completed in 2.73 seconds
50 parallel clients
3 bytes payload
keep alive: 1

59.64% <= 1 milliseconds
97.82% <= 2 milliseconds
99.36% <= 3 milliseconds
99.71% <= 4 milliseconds
99.78% <= 5 milliseconds
99.88% <= 6 milliseconds
99.92% <= 7 milliseconds
99.93% <= 8 milliseconds
99.98% <= 10 milliseconds
99.99% <= 11 milliseconds
100.00% <= 11 milliseconds
36643.46 requests per second

====== HSET ======
100000 requests completed in 2.80 seconds
50 parallel clients
3 bytes payload
keep alive: 1

55.56% <= 1 milliseconds
97.49% <= 2 milliseconds
98.96% <= 3 milliseconds
99.58% <= 4 milliseconds
99.85% <= 5 milliseconds
99.97% <= 6 milliseconds
99.97% <= 7 milliseconds
100.00% <= 7 milliseconds
35714.29 requests per second

====== SPOP ======
100000 requests completed in 2.69 seconds
50 parallel clients
3 bytes payload
keep alive: 1

63.08% <= 1 milliseconds
97.82% <= 2 milliseconds
99.39% <= 3 milliseconds
99.69% <= 4 milliseconds
99.70% <= 5 milliseconds
99.82% <= 6 milliseconds
99.89% <= 7 milliseconds
99.90% <= 9 milliseconds
99.95% <= 10 milliseconds
100.00% <= 10 milliseconds
37119.52 requests per second

====== LPUSH (needed to benchmark LRANGE) ======
100000 requests completed in 2.76 seconds
50 parallel clients
3 bytes payload
keep alive: 1

57.28% <= 1 milliseconds
97.95% <= 2 milliseconds
99.27% <= 3 milliseconds
99.54% <= 4 milliseconds
99.72% <= 5 milliseconds
99.93% <= 6 milliseconds
99.98% <= 9 milliseconds
100.00% <= 9 milliseconds
36258.16 requests per second

====== LRANGE_100 (first 100 elements) ======
100000 requests completed in 7.34 seconds
50 parallel clients
3 bytes payload
keep alive: 1

0.34% <= 1 milliseconds
14.99% <= 2 milliseconds
67.96% <= 3 milliseconds
95.18% <= 4 milliseconds
98.26% <= 5 milliseconds
99.11% <= 6 milliseconds
99.48% <= 7 milliseconds
99.64% <= 8 milliseconds
99.81% <= 9 milliseconds
99.87% <= 10 milliseconds
99.91% <= 11 milliseconds
99.92% <= 12 milliseconds
99.92% <= 13 milliseconds
99.95% <= 14 milliseconds
99.97% <= 15 milliseconds
99.99% <= 16 milliseconds
100.00% <= 16 milliseconds
13631.41 requests per second

====== LRANGE_300 (first 300 elements) ======
100000 requests completed in 17.18 seconds
50 parallel clients
3 bytes payload
keep alive: 1

0.00% <= 1 milliseconds
0.11% <= 2 milliseconds
1.32% <= 3 milliseconds
5.11% <= 4 milliseconds
22.57% <= 5 milliseconds
45.09% <= 6 milliseconds
67.23% <= 7 milliseconds
86.20% <= 8 milliseconds
94.58% <= 9 milliseconds
97.06% <= 10 milliseconds
98.06% <= 11 milliseconds
98.64% <= 12 milliseconds
99.02% <= 13 milliseconds
99.30% <= 14 milliseconds
99.48% <= 15 milliseconds
99.62% <= 16 milliseconds
99.74% <= 17 milliseconds
99.82% <= 18 milliseconds
99.86% <= 19 milliseconds
99.90% <= 20 milliseconds
99.90% <= 21 milliseconds
99.92% <= 22 milliseconds
99.94% <= 23 milliseconds
99.97% <= 24 milliseconds
99.97% <= 29 milliseconds
99.99% <= 35 milliseconds
99.99% <= 39 milliseconds
100.00% <= 40 milliseconds
100.00% <= 40 milliseconds
5821.40 requests per second

====== LRANGE_500 (first 450 elements) ======
100000 requests completed in 23.98 seconds
50 parallel clients
3 bytes payload
keep alive: 1

0.00% <= 1 milliseconds
0.00% <= 2 milliseconds
0.09% <= 3 milliseconds
0.88% <= 4 milliseconds
3.03% <= 5 milliseconds
8.62% <= 6 milliseconds
22.30% <= 7 milliseconds
38.27% <= 8 milliseconds
54.54% <= 9 milliseconds
70.24% <= 10 milliseconds
83.97% <= 11 milliseconds
92.72% <= 12 milliseconds
96.17% <= 13 milliseconds
97.54% <= 14 milliseconds
98.28% <= 15 milliseconds
98.74% <= 16 milliseconds
99.05% <= 17 milliseconds
99.27% <= 18 milliseconds
99.44% <= 19 milliseconds
99.55% <= 20 milliseconds
99.61% <= 21 milliseconds
99.65% <= 22 milliseconds
99.71% <= 23 milliseconds
99.76% <= 24 milliseconds
99.82% <= 25 milliseconds
99.86% <= 26 milliseconds
99.90% <= 27 milliseconds
99.93% <= 28 milliseconds
99.94% <= 29 milliseconds
99.95% <= 36 milliseconds
99.96% <= 37 milliseconds
99.97% <= 38 milliseconds
99.98% <= 39 milliseconds
99.99% <= 40 milliseconds
100.00% <= 41 milliseconds
100.00% <= 41 milliseconds
4169.97 requests per second

====== LRANGE_600 (first 600 elements) ======
100000 requests completed in 30.68 seconds
50 parallel clients
3 bytes payload
keep alive: 1

0.00% <= 2 milliseconds
0.01% <= 3 milliseconds
0.13% <= 4 milliseconds
0.58% <= 5 milliseconds
1.99% <= 6 milliseconds
4.64% <= 7 milliseconds
11.23% <= 8 milliseconds
22.54% <= 9 milliseconds
35.08% <= 10 milliseconds
47.90% <= 11 milliseconds
60.37% <= 12 milliseconds
72.43% <= 13 milliseconds
83.20% <= 14 milliseconds
91.29% <= 15 milliseconds
95.15% <= 16 milliseconds
96.71% <= 17 milliseconds
97.53% <= 18 milliseconds
98.13% <= 19 milliseconds
98.57% <= 20 milliseconds
98.94% <= 21 milliseconds
99.19% <= 22 milliseconds
99.38% <= 23 milliseconds
99.50% <= 24 milliseconds
99.60% <= 25 milliseconds
99.67% <= 26 milliseconds
99.73% <= 27 milliseconds
99.79% <= 28 milliseconds
99.84% <= 29 milliseconds
99.87% <= 30 milliseconds
99.89% <= 31 milliseconds
99.90% <= 32 milliseconds
99.93% <= 33 milliseconds
99.96% <= 34 milliseconds
99.97% <= 35 milliseconds
99.98% <= 36 milliseconds
99.99% <= 38 milliseconds
99.99% <= 39 milliseconds
99.99% <= 41 milliseconds
99.99% <= 43 milliseconds
100.00% <= 44 milliseconds
100.00% <= 44 milliseconds
3259.56 requests per second

====== MSET (10 keys) ======
100000 requests completed in 3.53 seconds
50 parallel clients
3 bytes payload
keep alive: 1

7.59% <= 1 milliseconds
95.91% <= 2 milliseconds
99.44% <= 3 milliseconds
99.66% <= 4 milliseconds
99.79% <= 5 milliseconds
99.83% <= 6 milliseconds
99.90% <= 7 milliseconds
99.91% <= 8 milliseconds
99.94% <= 9 milliseconds
99.99% <= 10 milliseconds
100.00% <= 10 milliseconds
28304.56 requests per second

2 Redis数据类型

Redis命令大全

Redis官网提供最全的命令,详细介绍命令语法,时间复杂度,注意事项,应用场景等等,需要耐心学习验证。同时学习命令的同时结合Java客户端如Jedis接口进行实践,记忆会更加深刻,提高实战能力。

说明:

a. Redis的键值设计非常关键,比如说键值可以用于区分业务类型。

  • Key要见名知义,类似数据库表名称
  • Key要辅助查找
  • Key设计需要避免重复,因为Redis赋值,Key重复将会覆盖之前数据

b. Redis 命令行忽略大小写,但是键值key不忽略大小写。

c. Redis大多数命令时间复杂度:O(1),具体问题具体分析,如遇到遍历相关的操作,时间复杂度另当别论。

2.1 String

SET key value [EX seconds] [PX milliseconds] [NX|XX]
1
2
3
4
5
6
7
8
9
10
11
SET key value [EX seconds] [PX milliseconds] [NX|XX]
# 命令时间复杂度:O(1)
# EX seconds – 设置键key的过期时间,单位时秒
# PX milliseconds – 设置键key的过期时间,单位时毫秒
# NX – 只有键key不存在的时候才会设置key的值
# XX – 只有键key存在的时候才会设置key的值

# 注意: 由于SET命令加上选项已经可以完全取代SETNX, SETEX, PSETEX的功能,所以在将来的版本中,redis可能会不推荐使用并且最终抛弃这几个命令。

# 返回值
# 如果SET命令正常执行那么回返回OK,否则如果加了NX 或者 XX选项,但是没有设置条件。那么会返回nil。

命令 SET resource-name anystring NX EX max-lock-time 是一种用 Redis 来实现锁机制的简单方法。

如果上述命令返回OK,那么客户端就可以获得锁(如果上述命令返回Nil,那么客户端可以在一段时间之后重新尝试),并且可以通过DEL命令来释放锁。

客户端加锁之后,如果没有主动释放,会在过期时间之后自动释放。

可以通过如下优化使得上面的锁系统变得更加鲁棒:

  • 不要设置固定的字符串,而是设置为随机的大字符串,可以称为token。
  • 通过脚步删除指定锁的key,而不是DEL命令。

上述优化方法会避免下述场景:

a客户端获得的锁(键key)已经由于过期时间到了被redis服务器删除,但是这个时候a客户端还去执行DEL命令。而b客户端已经在a设置的过期时间之后重新获取了这个同样key的锁,那么a执行DEL就会释放了b客户端加好的锁。

解锁脚本的一个例子将类似于以下:

1
2
3
4
5
6
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end

2.2 Hash

2.3 List

2.4 Set

2.5 SortedSet

3 Redis 事务

3.1 Redis事务介绍

  • Redis事务通过MULTI、EXEC、DISCARD和WATCH这四个命令实现

  • Redis单个命令都是原子性的,因此需要确保事务性的对象是命令集合

  • Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断执行

  • Redis不支持回滚操作

    如何理解呢?

    • 大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
    • Redis为了性能方面就忽略了事务回滚
3.1.1 MULTI

用于标记事务块的开始,Redis会将后续的命令诸葛放入队列中,然后使用EXEC命令原子化地执行该命令序列。

1
2
3
4
5
6
7
8
9
10
11
multi

# 测试,将同一个事务的redis命令加入队列
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
3.1.2 EXEC

在一个事务中执行所有先前放入队列的命令,然后恢复正常连接状态

1
2
3
4
5
6
7
exec

# 测试,执行队列中的redis命令
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK
3.1.3 DISCARD

清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。

1
discard
3.1.4 WATCH

当某个事务需要按照条件执行时,就要使用这个命令将给定的键设置为受监控的状态

1
2
# 使用该命令可以实现redis乐观锁
watch key [key ...]
3.1.5 UNWATCH

清除所有先前为一个事务监控的键

1
unwatch

4 Redis持久化

Redis持久化是因为Redis为内存数据库,内存中的数据无法应对灾难性的故障,如生产环境系统宕机、机房毁坏或停电、电缆施工过程毁坏等,会导致大型的故障和Case。为了应对灾难,必须有持久化和备份恢复的方案。如将持久化文件备份到云存储服务器上,如果灾难发生,同步到本地进行数据恢复,就能扛过一次灾难。

Redis持久化方式两种:

  • RDB(Redis默认持久化方式)

    优点:通过存储数据快照文件,恢复数据便捷

    缺点:RDB模式会丢失最后一次快照以后更改的所有数据

  • AOF

注意: 两种持久化方式可以单独使用,亦可结合使用,当同时存在两种持久化方式时,优先使用AOF

4.1 Redis持久化之RDB

​ RDB持久化是通过快照完成,当符合一定条件时,redis会自动将内存中全部数据执行快照操作,并存储到硬盘。默认存储在dump.rdb文件(文件名在redis.conf文件中dbfilename)

Redis进行快照的时机,配置文件redis.conf

1
2
3
save 900 1      # 表示900秒内至少1个键被更改则进行快照。  
save 300 10 # 表示300秒内至少10条被更改则快照
save 60 10000 # 表示60秒内至少10000条

Redis自动实现快照过程:

  • Redis使用fork函数复制一份当前进程的副本(子进程);
  • 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件;
  • 当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,至此,一次快照操作完成。

注意:Redis在进行快照的过程中不会修改RDB文件,只有快照结束后才将旧的文件替换为新的,因此任何时候RDB文件都是完整的。该模式使得我们可以通过定时备份RDB文件来实现Redis数据库备份。

RDB文件经过压缩的二进制文件,占用空间小于内存中的数据,便于传输。

手动测试RDB:

rdb文件会保持在dir参数对应的路径下。

1
2
3
4
# 主进程进行快照,会阻塞其他请求
save
# Redis执行fork函数复制出一个子进程进行快照操作
bgsave

检测并修复rdb文件?

redis-check-dump

知识点:

  • redis-cli shutdown命令关闭redis,redis会生成完整的数据快照存到dump.rdb文件,属于安全关闭。
  • 模拟redis故障退出,kill -9 redis进程id,内存数据丢失

4.2 Redis持久化之AOF

AOF方式是通过日志文件进行持久化。默认情况下Redis未开启AOF,可以通过修改redis.conf配置文件的appendonly参数开启。

aof文件会保持在dir参数对应的路径下。

1
appendonly yes

默认文件名appendonly.aof,可以通过修改redis.conf参数appendfilename重命名。

1
appendfilename appendonly.aof

Redis写命令的同步时机:

1
2
3
appendfsync always    # 每次都会执行,性能差  
appendfsync everysec # 默认 每秒执行一次同步操作(推荐,默认)
appendfsync no # 不主动进行同步,由操作系统来做,30秒一次

AOF日志文件重写:

1
2
3
4
# 当前aof文件大小超过上一次重写时的aof文件大小的百分之多少时会再次进行重写,如果之前没有重写,则以启动时的aof文件大小为依据

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

手动测试AOF

1
2
3
# 重写过程只和内存中的数据有关,和之前aof文件无关
# 所谓重写,不需要对原有aof文件进行任何读写,它针对的是数据库中key的当前值
bgrewriteaof

检测并修复aof文件?

redis-check-aof

1
2
# 修复损坏的AOF文件
redis-check-aof --fix

动态切换redis持久方式,从 RDB 切换到 AOF(支持Redis 2.2及以上)

1
2
3
CONFIG SET appendonly yes

CONFIG SET save "" (可选)

注意:

  • 当redis启动时,如果rdb持久化和aof持久化都打开,那么程序会优先使用aof方式来恢复数据集,因为aof方式所保存的数据通常是最完整的。如果aof文件丢失,则启动之后数据库内容为空。

  • 如果想把正在运行的redis数据库,从RDB切换到AOF,建议先使用动态切换方式,再修改配置文件,重启数据库。(不能自己修改配置文件,重启数据库,否则数据库中数据就为空了,问题出在redis在开启aof后就会默认使用aof,因为此时aof文件为空。)

  • 如果RDB执行snapshotting操作,redis不会执行AOF rewrite操作;如果redis在执行AOF rewrite操作,那么不会执行RDB snapshotting操作

  • 如果RDB执行snapshotting操作,此时用户执行BGREWRITEAOF命令,那么需要等RDB快照生成之后才能执行AOF rewrite操作。

4.3 企业级数据备份方案

RDB方案适合数据冷备方案:

  • 写crontab定时调度脚本做数据备份;
  • 每小时copy一份RBD文件,备份到一个目录,保留最近48小时的数据备份;
  • 每天都保留一份RDB文件,备份到一个目录,仅仅保留最近一个月的备份;
  • 每次copy备份的时候,将过旧的备份文件删除;
  • 每天晚上将当前服务器上所有数据备份,发送一份至远程的云端服务器。

冷备方案实践一下:

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
/usr/local/redis

# 每小时copy一次备份,删除48小时前的数据

crontab -e

0 * * * * sh /usr/local/redis/copy/redis_rdb_copy_hourly.sh

redis_rdb_copy_hourly.sh

#!/bin/sh

cur_date=`date +%Y%m%d%k`
rm -rf /usr/local/redis/snapshotting/$cur_date
mkdir /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date

del_date=`date -d -48hour +%Y%m%d%k`
rm -rf /usr/local/redis/snapshotting/$del_date

# 每天copy一次备份

crontab -e

0 0 * * * sh /usr/local/redis/copy/redis_rdb_copy_daily.sh

redis_rdb_copy_daily.sh

#!/bin/sh

cur_date=`date +%Y%m%d`
rm -rf /usr/local/redis/snapshotting/$cur_date
mkdir /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date

del_date=`date -d -1month +%Y%m%d`
rm -rf /usr/local/redis/snapshotting/$del_date

RDB冷备方案故障恢复:

如果数据误删后,需要通过rdb文件进行恢复,需要注意一下,生产环境一般都会开启aof,此时redis优先使用aof加载数据,所以需要先将aof关闭,redis才能使用rdb备份文件恢复数据,此时一定要注意,需要通过CONFIG SET热修改appendonly yes,再次执行config rewrite将热修改重新写入配置文件,此时会根据恢复后的数据生成aof文件。

5 Redis常用命令

参考博客:

Redis特性及应用场景总结

5.1 config命令

1
2
3
4
5
6
# 动态设置参数,服务器重启后失效
config set

# 举例
config set appendonly yes
config set save "90 1 30 10 60 100"
1
2
# 查看所有可以用config set命令设置的参数
config get *
1
2
3
4
# Redis服务器启动后,config rewrite命令修改配置文件redis.conf(Redis 2.8及以上版本可以使用),主要是将config set动态指定的命令保存到配置文件中。
config rewrite
# 注意:config rewrite 命令对redis.conf文件的重写是原子性的,并且是一致的:
# 如果重写出错或重写期间服务器崩溃,那么重写失败,原有redis.conf文件不会被修改;如果重写成功,那么redis.conf文件为重写后的新文件

5.2 安全策略

设置数据库密码

1
2
3
4
5
6
# 修改密码
requirepass password
# 验证密码
auth password
# bind参数(数据库只能在指定IP下访问)
bind 127.0.0.1

5.3 命令重命名

1
2
3
4
# 修改命令的名称
rename-command flushall cleanall
# 禁用命令
rename-command flushall ""

6 Redis主从复制

6.1 Redis高并发企业实践

  • Redis主从架构

    QPS量级决定Redis高并发方案,单机Redis支持QPS 1W+,如果业务场景超过10W+并发,单机Redis无法承受到达瓶颈。首先想到读写分离,一般情况下业务场景都是读多写少。Redis主从架构,一主多从,主节点负责写请求,从节点负责读请求。支持水平扩容,读请求逐渐增加,我们可以直接通过添加从节点方式解决。(Redis主从架构解决读高并发问题,如果是写高并发需要异步方案)

    • 主从复制原理:master -> slave复制数据

      • 异步方式复制数据到slave节点,redis2.8版本开始,slave node会周期性的确认自己每次复制的数据量
      • 一个master node可以配置多个slave node
      • slave node 可以连接其他slave node
      • slave node做复制的时候,是不会block master node的正常工作
      • slave node做复制时,不会block对自己的查询操作,会用旧的数据集提供服务,复制完成时,需要删除旧的数据,并加载新数据,此时会暂停对外服务
      • slave node主要用于水平扩容,做读写分离,扩容的slave node可以提高读的吞吐量
    • master持久化对于主从架构安全保障意义

      如果采用主从架构,建议必须开启master node持久化

    • 主从架构原理描述

      • slave ping master
      • full resynchronization命令,slave 全量复制
      • master收到slave全量复制请求后,根据最新数据生成rdb文件发送给slave,slave将rdb持久化到磁盘,然后加载到slave内存中
      • master全量复制后,继续同步后续的数据给slave,异步复制的方式
      • 支持断点续传,master和slave保存replica offset
      • 无磁盘化复制 repl-diskless-sync yes
      • 过期key处理,slave不会过期key,需要同步master过期key操作
    • 复制完整流程

      • slave node启动,仅仅保存master node信息,包括master node和host和ip(redis.conf中的replicaof配置),此时复制流程未开始
      • slave node内部有个定时任务,每秒检查是否有新的master node需要连接和复制,如果发现,则跟master node建立网络连接
      • slave node发送ping命令给master node
      • 口令认证,如果master设置requirepass,那么slave node必须发送masterauth口令进行认证
      • master node第一次执行全量复制,将所有数据发送给slave node
      • master node 后续持续将写命令异步复制给slave node

6.2 Redis主从架构配置

注意:

主从架构瓶颈在于,主从节点数据内容是一致的,即全量副本。那么单机内存容量就是Redis主从架构集群的瓶颈,如果遇到Redis节点对应的硬件设备低于其他节点,那么还会出现木桶效应。

当然为了优化单机性能,提供缓存淘汰策略,如LRU,保证内存中的数据都是有用无冗余的数据,但是也无法解决单机瓶颈的问题。

为解决单机瓶颈问题,Redis集群方案势在必行,支持横向扩容,增加master节点,数据分散到各个master节点上,Redis Cluster是官方提供的集群方案,多个master节点,读写分离架构,高可用。

Redis集群场景:

  • 海量数据: Redis Cluster方案
  • 常规数据量:Replication方案,master + 多slave + 哨兵集群+ 读写分离

主从复制,主机接受读写请求,从机同步主机数据

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
#指定主节点。旧版本是:slaveof
replicaof

#master的密码
masterauth

# Redis新版本从机配置
################################# REPLICATION #################################

# Master-Replica replication. Use replicaof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
# +------------------+ +---------------+
# | Master | ---> | Replica |
# | (receive writes) | | (exact copy) |
# +------------------+ +---------------+
#
# 1) Redis replication is asynchronous, but you can configure a master to
# stop accepting writes if it appears to be not connected with at least
# a given number of replicas.
# 2) Redis replicas are able to perform a partial resynchronization with the
# master if the replication link is lost for a relatively small amount of
# time. You may want to configure the replication backlog size (see the next
# sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
# network partition replicas automatically try to reconnect to masters
# and resynchronize with them.
#
# replicaof <masterip> <masterport>
replicaof 192.168.1.105 6379

# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the replica to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the replica request.
#
# masterauth <master-password>

# 注意,如果在从机上执行写操作,报错
127.0.0.1:6379> set redis in-action
(error) READONLY You can't write against a read only replica.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看主从复制集群状态
[root@centos-p162 redis]# redis-cli -h 192.168.1.161 -p 6379
192.168.1.161:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.162,port=6379,state=online,offset=997112,lag=0
slave1:ip=192.168.1.160,port=6379,state=online,offset=997112,lag=0
master_replid:c32bc282c4f142e5c093b676cc0b0c1c0a37aef6
master_replid2:0ae1879931ea6ce640f40654e7e5b30217853f31
master_repl_offset:997126
second_repl_offset:914459
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:997126

6.3 Redis主从高可用

高可用受很多因素影响,如何保证高可用是每个架构师必须考虑的问题。

系统架构要求4个9什么意思? 开发的系统保证在全年365天99.99%时间内可用(现在主流的说法是5个9,那么架构难度就更高),那么称为高可用。

如何描述Redis主从高可用?

如果一主多从的Redis主从复制架构,没有做高可用方案,其中一台从机挂掉,不影响可用性;但是如果主机挂掉,那么不能接受写请求,Redis就不可用了,请求就会打到MySQL数据库,后果非常严重。

高可用方案,故障转移failover,master故障时,自动检测,并将slave自动切换为新的master,主备切换。但是注意主备切换过程,无法对外提供服务。

7 Redis哨兵机制

7.1 Redis数据丢失问题

7.1.1 异步复制

master异步复制数据给slave节点,在slave节点未接到数据时,master 宕机,此时slave被切换为master对外提供读写服务,那么旧的master想要复制的那部分数据丢失。

7.1.2 网络分区,脑裂问题

网络分区导致出现两个master对外提供读写服务,网络恢复后会丢失部分数据

7.1.3 Redis减少数据丢失方案

Redis提供解决方案,控制若干从节点复制数据的延迟不能超过指定时间,如果超限,那么master将暂缓对外提供服务,内部等待从节点复制追上master要求的阈值。此方案能够将由于异步复制数据带来的数据丢失问题,限制在可控范围。

如果配置该方案,那么客户端需要做降级处理,比如临时将数据存储到本地磁盘或者依赖消息中间件临时存放队列,然后等redis master节点继续提供写请求服务时,再将数据写入master。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# It is possible for a master to stop accepting writes if there are less than
# N replicas connected, having a lag less or equal than M seconds.
#
# The N replicas need to be in "online" state.
#
# The lag in seconds, that must be <= the specified value, is calculated from
# the last ping received from the replica, that is usually sent every second.
#
# This option does not GUARANTEE that N replicas will accept the write, but
# will limit the window of exposure for lost writes in case not enough replicas
# are available, to the specified number of seconds.
#
# For example to require at least 3 replicas with a lag <= 10 seconds use:
# 举例:需要满足至少3个从节点数据同步与master对比延迟低于10s
min-replicas-to-write 3
min-replicas-max-lag 10
#
# Setting one or the other to 0 disables the feature.
#
# By default min-replicas-to-write is set to 0 (feature disabled) and
# min-replicas-max-lag is set to 10.

7.2 哨兵简介

Redis主从复制缺点:

无法监控redis master动态选举,需要用sentinel机制完成动态选举。

  • 哨兵(Sentinel)进程是用于监控Redis集群中Master主服务器工作的状态
  • 在Master主服务器发送故障的时候,可以实现Master和Slave服务器切换,保证系统的高可用(HA)

  • 哨兵已经被集成在redis2.6+版本中,Redis的哨兵模式在2.8版本后就已经稳定

7.3 哨兵进程的作用

  • 监控(Monitoring)

    哨兵不断检查Master和Slave是否运转正常

  • 提醒(Notification)

    当监控的某个Redis节点出现的问题时,哨兵可以通过API向管理员或者其他应用程序发送通知

  • 自动故障迁移(Automatic failover)

    当一个Master不能正常工作时,哨兵会开始自动故障迁移操作,具体操作如下:

    • 哨兵将失效Master的其中一个Slave升级为新的Master,并让失效Master的其他Slave改为复制新的Master;
    • 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master;
    • Master和Slave服务器切换后,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置文件的内容都会发生相应的改变,即Master主服务器的redis.conf配置文件中会多一行replicaof的配置,sentinel.conf的监控目标会随之调换;

7.4 哨兵原理

7.4.1 Redis宕机
  • sdown 主观宕机

    如果一个哨兵自己觉得一个master宕机,那么标记该master为主观宕机

    哨兵ping一个master,超过is-master-down-after-milliseconds指定的毫秒数,认为master宕机

  • odown 客观宕机

    如果quorum数量的哨兵都认为某个master宕机,那么该master为客观宕机

7.4.2 哨兵和slave集群的自动发现机制

哨兵之间的发现,通过redis的pub/

sub系统实现,每个哨兵会往sentinel:hello这个channel发送消息,其他哨兵均可消费该消息,感知其他哨兵的存在,每间隔2s,每个哨兵都往自己监控的某个master+slaves对应的sentinel:hello channel发送一个消息,内容是自己的host、ip和runid还有该master的监控配置

每个哨兵也会去监听自己监控的每个master+slaves对应的sentinel:hello channel,然后感知同样在监听该master+slaves的其他哨兵的存在

每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步

7.4.3 slave配置的自动纠正

哨兵会负责自动纠正slave的一些配置,比如slave如果成为潜在的master候选人,哨兵确保slave在复制现有master的数据

如果slave连接到一个错误的master,比如故障转移之后,那么哨兵会确保它们连接到正确的master上。

7.4.4 slave -> master选举算法

如果一个master被认为odown,而majority哨兵都允许主备切换,那么某个哨兵就会执行主备切换操作,此时需要先选举出一个slave,会考虑slave的信息:

  • 跟master断开连接的时长
  • slave优先级
  • 复制的offset
  • runid

如果一个slave和master断开连接已经超过down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master

对slave排序:

  • 按照slave优先级排序,slave priority越低,优先级就越高
  • 如果slave priority相同,那么看replica offset,哪个slave复制越多数据,offset越靠后,优先级越高
  • 如果以上两个条件均相同,选择runid较小那个slave

7.5 Redis哨兵3节点搭建

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
# port <sentinel-port>
# The port that this sentinel instance will run on
port 26379
# By default Redis Sentinel does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis-sentinel.pid when
# daemonized.
daemonize yes
# When running daemonized, Redis Sentinel writes a pid file in
# /var/run/redis-sentinel.pid by default. You can specify a custom pid file
# location here.
pidfile "/var/run/redis-sentinel.pid"

# Specify the log file name. Also the empty string can be used to force
# Sentinel to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile "/usr/apps/redis/logs/redis-sentinel.log"
# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
# The above two configuration directives are useful in environments where,
# because of NAT, Sentinel is reachable from outside via a non-local address.
#
# When announce-ip is provided, the Sentinel will claim the specified IP address
# in HELLO messages used to gossip its presence, instead of auto-detecting the
# local address as it usually does.
#
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
# will announce the specified TCP port.
#
# The two options don't need to be used together, if only announce-ip is
# provided, the Sentinel will announce the specified IP and the server port
# as specified by the "port" option. If only announce-port is provided, the
# Sentinel will announce the auto-detected local IP and the specified port.
#
# Example:
#
# sentinel announce-ip 1.2.3.4

# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
# for the process to don't interfere with administrative tasks such as
# unmounting filesystems.
dir "/usr/apps/redis/sentinel"
# sentinel parallel-syncs <master-name> <numreplicas>
#
# How many replicas we can reconfigure to point to the new replica simultaneously
# during the failover. Use a low number if you use the replicas to serve query
# to avoid that all the replicas will be unreachable at about the same
# time while performing the synchronization with the master.
sentinel monitor mymaster 192.168.1.160 6379 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 哨兵集群最少3个节点,才能顺利进行选举。哨兵集群可以监控多个主从复制集群
[root@centos-p160 redis]# ./bin/redis-sentinel ./bin/sentinel.conf
[root@centos-p160 redis]# tail logs/redis-sentinel.log
7594:X 23 Sep 2019 16:42:47.487 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7594:X 23 Sep 2019 16:42:47.487 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=7594, just started
7594:X 23 Sep 2019 16:42:47.487 # Configuration loaded
7595:X 23 Sep 2019 16:42:47.490 * Increased maximum number of open files to 10032 (it was originally set to 1024).
7595:X 23 Sep 2019 16:42:47.492 * Running mode=sentinel, port=26379.
7595:X 23 Sep 2019 16:42:47.492 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
7595:X 23 Sep 2019 16:42:47.492 # Sentinel ID is 2305faed6805cf7db752cff6d4573426ffa9957b
7595:X 23 Sep 2019 16:42:47.492 # +monitor master mymaster 192.168.1.160 6379 quorum 2
# 记录新增一个哨兵节点
7595:X 23 Sep 2019 16:42:58.030 * +sentinel sentinel 756d6dec0cf2dbfb1bbce9b663d749643a826be1 192.168.1.161 26379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 16:43:05.470 * +sentinel sentinel f4ce4c397dda166939651ffe0f4878a2a7f49d3c 192.168.1.162 26379 @ mymaster 192.168.1.160 6379
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
# 查看哨兵集群
[root@centos-p161 redis]# redis-cli -h 192.168.1.161 -p 26379
192.168.1.161:26379> sentinel sentinels mymaster
1) 1) "name"
2) "f4ce4c397dda166939651ffe0f4878a2a7f49d3c"
3) "ip"
4) "192.168.1.162"
5) "port"
6) "26379"
7) "runid"
8) "f4ce4c397dda166939651ffe0f4878a2a7f49d3c"
9) "flags"
10) "sentinel"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "1018"
19) "last-ping-reply"
20) "1018"
21) "down-after-milliseconds"
22) "30000"
23) "last-hello-message"
24) "372"
25) "voted-leader"
26) "?"
27) "voted-leader-epoch"
28) "1"
2) 1) "name"
2) "2305faed6805cf7db752cff6d4573426ffa9957b"
3) "ip"
4) "192.168.1.160"
5) "port"
6) "26379"
7) "runid"
8) "2305faed6805cf7db752cff6d4573426ffa9957b"
9) "flags"
10) "sentinel"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "1018"
19) "last-ping-reply"
20) "1018"
21) "down-after-milliseconds"
22) "30000"
23) "last-hello-message"
24) "1110"
25) "voted-leader"
26) "?"
27) "voted-leader-epoch"
28) "1"
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
#测试容灾,哨兵监控160主机master,当master宕机,然后再恢复,查看如下日志

# 主观宕机
7595:X 23 Sep 2019 21:42:48.288 # +sdown master mymaster 192.168.1.160 6379
# 客观宕机
7595:X 23 Sep 2019 21:42:48.355 # +odown master mymaster 192.168.1.160 6379 #quorum 2/2
7595:X 23 Sep 2019 21:42:48.355 # +new-epoch 3
7595:X 23 Sep 2019 21:42:48.355 # +try-failover master mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:48.363 # +vote-for-leader 2305faed6805cf7db752cff6d4573426ffa9957b 3
7595:X 23 Sep 2019 21:42:48.411 # 756d6dec0cf2dbfb1bbce9b663d749643a826be1 voted for 756d6dec0cf2dbfb1bbce9b663d749643a826be1 3
7595:X 23 Sep 2019 21:42:48.420 # f4ce4c397dda166939651ffe0f4878a2a7f49d3c voted for 2305faed6805cf7db752cff6d4573426ffa9957b 3
7595:X 23 Sep 2019 21:42:48.441 # +elected-leader master mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:48.441 # +failover-state-select-slave master mymaster 192.168.1.160 6379
# 选举出161 slave切换为新的master
7595:X 23 Sep 2019 21:42:48.513 # +selected-slave slave 192.168.1.161:6379 192.168.1.161 6379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:48.513 * +failover-state-send-slaveof-noone slave 192.168.1.161:6379 192.168.1.161 6379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:48.568 * +failover-state-wait-promotion slave 192.168.1.161:6379 192.168.1.161 6379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:48.677 # +promoted-slave slave 192.168.1.161:6379 192.168.1.161 6379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:48.677 # +failover-state-reconf-slaves master mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:48.709 * +slave-reconf-sent slave 192.168.1.162:6379 192.168.1.162 6379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:49.485 # -odown master mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:49.673 * +slave-reconf-inprog slave 192.168.1.162:6379 192.168.1.162 6379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:49.673 * +slave-reconf-done slave 192.168.1.162:6379 192.168.1.162 6379 @ mymaster 192.168.1.160 6379
7595:X 23 Sep 2019 21:42:49.728 # +failover-end master mymaster 192.168.1.160 6379
# 切换master到161主机
7595:X 23 Sep 2019 21:42:49.728 # +switch-master mymaster 192.168.1.160 6379 192.168.1.161 6379
# 重启160,此时该主机已经变为slave
7595:X 23 Sep 2019 21:42:49.728 * +slave slave 192.168.1.162:6379 192.168.1.162 6379 @ mymaster 192.168.1.161 6379
7595:X 23 Sep 2019 21:42:49.728 * +slave slave 192.168.1.160:6379 192.168.1.160 6379 @ mymaster 192.168.1.161 6379
7595:X 23 Sep 2019 21:43:19.761 # +sdown slave 192.168.1.160:6379 192.168.1.160 6379 @ mymaster 192.168.1.161 6379
7595:X 23 Sep 2019 21:45:28.487 # -sdown slave 192.168.1.160:6379 192.168.1.160 6379 @ mymaster 192.168.1.161 6379

二、Redis集群搭建

1 环境基础

为了能更好的模拟生产环境的网络拓扑,Redis集群需要在多节点部署,实现方案采用CentOS集群。

1.1 CentOS集群搭建

  • 安装虚拟机软件

    虚拟机软件VirtualBox

    • 创建虚拟机实例,根据引导进行创建,选择CentOS镜像进行Linux系统安装,注意网络配置选择”桥接模式”
  • 下载CentOS镜像

    CentOS官网

  • 安装CentOS后进行网络配置,使虚拟机中的CentOS可以上网

    • static 虚拟机IP地址
    • 修改/etc/hosts 本机IP地址映射域名,使得宿主机可以访问
  • SecureCRT或Xshell在宿主机上管理虚拟机

  • 虚拟机防火墙关闭,或是放行需要的端口号

  • CenOS通过ssh配置免密通信

1.2 Redis集群环境搭建

1.2.1 Redis开机自启动配置
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
# 将redis初始化脚本复制到/etc/init.d目录下
cp /usr/apps/redis-5.0.5/utils/redis_init_script /etc/init.d

# redis_init_script配置文件内容如下:
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.

### BEGIN INIT INFO
# Provides: redis_6379
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Redis data structure server
# Description: Redis data structure server. See https://redis.io
### END INIT INFO
#端口号配置
REDISPORT=6379
#redis-server路径
EXEC=/usr/apps/redis/bin/redis-server
#redis-cli路径
CLIEXEC=/usr/apps/redis/bin/redis-cli

PIDFILE=/var/run/redis_${REDISPORT}.pid
#CONF="/etc/redis/${REDISPORT}.conf"
#redis配置文件路径
CONF="/usr/apps/redis/bin/redis.conf"

# 配置好即可在/etc/init.d路径下执行 ./redis_init_script start启动redis
# 注意:如果重命名脚本,需要chmod 777 新脚本名称
# 脚本加入如下注释:
# chkconfig: 2345 90 10
# description: Redis is a persistent key-value database
# 开启脚本随系统启动而执行,即开机就启动redis
chkconfig redis_init_script on

1.3 Redis集群

Redis集群官方文档

Redis3.0以后推出redis cluster集群方案,redis cluster集群保证高可用、高性能、高可扩展性。

1.3.1 Redis集群策略

redis-Cluster第一篇分区方案&应用场景篇

之所以有如下多种Redis集群策略,是因为Redis官网3.0版本才推出分布式解决方案,Redis Cluster。

  • Twitter: twemproxy(基于代理层的redis集群方案)
  • 豌豆荚:codis(基于代理层的redis集群方案)
  • 官方:redis cluster(无中间代理层)
1.3.2 Redis集群架构细节

1569068020757

如上图所示,该集群中包含 6 个 Redis 节点,3主3从,分别为M1,M2,M3,S1,S2,S3。除了主从 Redis 节点之间进行数据复制外,所有 Redis 节点之间采用 Gossip 协议进行通信,交换维护节点元数据信息。

数据分片策略

Redis Cluster数据分片机制

分布式数据存储方案中最为重要的一点就是数据分片,也就是所谓的 Sharding。

为了使得集群能够水平扩展,首要解决的问题就是如何将整个数据集按照一定的规则分配到多个节点上,常用的数据分片的方法有:

  • 范围分片

    范围分片假设数据集是有序,将顺序相临近的数据放在一起,可以很好的支持遍历操作。范围分片的缺点是面对顺序写时,会存在热点。比如日志类型的写入,一般日志的顺序都是和时间相关的,时间是单调递增的,因此写入的热点永远在最后一个分片。

  • 哈希分片

  • 一致性哈希算法

  • 虚拟哈希槽

Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,计算公式:slot = CRC16(key) & 16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。

Redis 集群提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的情况下,可以为集群添加节点进行扩容也可以下线部分节点进行缩容。可以说,槽是 Redis 集群管理数据的基本单位,集群伸缩就是槽和数据在节点之间的移动。

1569068491782

Redis集群描述

  • Redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽;

  • 节点的fail是通过集群中超过半数的节点检测失效才生效;

  • 客户端与redis节点直连,不需要中间Proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可;

  • redis-cluster把所有物理节点映射到[0-16383] slot上,cluster负责维护node<->slot<->value;

    Redis集群中内置16384个哈希槽,当需要在Redis集群中放置一个key-value时,redis先对key使用crc16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。

1.3.3 Redis-cluster 投票:容错

如何判断redis cluster中master主节点fail?

如何判断redis cluster集群fail?

  • 节点失效判断:集群中所有master参与投票,如果半数以上master节点与其中一个master节点通信超过(cluster-node-timeout),认为该master节点挂掉;
  • 集群失效判断:什么时候整个集群不可用(cluster_state:fail)
    • 如果集群任意master挂掉,且当前master无slave,则集群进入fail状态。可以理解成集群的[0-16383] slot映射不完全时进入fail状态;
    • 如果集群超过半数以上master挂掉,无论是否存在slave,集群进入fail状态;
1.3.4 Ruby环境

Ruby下载

Redis集群需要使用集群管理脚本redis-trib.rb,依赖Ruby环境。

  • 安装Ruby

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # yum方式安装ruby
    yum install -y ruby

    ruby -v
    # 查看安装后的ruby版本
    ruby 2.0.0p648 (2015-12-16) [x86_64-linux]


    yum install rubygems
    • 采坑:redis requires ruby version 2.3.0,CentOS7 yum库中ruby的版本支持到 2.0.0,但是gem安装redis需要最低是2.3.0,采用rvm来更新ruby

      1
      2
      3
      4
      5
      # 安装curl
      yum -y install curl
      # 安装rvm
      gpg2 --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
      curl -L get.rvm.io | bash -s stable
      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
      # 执行rvm安装时遇到异常
      [root@localhost software]# curl -L get.rvm.io | bash -s stable
      % Total % Received % Xferd Average Speed Time Time Time Current
      Dload Upload Total Spent Left Speed
      100 194 100 194 0 0 46 0 0:00:04 0:00:04 --:--:-- 49
      100 24535 100 24535 0 0 1502 0 0:00:16 0:00:16 --:--:-- 6385
      Downloading https://github.com/rvm/rvm/archive/1.29.9.tar.gz
      Downloading https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc
      gpg: Signature made Wed 10 Jul 2019 04:31:02 PM CST using RSA key ID 39499BDB
      gpg: Can't check signature: No public key
      GPG signature verification failed for '/usr/local/rvm/archives/rvm-1.29.9.tgz' - 'https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc'! Try to install GPG v2 and then fetch the public key:

      gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB

      or if it fails:

      command curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -
      command curl -sSL https://rvm.io/pkuczynski.asc | gpg2 --import -

      In case of further problems with validation please refer to https://rvm.io/rvm/security

      # 安装错误提示,先执行如下命令
      gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
      # 执行结果
      gpg: requesting key D39DC0E3 from hkp server pool.sks-keyservers.net
      gpg: requesting key 39499BDB from hkp server pool.sks-keyservers.net
      gpg: key D39DC0E3: "Michal Papis (RVM signing) <mpapis@gmail.com>" not changed
      gpg: key 39499BDB: public key "Piotr Kuczynski <piotr.kuczynski@gmail.com>" imported
      gpg: no ultimately trusted keys found
      gpg: Total number processed: 2
      gpg: imported: 1 (RSA: 1)
      gpg: unchanged: 1
      # 继续安装rvm
      [root@localhost software]# curl -L get.rvm.io | bash -s stable
      % Total % Received % Xferd Average Speed Time Time Time Current
      Dload Upload Total Spent Left Speed
      100 194 100 194 0 0 287 0 --:--:-- --:--:-- --:--:-- 287
      100 24535 100 24535 0 0 2360 0 0:00:10 0:00:10 --:--:-- 5422
      Downloading https://github.com/rvm/rvm/archive/1.29.9.tar.gz
      Downloading https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc
      gpg: Signature made Wed 10 Jul 2019 04:31:02 PM CST using RSA key ID 39499BDB
      gpg: Good signature from "Piotr Kuczynski <piotr.kuczynski@gmail.com>"
      gpg: WARNING: This key is not certified with a trusted signature!
      gpg: There is no indication that the signature belongs to the owner.
      Primary key fingerprint: 7D2B AF1C F37B 13E2 069D 6956 105B D0E7 3949 9BDB
      GPG verified '/usr/local/rvm/archives/rvm-1.29.9.tgz'
      Creating group 'rvm'
      Installing RVM to /usr/local/rvm/
      Installation of RVM in /usr/local/rvm/ is almost complete:
      # rvm需要重新登录系统才生效
      * First you need to add all users that will be using rvm to 'rvm' group,
      and logout - login again, anyone using rvm will be operating with `umask u=rwx,g=rwx,o=rx`.

      * To start using RVM you need to run `source /etc/profile.d/rvm.sh`
      in all your open shell windows, in rare cases you need to reopen all shell windows.
      * Please do NOT forget to add your users to the rvm group.
      The installer no longer auto-adds root or users to the rvm group. Admins must do this.
      Also, please note that group memberships are ONLY evaluated at login time.
      This means that users must log out then back in before group membership takes effect!
      Thanks for installing RVM 🙏
      Please consider donating to our open collective to help us maintain RVM.

      👉 Donate: https://opencollective.com/rvm/donate
  • 修改rvm下载ruby的源,到Ruby China镜像

    1
    2
    3
    4
    5
    gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/

    [root@centos-p160 ~]# gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
    https://gems.ruby-china.com/ added to sources
    https://rubygems.org/ removed from sources
  • 查看rvm库中已知的ruby版本

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

    [root@localhost ~]# rvm list known
    # MRI Rubies
    [ruby-]1.8.6[-p420]
    [ruby-]1.8.7[-head] # security released on head
    [ruby-]1.9.1[-p431]
    [ruby-]1.9.2[-p330]
    [ruby-]1.9.3[-p551]
    [ruby-]2.0.0[-p648]
    [ruby-]2.1[.10]
    [ruby-]2.2[.10]
    [ruby-]2.3[.8]
    [ruby-]2.4[.6]
    [ruby-]2.5[.5]
    [ruby-]2.6[.3]
    [ruby-]2.7[.0-preview1]
    ruby-head

    # for forks use: rvm install ruby-head-<name> --url https://github.com/github/ruby.git --branch 2.2
  • 安装一个ruby版本

    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
    rvm install 2.6.3
    # 过程比较耗时
    [root@localhost ~]# rvm install 2.6.3
    Searching for binary rubies, this might take some time.
    No binary rubies available for: centos/7/x86_64/ruby-2.6.3.
    Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
    Checking requirements for centos.
    Installing requirements for centos.
    Installing required packages: patch, autoconf, automake, bison, libffi-devel, libtool, patch, readline-devel, sqlite-devel, zlib-devel, openssl-devel.........................................................................................................................................................
    Requirements installation successful.
    Installing Ruby from source to: /usr/local/rvm/rubies/ruby-2.6.3, this may take a while depending on your cpu(s)...
    ruby-2.6.3 - #downloading ruby-2.6.3, this may take a while depending on your connection...
    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    100 13.8M 100 13.8M 0 0 11697 0 0:20:40 0:20:40 --:--:-- 13006
    ruby-2.6.3 - #extracting ruby-2.6.3 to /usr/local/rvm/src/ruby-2.6.3.....
    ruby-2.6.3 - #configuring......................................................................
    ruby-2.6.3 - #post-configuration..
    ruby-2.6.3 - #compiling................................................................................................
    ruby-2.6.3 - #installing...............................
    ruby-2.6.3 - #making binaries executable..
    ruby-2.6.3 - #downloading rubygems-3.0.6
    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    100 866k 100 866k 0 0 5761 0 0:02:33 0:02:33 --:--:-- 6064
    No checksum for downloaded archive, recording checksum in user configuration.
    ruby-2.6.3 - #extracting rubygems-3.0.6.....
    ruby-2.6.3 - #removing old rubygems........
    ruby-2.6.3 - #installing rubygems-3.0.6...............................................
    ruby-2.6.3 - #gemset created /usr/local/rvm/gems/ruby-2.6.3@global
    ruby-2.6.3 - #importing gemset /usr/local/rvm/gemsets/global.gems................................................................
    ruby-2.6.3 - #generating global wrappers.......
    ruby-2.6.3 - #gemset created /usr/local/rvm/gems/ruby-2.6.3
    ruby-2.6.3 - #importing gemsetfile /usr/local/rvm/gemsets/default.gems evaluated to empty gem list
    ruby-2.6.3 - #generating default wrappers.......
    ruby-2.6.3 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
    Install of ruby-2.6.3 - #complete
    Ruby was built without documentation, to build it run: rvm docs generate-ri
  • rvm命令选择ruby版本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 使用ruby版本
    rvm use 2.6.3
    Using /usr/local/rvm/gems/ruby-2.6.3
    # 默认使用ruby版本
    rvm use 2.6.3 --default
    # 卸载已知ruby版本
    rvm remove 2.0.0
    # 查看ruby版本
    ruby -v
    ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
  • 安装ruby和redis的接口程序

    1
    2
    3
    4
    5
    6
    7
    8
    gem install redis
    #安装结果信息
    Fetching redis-4.1.3.gem
    Successfully installed redis-4.1.3
    Parsing documentation for redis-4.1.3
    Installing ri documentation for redis-4.1.3
    Done installing documentation for redis after 2 seconds
    1 gem installed
  • 复制redis-trib.rb到安装目录bin文件夹下
1.3.5 单机安装Redis Cluster

Redis Cluster最少需要3台Master和3台Slave。

  • 复制Redis安装路径/bin目录,重命名7001-7006,针对每个Redis实例修改redis.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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 7001

# If a pid file is specified, Redis writes it where specified at startup
# and removes it at exit.
#
# When the server runs non daemonized, no pid file is created if none is
# specified in the configuration. When the server is daemonized, the pid file
# is used even if not specified, defaulting to "/var/run/redis.pid".
#
# Creating a pid file is best effort: if Redis is not able to create it
# nothing bad happens, the server will start and run normally.
pidfile /var/run/redis_7001.pid

# Specify the log file name. Also the empty string can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile "/usr/apps/redis/logs/redis_7001.log"

# The filename where to dump the DB
dbfilename dump_7001.rdb

# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.

appendonly yes

# The name of the append only file (default: "appendonly.aof")

appendfilename "appendonly_7001.aof"


################################ REDIS CLUSTER ###############################
#
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however
# in order to mark it as "mature" we need to wait for a non trivial percentage
# of users to deploy it in production.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# Normal Redis instances can't be part of a Redis Cluster; only nodes that are
# started as cluster nodes can. In order to start a Redis instance as a
# cluster node enable the cluster support uncommenting the following:
# 开启集群模式
cluster-enabled yes

# Every cluster node has a cluster configuration file. This file is not
# intended to be edited by hand. It is created and updated by Redis nodes.
# Every Redis Cluster node requires a different cluster configuration file.
# Make sure that instances running in the same system do not have
# overlapping cluster configuration file names.
# 使得集群中每个节点单独有自己的节点配置文件,否则报错:
# Sorry, the cluster configuration file nodes.conf is already used by a different Redis Cluster node. Please make sure that different nodes use different cluster configuration files.
cluster-config-file nodes-7001.conf
  • 启动7001-7006对应的Redis实例
1
2
3
4
5
6
7
8
[root@localhost 7006]# ps -ef | grep redis
root 24213 1 0 02:58 ? 00:00:01 ./redis-server *:7001 [cluster]
root 24324 1 0 03:03 ? 00:00:00 ./redis-server *:7002 [cluster]
root 28127 1 0 03:15 ? 00:00:00 ./redis-server *:7003 [cluster]
root 28260 1 0 03:27 ? 00:00:00 ./redis-server *:7004 [cluster]
root 28294 1 0 03:28 ? 00:00:00 ./redis-server *:7005 [cluster]
root 28330 1 0 03:29 ? 00:00:00 ./redis-server *:7006 [cluster]
root 28337 21226 0 03:29 pts/1 00:00:00 grep --color=auto redis
  • 创建redis集群
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@localhost redis]# ./redis-trib.rb create --replicas 1 192.168.1.101:7001 192.168.1.101:7002 192.168.1.101:7003 192.168.1.101:7004 192.168.1.101:7005 192.168.1.101:7006
# redis-trib.rb 废弃无效
WARNING: redis-trib.rb is not longer available!
You should use redis-cli instead.

All commands and features belonging to redis-trib.rb have been moved
to redis-cli.
In order to use them you should call redis-cli with the --cluster
option followed by the subcommand name, arguments and options.

Use the following syntax:
redis-cli --cluster SUBCOMMAND [ARGUMENTS] [OPTIONS]

Example:
redis-cli --cluster create 192.168.1.101:7001 192.168.1.101:7002 192.168.1.101:7003 192.168.1.101:7004 192.168.1.101:7005 192.168.1.101:7006 --cluster-replicas 1

To get help about all subcommands, type:
redis-cli --cluster help
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
# redis-trib.rb 废弃无效,改为redis-cli --cluster
[root@localhost bin]# ./redis-cli --cluster create 192.168.1.101:7001 192.168.1.101:7002 192.168.1.101:7003 192.168.1.101:7004 192.168.1.101:7005 192.168.1.101:7006 --cluster-replicas 1
# 创建集群的详细信息
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.1.101:7004 to 192.168.1.101:7001
Adding replica 192.168.1.101:7005 to 192.168.1.101:7002
Adding replica 192.168.1.101:7006 to 192.168.1.101:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 644e74b549c7d63c48c3cc5009963cae6f63e404 192.168.1.101:7001
slots:[0-5460] (5461 slots) master
M: b195eb876cad13a0c5218e772a8452441278a0cf 192.168.1.101:7002
slots:[5461-10922] (5462 slots) master
M: 96172fa5e8c69252ab5a0754ceccb20e6abe1993 192.168.1.101:7003
slots:[10923-16383] (5461 slots) master
S: 12d9d7554134a98cc9653e8d1b95e30a27791208 192.168.1.101:7004
replicates 96172fa5e8c69252ab5a0754ceccb20e6abe1993
S: b747811dd023a59f54109a70bfc86f905bf40aeb 192.168.1.101:7005
replicates 644e74b549c7d63c48c3cc5009963cae6f63e404
S: dc9454fe91014045ea81bb0c553767a1e3417140 192.168.1.101:7006
replicates b195eb876cad13a0c5218e772a8452441278a0cf
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 192.168.1.101:7001)
M: 644e74b549c7d63c48c3cc5009963cae6f63e404 192.168.1.101:7001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 96172fa5e8c69252ab5a0754ceccb20e6abe1993 192.168.1.101:7003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: b195eb876cad13a0c5218e772a8452441278a0cf 192.168.1.101:7002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: dc9454fe91014045ea81bb0c553767a1e3417140 192.168.1.101:7006
slots: (0 slots) slave
replicates b195eb876cad13a0c5218e772a8452441278a0cf
S: b747811dd023a59f54109a70bfc86f905bf40aeb 192.168.1.101:7005
slots: (0 slots) slave
replicates 644e74b549c7d63c48c3cc5009963cae6f63e404
S: 12d9d7554134a98cc9653e8d1b95e30a27791208 192.168.1.101:7004
slots: (0 slots) slave
replicates 96172fa5e8c69252ab5a0754ceccb20e6abe1993
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
  • 查看集群命令
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
# 客户端连接 -c 表示集群方式连接
[root@localhost bin]# ./redis-cli -h 192.168.1.101 -p 7001 -c
# 查看集群状态
192.168.1.101:7001> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:1168
cluster_stats_messages_pong_sent:1148
cluster_stats_messages_sent:2316
cluster_stats_messages_ping_received:1143
cluster_stats_messages_pong_received:1168
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:2316

# 查看集群节点
192.168.1.101:7003> cluster nodes
b747811dd023a59f54109a70bfc86f905bf40aeb 192.168.1.101:7005@17005 slave 644e74b549c7d63c48c3cc5009963cae6f63e404 0 1569012946031 1 connected
96172fa5e8c69252ab5a0754ceccb20e6abe1993 192.168.1.101:7003@17003 myself,master - 0 1569012945000 3 connected 10923-16383
12d9d7554134a98cc9653e8d1b95e30a27791208 192.168.1.101:7004@17004 slave 96172fa5e8c69252ab5a0754ceccb20e6abe1993 0 1569012944000 4 connected
b195eb876cad13a0c5218e772a8452441278a0cf 192.168.1.101:7002@17002 master - 0 1569012947069 2 connected 5461-10922
dc9454fe91014045ea81bb0c553767a1e3417140 192.168.1.101:7006@17006 slave b195eb876cad13a0c5218e772a8452441278a0cf 0 1569012943000 6 connected
644e74b549c7d63c48c3cc5009963cae6f63e404 192.168.1.101:7001@17001 master - 0 1569012946000 1 connected 0-5460

2 数据分布算法简介

  • hash算法

    描述:根据key计算hash值,hash与节点数取模,即求余数,根据取模结果将数据放在对应节点上。

    问题:任意节点宕机,节点数变化,会影响取模结果,大部分请求失效,大量数据需要重新计算存入缓存。

  • 一致性hash算法

    描述:节点在圆环上,根据key求hash值,key值落在圆环上,就会顺时针寻找距离自己最近的一个节点。如果其中一个节点宕机,不会导致缓存全部失效。

    一致性hash虚拟节点,负载均衡。

  • hash slot算法

    描述:槽位数16384固定,数据相对均匀的分布到对应的槽位上。

三、Redis和Lua整合

1 Lua介绍及环境搭建

1.1 Lua介绍

Lua是一种轻量小巧的脚步语言,用标准C语言编写并以源码形式开发,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

1.2 Redis使用Lua的好处

  • 减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行
  • 原子操作,Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话,编写脚本的过程中无需担心出现竞态条件
  • 复用性,客户端发送的脚本会永远存在Redis中,意味着其他客户端可以复用这一脚本完成同样逻辑

1.3 Lua安装

Lua官方下载

1
2
3
4
5
6
7
8
9
10
11
12
13
yum -y install readline-devel ncurses-devel
tar -zxvf lua-5.3.5.tar.gz
make linux
make install

# 安装后输入lua -v查看版本号
# 输入lua,进入lua控制台,然后即可输入lua命令
[root@localhost lua-5.3.5]# lua
Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
> print(1)
1
> print("hello world")
hello world

1.4 Redis整合Lua脚本

注意: Redis2.6.0版本开始,通过内置的Lua编译/解释器,可以使用EVAL命令对Lua脚本进行求值。

EVAL命令

1
2
# Redis客户端,执行如下命令
EVAL script numkeys key [key ... ] arg [arg ... ]
  • script参数: 一段Lua脚本,它会被运行在Redis服务器上下文中,该Lua脚本不必定义为一个Lua函数
  • numkeys参数: 用于指定键名参数的个数
  • key[key …]参数:从EVAL的第三个参数开始算起,使用numkeys个键(key),表示脚本中所用到的那些Redis键(Key),这些键名参数可以在Lua中通过全局变量KEYS数组,用1为基址形式访问(KEYS[1],KEYS[2],以此类推)
  • arg[arg …]参数:可以在Lua中通过全局变量ARGV数组访问,访问形式和KEYS变量类似(ARGV[1]、ARGV[2],诸如此类)