Redis学习整理

redis几种数据类型

1.字符串(string)

2.列表(list)

3.集合(set)

4.哈希(hash)

5.有序集合(zset)

1.字符串

字符串类型是Redis中最基本的数据类型,String可以存储任何形式的字符串,包括二进制数据。

一个string类型允许存储的数据最大容量是512MB。

set 设置指定key的值

语法:set key value

127.0.0.1:6379 > set name zhaoliu
OK
127.0.0.1:6379 > set name "li si"
OK
  • set 设置给定key的值。如果key已存储其他值,则覆盖旧值
  • 如果有空格等其他字符,则使用引号包含。

get 获取指定key的值

语法:get key

127.0.0.1:6379 > get name
"li si"
# 获取不存在的key
127.0.0.1:6379> get name1
(nil)

# 获取不是String类型的key
127.0.0.1:6379> lpush a '111'
(integer) 3
127.0.0.1:6379> get a
(error) WRONGTYPE Operation against a key holding the wrong kind of value
  • 获取指定key的值,如果key不存在,则返回nil,如果key存储的值不是String类型,则返回错误

mset 设置一个或多个key-value对

语法:mset key1 value1 key2 value2

127.0.0.1:6379> mset keya valuea keyb valueb
OK

# 设置已经存在的key
127.0.0.1:6379> mset keya 'i ma a' keyb 'i am b'
OK
  • 如果key存在,则新值覆盖旧值 (和set一样)

mget获取一个或者多个给定key的值

# 获取一个key的值
127.0.0.1:6379> mget keya
1) "i ma a"

# 获取多个key的值
127.0.0.1:6379> mget keya keyb
1) "i ma a"
2) "i am b"

**setnx key不存在时设置key的值 **

127.0.0.1:6379> setnx age 18
(integer) 1

# key存在,设置不成功,返回0
127.0.0.1:6379> setnx name 'xiao bai'
(integer) 0
127.0.0.1:6379> get name
"wang wu"
  • 只能用于设置不存在的key

msetnx 设置一个或多个key-value对,当且仅当所有key不存在

语法:msetnx key1 value1 key2 value2 …

127.0.0.1:6379> msetnx a1 10 a2 20
(integer) 1

# 存在key,设置不成功
127.0.0.1:6379> msetnx num 10 age 10
(integer) 0
  • 设置多个key时,只要其中一个key存在,则所有的值设置失败

getset 设置指定key的值,并返回旧值

127.0.0.1:6379> getset name 'wang wu'
"li si"

127.0.0.1:6379> get name
"wang wu"

# key不存在,返回nil
127.0.0.1:6379> getset aa 'i am aa'
(nil)
  • 当key不存在时,返回nil
  • 当key不是字符串时,报错

**setrange为指定的key进行指定范围value值,从offset处开始重写 **

语法:setrange key offset value

127.0.0.1:6379> getset name 'wang wu'
"li si"

127.0.0.1:6379> get name
"wang wu"

# key不存在,返回nil
127.0.0.1:6379> getset aa 'i am aa'
(nil)

getrange获取指定在key中指定范围(偏移量)的字符串

语法:getrange key start end

127.0.0.1:6379> get keya
"i ma a hello"
127.0.0.1:6379> getrange keya 0 -1
"i ma a hello"

# 获取keya中hello
127.0.0.1:6379> getrange keya 7 -1
"hello"
127.0.0.1:6379> getrange keya 7 12
"hello"

append为指定的key追加值

语法:append key value

# 已存在的key
127.0.0.1:6379> append keya ' hello'
(integer) 12
127.0.0.1:6379> get keya
"i ma a hello"

# 不存在的key
127.0.0.1:6379> append name 'li si'
(integer) 5
127.0.0.1:6379> get name
"li si"
  • 若key存在,则将value追加到原来的key值的末尾
  • 若key不存在,则进行设置该key的值为value,可以理解为执行set操作

strlen获取指定key中值的长度

语法:strlen key

127.0.0.1:6379> strlen keya
(integer) 12

# key不存在,返回0
127.0.0.1:6379> strlen ziruchu
(integer) 0

# key不是字符串时报错
127.0.0.1:6379> lpush db mysql redis
(integer) 2
127.0.0.1:6379> strlen db
(error) WRONGTYPE Operation against a key holding the wrong kind of value
  • 只能获取key类型为字符串的值的长度,否则报错
  • 当key不存在时,返回0

incr将key中存储的数字值增1

语法:incr key

127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2

# key中的值不是数字类型
127.0.0.1:6379> incr name
(error) ERR value is not an integer or out of range
  • key不存在,则进行设置,并且值为1;如果key的值不是数字类型,则报错

decr将key中存在的数字值减1

语法:decr key

127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2

# key中的值不是数字类型
127.0.0.1:6379> incr name
(error) ERR value is not an integer or out of range

incrby 将指定key的值加上给定的增量值

语法:incrby key 数字值

127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2

# key中的值不是数字类型
127.0.0.1:6379> incr name
(error) ERR value is not an integer or out of range

decrby 将指定key的值减去给定的额增量值

语法:decrby key 数字值

127.0.0.1:6379> decrby num3 2
(integer) 8

# key不存在
127.0.0.1:6379> decrby num4 10
(integer) -10
# key中值不是数值型
127.0.0.1:6379> decrby name 10
(error) ERR value is not an integer or out of range

incrbyfloat将key所存储的值加上给定的浮点增量值

语法:incrybyfloat key 增量值

127.0.0.1:6379> get num
"1"
127.0.0.1:6379> incrbyfloat num 5.5
"6.5"
127.0.0.1:6379> incrbyfloat num 5
"11.5"

# 不存在的key
127.0.0.1:6379> incrbyfloat num10 10.1
"10.1"

# 不是数字值 则报错
127.0.0.1:6379> get name
"wang hello"
127.0.0.1:6379> incrbyfloat name 5.5
(error) ERR value is not a valid float

setex 为指定的key设置过期时间

语法:setex key 时间(秒)value

127.0.0.1:6379> setex db 60 db
# 查看过期时间
127.0.0.1:6379> ttl db
(integer) 56

# 不存在的key,相当于set,并设置过期时间
127.0.0.1:6379> setex db 10 mysql
OK
# 替换旧值并设置过期时间
127.0.0.1:6379> setex age 60 19
OK
127.0.0.1:6379> get age
"19"
  • key存在,则旧值替换新值;key不存在,则设置值并设置过期时间

psetex 以毫秒为单位设置key的有效时间

语法:psetex key 毫秒值 value

127.0.0.1:6379> psetex gender 10000 nan
OK
位操作

一个字节由8个二进制位组成。redis有4个位操作命令setbitgetbitbitcountbittop

# 设置一个键值对
127.0.0.1:6379> set foo bar
OK

bar这3个字母分别对应的ASCLL为98,97,114,转成二进制分别为01100010,01100001,01110010

b a r
01100010 01100001 01110010

getbit获取key中值指定偏移量的位(bit),偏移量索引从0开始

语法:getbit key 偏移量

getbit命令 获取一个字符串类型键指定位置的二进制位的值(0或者1),索引从0开始

# 获取bar的第0位,也是b的ASSIC二进制的第一个数字
127.0.0.1:6379> getbit foo 0
(integer) 0
127.0.0.1:6379> getbit foo 1
(integer) 1
127.0.0.1:6379>
  • 获取二进制位的索引超过键值的二进制位的实际长度则默认值为0

setbit 对key中值设置或清除指定偏移量上的位

语法:setbit key偏移量 value

# 设置bar中b对应的二进制位,从0开始数,也是第7个数,从1改0
127.0.0.1:6379> setbit foo 6 0
(integer) 1
  • 若设置的位置超过了二进制位的长度,setbit命令自动将中间的二进制位设置为0
  • 设置一个不存在的键指定的二进制位值会自动将前面的位赋值为0

bitcount获取字符串类key中值是1的二进制位个数

语法:bitcount key [start end]

1)获取所有二进制为1的总数

127.0.0.1:6379> bitcount foo
(integer) 11
127.0.0.1:6379> bitcount foo 0 -1
(integer) 11

2)获取前2个字节指定范围内二进制为1的总数

127.0.0.1:6379> bitcount foo 0 1
(integer) 7

bittop对字符串key进行位运算,并将结果存储在指定key中

语法:bittop 位运算符 结果存储键 key [key1 key2 …]

参数解释:

位运算符有:AND,OR,XOR,NOT

127.0.0.1:6379> set foo1 bar
OK
127.0.0.1:6379> set foo2 aar
OK
127.0.0.1:6379> bitop OR result foo1 foo2
(integer) 3
127.0.0.1:6379> get result
"car"

哈希(Hash)

  • Redis是采用字典结构以键值对的形式存储数据的。

  • 哈希类型的键值也是一种字典结构,存储了字段和字段值的映射,字段值只能是字符串,不支持其他数据类型。

  • 哈希类型不能嵌套其他数据类型。

  • 一个 哈希类型键可以包含至多2^32-1个字段。

  • 哈希类型适合存储对象 :使用对象类别和ID构成键名,使用字段表示对象的属性,字段值则存储属性值。

如下案例:存储ID为1的文章对象,分别使用作者,时间,内容这3个字段 来存储文章的信息

hset 将哈希表 中key的field的值设置为value

语法:hset key field value

# hset设置单个值
127.0.0.1:6379> hset car price 10000
(integer) 1
127.0.0.1:6379> hset car name BMW
(integer) 1
# 字段已存在,执行更新操作,返回0
127.0.0.1:6379> hset car name BMWM
(integer) 0
  • 若hash不存在 ,则创建;若字段已存在,则新值覆盖旧值。
  • hset不区分插入还是更新操作,当执行插入操作时(字段不存在),hset命令返回1;当执行更新操作时(字段存在),hset命令返回0。键不存在时,hset会自动创建该键。

hget获取 哈希表 中的值

127.0.0.1:6379> hget car name
BMWM

hmset 将多个field-value对设置到哈希表key中

语法:hmset key field1 value1 [ field2 value2]

127.0.0.1:6379> hmset cat color white age 2
OK

hmget 获取哈希表指定字段的值

语法:hmget key field1 [field2,field3…]

127.0.0.1:6379> hmget cat color age
1) "white"
2) "2

hgetall获取指定哈希表指定key的所有字段和值

语法:hgetall key

127.0.0.1:6379> hgetall cat
1) "color"
2) "white"
3) "age"
4) "2"

hexists判断一个字段是否存在

语法:hexists key field

127.0.0.1:6379> hexists cat age
(integer) 1
127.0.0.1:6379> hexists cat a
(integer) 0
  • hexists 判断hash表中一个字段是否存在,存在返回1,不存在返回0

hdel删除一个或多个字段

语法:hde1 key field1 [field2 …]

127.0.0.1:6379> hdel car name
(integer) 1
127.0.0.1:6379> hdel cat color age
(integer) 2
  • 返回删除字段的个数

hkeys 获取一个Hash表中所有字段

语法:hkeys key

127.0.0.1:6379> hkeys article
1) "author"
2) "time"

hvalues获取一个Hash表中所有的值

语法:hvalues key

127.0.0.1:6379> hvals article
1) "ziruchu"
2) "2020"

hlen获取字段数量

语法:hlen key

127.0.0.1:6379> hlen article
(integer) 2

hincrby为哈希表key中指定字段的整数值加上增量值

语法:hincrby key field 增量值

127.0.0.1:6379> hmset number id 1 total 3
OK

127.0.0.1:6379> hincrby number id 10
(integer) 11

hincrbyfloat为哈希表key中指定字段的浮点数加上增量值

语法:hincrbyfloat key field 增量值

127.0.0.1:6379> hincrbyfloat number total 10.34
"13.34"

hscan 根据键迭代哈希表中的键值对

语法: hscan key cusssor [match pattern] [count count]

参数: cursor 游标 pattern 匹配模式 count指定从数据集返回多少元素,默认10

127.0.0.1:6379> hscan car 0 MATCH "p*" COUNT 1
1) "0"
2) 1) "price"
   2) "1001"
   3) "produce"
   4) "china"

列表

列表(list)类型可以存储一个有序的字符串列表。常用操作 是向列表两端添加元素,或者获得列表的某个片段。

列表类型内部使用双向链表实现,因此向两端添加元素的时间复杂度为O(1),获得越近两端的元素越快

缺点 :通过索引访问元素比较慢

经典使用场景: 微博上的热搜等有时效的内容

lpush 将一个或者多个值插入列表头部

语法: lpush key value [value1 value2 …]

127.0.0.1:6379> lpush nunber 1
(integer) 1
127.0.0.1:6379> lpush number 2 3
(integer) 2
  • lpush 向列表左边增加元素

lpop从列表左边弹出一个元素并显示该值

语法:lpop key

127.0.0.1:6379> lpop number
"3"

rpush向列表右边增加一个或者多个值

语法:rpush key value1 [value2 …]

127.0.0.1:6379> rpush number 0 -1
(integer) 5

rpop从列表右边弹出一个元素并显示该值

语法:rpop key

127.0.0.1:6379> rpop number
"-1"

llen获取列表中元素的个数

语法:llen key

127.0.0.1:6379> rpop number
"-1"
127.0.0.1:6379> rpop nunber
(nil)
  • key不存在返回nil

lrange获取指定范围的元素

语法:lrange key start end

# 获取所有元素
127.0.0.1:6379> lrange number 0 -1
1) "2"
2) "1"
3) "0"

# 获取前2个元素
127.0.0.1:6379> lrange number 0 1
1) "2"
2) "1"
  • 若start索引位置比end索引位置靠后,则返回空列表
  • 若end大于实际索引范围,则返回列表最右边的元素

lrem删除列表中指定的值

语法:lrem key count value

127.0.0.1:6379> rpush number 2
(integer) 4
127.0.0.1:6379> lrange number 0 -1
1) "2"
2) "1"
3) "0"
4) "2"

# 从右边开始删除值为2的元素
127.0.0.1:6379> lrange number 0 -1
1) "2"
2) "1"
3) "0"
4) "2"
127.0.0.1:6379> lrem number -1 2
(integer) 1
127.0.0.1:6379> lrange number 0 -1
1) "2"
2) "1"
3) "0"

lrem删除列表中前count个值为value的元素,返回实际删除元素的个数。count值不同,lrem命令执行方式也不同

  • 当count > 0时,lrem命令从列表左边开始删除前count个值为value的元素
  • 当count <0 时,lrem命令从列表右边开始删除前count个值为value的元素
  • 当count=0时,lrem命令会删除所有值为value的元素

lindex通过索引获取列表中的元素

语法:lindex key index

127.0.0.1:6379> lrange number 0 -1
1) "1"
2) "1"
3) "1"
4) "3"
5) "2"
6) "2"
127.0.0.1:6379> lindex number 5
"2"
127.0.0.1:6379> lindex number 3
"3"

lset通过索引设置列表元素的值

# 列表元素中索引为1的值设置为10
127.0.0.1:6379> lset number 1 10
OK

ltrem只保留列表指定片段的元素

语法:ltrim key start stop

127.0.0.1:6379> lrange number 0 -1
1) "1"
2) "1"
3) "1"
4) "3"
5) "2"
6) "2"
127.0.0.1:6379> ltrim number 1 5
OK
127.0.0.1:6379> lrange number 0 -1
1) "1"
2) "1"
3) "3"
4) "2"
5) "2"
  • ltrim可以删除指定元素之外的所有元素

linsert在列表的元素前或后插入元素

语法:linsert key before|after pivot value

127.0.0.1:6379> lrange number 0 -1
1) "1"
2) "1"
3) "3"
4) "2"
5) "2"
127.0.0.1:6379> linsert number before 2 4
(integer) 6
127.0.0.1:6379> lrange number 0 -1
1) "1"
2) "1"
3) "3"
4) "4"
5) "2"
6) "2"
127.0.0.1:6379> linsert number after 2 4
(integer) 7
127.0.0.1:6379>
127.0.0.1:6379> lrange number 0 -1
1) "1"
2) "1"
3) "3"
4) "4"
5) "2"
6) "4"
7) "2"
  • linsert 现在列表中从左到右查找值为pivot的元素,然后根据第二个参数是before|after来决定将value值插入到该元素前还是后

rpoplpush 将元素从一个列表转到另一个列表

语法:rpoplpush source destination timeout

127.0.0.1:6379> rpoplpush nunber number
"a"
127.0.0.1:6379> rpoplpush nunber number
"b"
127.0.0.1:6379> lrange number 0 -1
1) "b"
2) "a"
3) "1"
4) "1"
5) "3"
6) "4"
7) "2"
8) "4"
9) "2"
  • rpoplpush先将source列表类型的右边弹出一个元素;然后将它加入到destination列表类型的左边,并返回弹出的元素,整个过程原子性

rpushx为已存在的列表添加值

语法:rpushx key value

127.0.0.1:6379> rpushx number +
(integer) 10

lpushx将值插入到已存在列表的头部

语法:lpushx key value

127.0.0.1:6379> lpushx n -
(integer) 0
127.0.0.1:6379> lpush nnumber -
(integer) 1
127.0.0.1:6379> lrange nmber 0 -1
(empty array)
127.0.0.1:6379> lrange number 0 -1
 1) "b"
 2) "a"
 3) "1"
 4) "1"
 5) "3"
 6) "4"
 7) "2"
 8) "4"
 9) "2"
10) "+"

blpop移除并获取列表的第一个元素

语法:blpop key1 [key2] timeout

127.0.0.1:6379> blpop number1 300
1) "number1"
2) "3"
  • 移除并获取列表的第一个元素,如果列表没有元素,会阻塞列表知道等待超时或发现可弹出元素为止

brpop移除并获取列表的最后一个元素

语法:brpop key1 [key2] timeout

127.0.0.1:6379> brpop number 200
1) "number"
2) "2"

**brpoplpush从列表弹出一个值,将弹出的值插入到另一个列表 **

语法:brpoplpush source destination timeout

127.0.0.1:6379> brpoplpush number number1 500
"1"
  • 从列表中取出最后一个元素,并插入到另外一个列表的头部;
  • 如果 列表中没有元素会阻塞列表,直到等待超时或发现可弹出元素为止

集合(Set)

集合中每个元素都是不同的,且没有顺序。一个集合类型(Set)键可以存储至多2^32 - 1个字符串。

集合的常用操作时向集合中加入或删除元素,判断箢篼是否存在等,由于集合类型在redis内部是使用值为空的hash表实现的,因此这些操作的时间复杂度都是O(1) 。最为方便的是集合类型之间可以进行交集,并集,差集运算。

例如:存储文章标签

集合类型和列表类型的对比:

集合类型 列表类型
有序性
唯一性

sadd向集合中添加一个或多个元素

语法:sadd key member1 [member2 …]

127.0.0.1:6379> sadd letters a
(integer) 1
# 因为a已经存在,所以不会添加
127.0.0.1:6379> sadd letters a b c
(integer) 2

集合中不能有相同的元素,加入的元素若有重复的,就会忽略这个重复的元素。该命令返回成功加入集合的元素数量

smembers获取集合中所有元素

语法:smembers key

127.0.0.1:6379> smembers letters
1) "c"
2) "b"
3) "a"

srem从集合中删除一个或多个元素

语法: srem key member1 [members …]

127.0.0.1:6379> smembers letters
1) "c"
2) "b"
3) "a"

sismember判断元素是否在集合中

语法:sismember key member

127.0.0.1:6379> smembers letters
1) "c"
2) "b"
3) "a"

sdiff对多个集合执行差集运算

语法:sdiff key1 [key2 key3 …]

127.0.0.1:6379> sadd setA 1 2 3
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB
1) "1"
127.0.0.1:6379> sdiff setB setA
1) "4"
127.0.0.1:6379> sadd setC 2 3
(integer) 2
127.0.0.1:6379> sdiff setA setB setC
1) "1"

setA setB setC 差集运算;先算setA - setB,再计算与setC的差集

sinter对多个集合执行交集运算

语法:sinter key1 [key2 …]

127.0.0.1:6379> sinter setA setB
1) "2"
2) "3"

127.0.0.1:6379> sinter setA setB setC
1) "2"
2) "3"

sunion 对多个集合执行并集运算

语法 :sunion key1 [key2 …]

127.0.0.1:6379> sunion setA setB
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> sunion setA setB setC
1) "1"
2) "2"
3) "3"
4) "4"

sdiffscore返回给定所有集合的差集并存储在destination中

语法:sdiffscore destination key1 [key2 …]

127.0.0.1:6379> sdiffstore new_set setA setB
(integer) 1

127.0.0.1:6379> smembers new_set
1) "1"

将给定集合之间的差集存储在指定的集合中。如果指定的集合key已存在,则会被覆盖。

sinterstore返回所有集合的交集并存储在destination中

语法:sinterstore destination key1 [key2 …]

127.0.0.1:6379> sinterstore new_set1 setA setB
(integer) 2

127.0.0.1:6379> smembers new_set1
1) "2"
2) "3"

suninnstore所有给定集合的并集存储在destination

语法:sunionstore destination key1 [key2 …]

127.0.0.1:6379> sunionstore setUnionJi setA setB
(integer) 4

scard获取集合中成员数量

127.0.0.1:6379> smembers new_set2
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> scard setA
(integer) 3
127.0.0.1:6379> scard new_set2
(integer) 4

smove将member元素从source集合移到destination集合

语法:smove source destination member

127.0.0.1:6379> sadd name lisi wangwu
(integer) 2
127.0.0.1:6379> sadd age 17 18
(integer) 2

127.0.0.1:6379> smove age name 17
(integer) 1
127.0.0.1:6379> smembers name
1) "lisi"
2) "17"
3) "wangwu"

spop移除并返回集合中的一个随机元素

语法:spop key [count]

127.0.0.1:6379> smembers name
1) "wangwu"
2) "17"
3) "lisi"
127.0.0.1:6379>
127.0.0.1:6379> spop name
"lisi"
127.0.0.1:6379> spop name
"17"
127.0.0.1:6379> spop new_set1 2
1) "2"
2) "3"
127.0.0.1:6379> smembers new_set1
(empty array)

srendmember返回集合中一个或多个随机数

语法:srandmember key [count]

127.0.0.1:6379> srandmember setC
"3"
127.0.0.1:6379> srandmember setC
"2"

scan迭代集合中的元素

语法:sscan key cursor [match patrn] [count count]

127.0.0.1:6379> sscan name 0 match w*
1) "0"
2) 1) "wangwu"

有序集合(sorted set)

Redis有序集合和集合一样也是string类型元素的集合,并且不允许重复的成员

不同的是每个元素都会关联一个double类型的分数。redis通过分数来为集合中的成员进行从小到大的排序

有序集合的成员是唯一的,但分数(score)却可以重复。

有序集合使用跳跃表和Hash实现,所以添加删除查找的复杂度都是O(1)。 集合中最大的成员数为 2^32 - 1

使用场景 : 根据用户权重抢购商品 (优先级消息队列,先来先),学生成绩排名…

zadd向集合中添加一个或多个元素和该元素的分数

语法:zadd key score1 member1 [score2 member2 …]

127.0.0.1:6379> zadd board 89 tom 60 peter 100 hipi
(integer) 3

# 修改分数
127.0.0.1:6379> zadd board 87 hipi
(integer) 0

zscore获取元素分数

语法:zscore key member

127.0.0.1:6379> zscore board hipi
"87"

zrange 获取排名在某个范围的元素列表

语法:zrange key start stop [withscores]

127.0.0.1:6379> zrange board 0 -1
1) "peter"
2) "hipi"
3) "tom"
127.0.0.1:6379> zrange board 0 2
1) "peter"
2) "hipi"
3) "tom"

127.0.0.1:6379> zrange board 0 -1 withscores
1) "peter"
2) "60"
3) "hipi"
4) "87"
5) "tom"
6) "89"

zrange按照分数值从大到小排序。下标参数start和stop都是以0为底

zrevrange返回有序集合中指定区间内的成员,通过索引 ,分数由高到低

语法:zrevrange key start top [withscores]

127.0.0.1:6379> zrevrange board 0 -1
1) "tom"
2) "hipi"
3) "peter"

其中成员的位置按照分数值递减来排列

zrevrangebyscore返回有序集合中指定分区区间内的成员,分数从高到低排序

语法 :zrevrangebyscore key max min [withscores]

127.0.0.1:6379> zrevrange board 0 -1
1) "tom"
2) "hipi"
3) "peter"

zincrby 增加某个元素的分数

语法:zincrby key increment member

# 返回值为更改后的分数
127.0.0.1:6379> zincrby board 4 jerry
"60"

zcard获取集合中元素的数量

语法:zcard key

# 返回值为更改后的分数
127.0.0.1:6379> zincrby board 4 jerry
"60"

zcount获取集合中元素的数量

语法:zcount key min max

127.0.0.1:6379> zcount board 90 100
(integer) 1

zrem删除一个或者多个元素

语法:zrem key member [member1 member2 …]

127.0.0.1:6379> zrem board tom
(integer) 1

zremrangebyrank按照排名范围删除元素

127.0.0.1:6379> zadd testrem 1 a 2 b 3 c 4 d 5 f 6 g
(integer) 6
127.0.0.1:6379> zremrangebyrank testrem 0 1
(integer) 2
127.0.0.1:6379> zrange testrem 0 -1
1) "c"
2) "d"
3) "f"
4) "g"

zremrangebyscore按照分数范围删除元素

语法 :zremrangebyscore key min max

127.0.0.1:6379> zremrangebyscore testrem 2 4
(integer) 2
127.0.0.1:6379> zrange testrem 0 -1 withscores
1) "f"
2) "5"
3) "g"
4) "6"

zrank获取元素的排名

语法:zrank key member

127.0.0.1:6379> zrank board hipi
(integer) 5

按照元素分数从小到大的顺序获得指定的元素排名(从0开始)

zinterstore 计算有序集合的交集

语法:zinterstore destination numkeys key1 [key2 …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]

解释:该命令用来计算多个有序集合的交集并将结果存储在destination键中,返回值为destination键中元素个数;

127.0.0.1:6379> zadd sort1 1 a 2 b
(integer) 2
127.0.0.1:6379> zadd sort2 10 a 20 b
(integer) 2
127.0.0.1:6379> zinterstore storeResult 2 sort1 sort2
(integer) 2
127.0.0.1:6379> zrange sorteResult 0 -1 withscores
(empty array)
127.0.0.1:6379> zrange storeResult 0 -1 withscores
1) "a"
2) "11"
3) "b"
4) "22"

zunionstore极端一个或者多个集合的并集

语法:zunionstore destination numbers key1 [key2 …]

127.0.0.1:6379> zunionstore uresult 2 sort1 sort2
(integer) 2
127.0.0.1:6379> zrange uresult 0 -1
1) "a"
2) "b"
127.0.0.1:6379> zrange uresult 0 -1 withscores
1) "a"
2) "11"
3) "b"
4) "22"
127.0.0.1:6379> zrange sort1 0 -1 withscores
1) "a"
2) "1"
3) "b"
4) "2"

HyperLogLog

Redis HyperLogLog 是用来做基数统计的算法。

什么是基数?

比如数据集{1,3,5,10,12},那么这个数据集的基数集为{1,3,5,10,12},基数(不重复元素)为5;基数估计就是在误差可接受的范围内,快速计算基数。

优点:在输入元素的数量或者体积非常大时,计算基数所需空间总是固定的,并且是很小的。

使用场景 :统计每天访问IP数,统计页面实时UV,统计在线用户数,统计用户每天搜索不同词条的个数…

pfadd添加指定元素到HyperLogLog中

语法:pfadd key value

127.0.0.1:6379> PFADD w3c "redis"
(integer) 1
127.0.0.1:6379> PFAdd w3c mongodb
(integer) 1
127.0.0.1:6379> pfcount w3c
(integer) 2

pfcount返回给定的HyperLogLog中的基数估算值

语法:pfcount key

127.0.0.1:6379>  PFAdd php thinkphp
(integer) 1
127.0.0.1:6379> pfadd php laravel
(integer) 1
127.0.0.1:6379> pfadd php yii2
(integer) 1
127.0.0.1:6379> pfadd php symfony
(integer) 1
127.0.0.1:6379> pfcount php
(integer) 4

pfmerge 将多个HyperLogLog合并为一个HyperLogLog

语法:pfmerge key1 kye2

127.0.0.1:6379> pfmerge php w3c
OK
127.0.0.1:6379> pfcount php
(integer) 7
127.0.0.1:6379> pfcount w3c
(integer) 3

事务

Redis中事务(transaction)是一组命令的集合。事务和命令都是Redis的最小执行单位。

事务执行流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iMTlAhhZ-1640782523490)(自如初博客redis学习.assets/redis事务执行过程.png)]

事务经历的三个阶段: 1)开始事务 2)命令入列 3)执行事务

应用场景:乐观锁

multi 开启一个事务

exec 执行一个事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set city shanghai
QUEUED
127.0.0.1:6379(TX)> exec
1) OK

#语法错误: 执行exec将直接返回错误
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set city aa
QUEUED
127.0.0.1:6379(TX)> set cit
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get city
"shanghai"

#运行错误:除了错误的命令,其他命令都会被执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name test
QUEUED
127.0.0.1:6379(TX)> sadd name php
QUEUED
127.0.0.1:6379(TX)> set name mysql
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get name
"mysql"

discard 取消事务

127.0.0.1:6379> get name
"mysql"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name oracle
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> get name
"mysql"

**watch 监听一个或者多个key(决定事务是执行还是回滚) **

#一个命令行窗口
127.0.0.1:6379> get name
"mysql"
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name mysqli
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
127.0.0.1:6379> get name
"mysqli"


#窗口1 
127.0.0.1:6379> get name
"mysqli"
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name oracle
QUEUED
#暂停,重新打开一个窗口,输入窗口2的命令,然后再继续输入以下命令
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> get name
"pdo"

#窗口2
127.0.0.1:6379> set name pdo
OK
  • 可以监听一个或多个键,一旦其中有一个键被修改或者删除,之后的事务 将不执行

unwatch 取消watch命令对所有key的监听

127.0.0.1:6379> unwatch
OK

Redis发布订阅

Redis发布订阅(pub/sub)是一种消息通信模式:发送者发送消息,订阅者接受消息

Redis客户端可以订阅任意数量的频道

subscribe 订阅一个或者多个频道

语法:subscribe channel

#窗口1
127.0.0.1:6379> subscribe redisChat
Reading messages... (press Ctrl-C to quit)
#别关,下面发送指定信息到这个channel

publish将信息发送到指定的频道

语法:publish channel message

#窗口2
127.0.0.1:6379> publish redisChat "redis is a caching technique"
(integer) 1

#窗口1(自动出现)
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
1) "message"
2) "redisChat"
3) "redis is a caching technique"
         
  • 该消息返回接受这条消息的订阅者数量

pubsub查看订阅和发布系统状态

127.0.0.1:6379> pubsub channels
1) "redisChat"

unscbscribe 退订给定一个或者多个频道的信息

127.0.0.1:6379> unsubscribe redisChat
1) "unsubscribe"
2) "redisChat"
3) (integer) 0

psubscribe 订阅一个或多个符合给定模式的频道

127.0.0.1:6379> psubscribe cctv?*
Reading messages… (press Ctrl-C to quit)

  1. “psubscribe”

  2. “cctv?*”

  3. (integer) 1

  4. “pmessage”

  5. “cctv?*”

  6. “cctv1”

  7. “\xe6\x96\xb0\xe9\x97\xbb”

punsubscribe退订给定的模式的频道

127.0.0.1:6379> punsubscribe cctv?*
1) "punsubscribe"
2) "cctv?*"
3) (integer) 0

Redis两种持久化方式

Redis持久化是将存储在内存中的数据永久的保存在磁盘中。

Redis为什么需要持久化?

Redis的强大性能很大程度上是因为将所有的数据都存储在内存当中,当Redis重启后,所有内存中的数据都会丢失。

Redis支持RDB和AOF两种持久化方式:

RDB是 根据指定的规则定时的将内存中的数据存储到硬盘中;

AOF是 在每次执行命令之后将命令本身记录下来

两种持久化方式可以单独使用其中一种,更多的是将两种结合使用。

RDB方式实现持久化

RBD方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的所有数据生成一份副本并存储在硬盘上,这个过程就是快照。

以下几种情况时,redis会对数据进行快照:

  • 根据配置规则进行自动快照
  • 用户执行save或者bgsave命令
  • 执行FLUSHALL命令
  • 执行复制(replication)时

(1)根据配置规则就行自动快照

#900秒后,有一个或者一个以上的键被修改,则 进行快照
save 900  1
#300秒后,至少有10个键被修改,则进行快照
save 300 10
#60秒内,至少有10000键被修改,则进行快照
save 60 10000


  • 如上可以看到:每条快照以save开头,且每条快照条件独占一行。可以同时存在多个条件,条件之间是“或”关系。
根据配置规则进行自动快照

实现:

步骤1 )注释默认的配置文件并设置新的快照

#save 900 1
#save 300 10
#save 60 10000
save 60 3

步骤2) 删除dump.rdb文件

步骤3) 设置值,并等待60s后查看是否有dump文件的生成

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> set k5 v5
OK

步骤4)60秒已经生成dump文件,重启虚拟机,强制退出redis,假设突然断电

步骤5)重启redis,查看数据是否恢复

127.0.0.1:6379> get k1
"v1"

可以看到 数据已经恢复了,这就是自动执行快照。

用户执行save或者bgsave命令

除了自动快照之外,还可以进行手动快照。当服务重启,手动迁移以及备份时,可以使用手动进行快照操作。

save命令

当执行save指令时,redis同步进行快照操作,在快照执行的过程中会阻塞所有来自客户端的请求。当数据过多时,save会导致Redis长时间不响应。

127.0.0.1:6379> set k8 v8
OK
127.0.0.1:6379> save
OK

恢复数据:只需将备份文件(dump.rdb)移动到redis安装目录并启动服务即可。

#获取redis目录命令
127.0.0.1:6379> config get dir
1) "dir"
2) "/www/server/redis"

bgsave命令

手动执行快照,推荐使用bgsave。bgsave命令可以在后台异步进行快照操作,快照的同时服务可以继续响应来自客户端的请求。执行bgsave后,redis会立即返回OK,表示开始执行快照操作。通过lastsave命令查看最近一次成功执行快照的时间。

127.0.0.1:6379> set k9 v9
OK
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> lastsave
(integer) 1640742064

flushall命令

当执行flushall命令时,Redis清除Redis服务器中的所有数据。无论清空服务器的过程中是否触发了自动快照条件,只要自动快照条件不为空,Redis就会执行一次快照操作。

执行复制时

当设置了主从模式,Redis会在复制初始化时进行自动快照。

快照原理Redis默认会将快照文件存储在Redis当前进程的工作目录中的dump.rdb文件中。通过配置dir和dbfilename两个参数分别指定快照文件的存储路径和文件名。

快照执行过程如下:

1)Redis使用fork函数复制一份当前进程(父进程)的副本(子进程)

2)父进程继续接收并处理客户端发来的命令,而子进程开始讲内存中的数据写入硬盘的临时文件

3)当子进程写入万所有数据后会用该临时文件替换旧的RDB文件,至此一次快照操作完成。

方式二:AOF方式实现

当使用Redis存储非临时数据时,一般需要打开AOF持久化来降低进程中职导致的数据丢失。

实现

步骤1):开启AOF

Redis默认不开启AOF(append only file)方式持久化,通过修改配置文件参数启动

appendonly yes

appendfsync always

步骤2):写入命令

127.0.0.1:6379> set name wangwu
OK

步骤3)查看appendonly.aof文件

[root@dcdfa0e9eb71 redis]# ll appendonly.aof
-rwxr-xr-x 1 redis redis 0 Dec 29 10:04 appendonly.aof

这样aof就实现了。

AOF重写

为什么需要重写?有些命令是冗余的,也会被记录。随着命令不断写入,AOF文件越来越大,这时希望对该文件进行优化,针对冗余命令,可以进行删除,保留一条。

手动后台重写bgrewriteaof

#窗口1
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
#窗口2
[root@dcdfa0e9eb71 redis]# ll appendonly.aof
-rwxr-xr-x 1 redis redis 148 Dec 29 10:15 appendonly.aof
#暂停,再窗口1输入berewriteaof的redis命令后
[root@dcdfa0e9eb71 redis]# ll appendonly.aof
-rwxr-xr-x 1 redis redis 112 Dec 29 10:15 appendonly.aof

可以清楚的看到,aof文件变小了

自动重写

#redis.conf默认配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

重写的作用

1)降低磁盘占用量,提高自盘利用率

2)提高持久化效率,降低持久化时间,提高I/O性能

3)降低数据恢复用时,提高数据恢复效率

同步硬盘数据

虽然每次执行命令,aof都会将命令记录在aof文件中,而实际上,由于操作系统缓存机制,数据并没有真正写入磁盘,而是进入了系统的硬盘缓存。默认情况下系统每30秒会执行一次同步操作,这时才是真正写入磁盘。

如果这30秒的过程中国,系统异常退出,则会导致硬盘缓存中的数据丢失,这时aof持久化无法容忍的损失。可以通过appendsync参数设置同步时机:

appendsync always #表示每次执行写入都会执行同步
appendsync no   #表示不主动进行同步操作
appendsync everysec #表示每秒进行同步

Redis中从复制之一主多从

主从复制时指将一条主节点(master)Redis服务器的数据复制到其他的从节点(slave)Redus服务器。朱勇之间的复制时单向的,只能是由主节点到从节点。

一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库

作用:数据冗余,故障恢复,负载均衡,读写分离,高可用基石(实现哨兵和集群的基础)

Redis可以通过slaveof与配置文件两种方式实现主从复制,推荐使用配置文件方式。

方式一 通过saveof命令行方式实现主从复制

实现一主两从

1)直接启动redis服务;不带任何参数,默认监听6379端口,将此作为主服务

#启动主服务
[root@dcdfa0e9eb71 bin]# /www/server/redis/src/redis-server
57811:C 29 Dec 2021 10:34:10.057 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
clock: POSIX clock_gettime

2)客户端连接6379端口的Redis主服务器

#查看配置信息
127.0.0.1:6379> info replication
# Replication
role:master #master主数据库
connected_slaves:0
master_failover_state:no-failover
master_replid:26d969624f32fcdb591ca1b65639b53d7df769b6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379> set name master
OK

3)使用6380端口开启Redis从服务器

需要重新打开窗口进行服务器配置

[root@dcdfa0e9eb71 /]# /www/server/redis/src/redis-server --port 6380 --slaveof 127.0.0.1 6379

连接成功后,主Redis服务器数据库的任何数据变化通过自动同步到从Redis库(6380)中。

4)客户端连接6380 Redis服务器

需要重新打开窗口进行连接

[root@dcdfa0e9eb71 /]# redis-cli -p 6380

查看主从信息

127.0.0.1:6380> info replication
# Replication
role:slave #6380为从库
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:70
slave_repl_offset:70
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:83f3e0a67b4f509ada3e0a0cb0ec3a12209142c7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:70
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:70

5)使用6381端口启动服务

 6379@dcdfa0e9eb71 /]# /www/server/redis/src/redis-server --port 6381 --slaveof 127.0.0.1

查看配置信息

[root@dcdfa0e9eb71 /]# redis-cli -p 6381
127.0.0.1:6381> INFO replication
# Replication
role:slave #6381为从库
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_read_repl_offset:238
slave_repl_offset:238
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:83f3e0a67b4f509ada3e0a0cb0ec3a12209142c7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:238
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:238

7)实测

#主库 6379
127.0.0.1:6379> set name master
OK
#从库 6380
127.0.0.1:6380> get name
(nil)
127.0.0.1:6380> get name
"master"
#从库 6381
127.0.0.1:6381> get name
(nil)
127.0.0.1:6381> get name
"master"

方式二 通过conf配置文件实现主从复制

实现一主两从

1)创建6380和6381配置文件,由于这两个配置文件只需要修改一下端口,因此只列出一个配置文件来

cp redis.conf redis6380.conf
vim /www/server/redis /redis_6380.conf


#############################
### 以下配置是6380的配置
#############################
# 端口
port 6380

# 后台启动
daemonize yes

pidfile /www/server/redis /redis_6380.pid

loglevel notice

# 日志文件
logfile "/www/server/redis /redis_6380.log"

# 快照文件
dbfilename 6380_dump.rdb

dir /www/server/redis /6380

replicaof 127.0.1 6379
replica-read-only yes       

2)启动redis主服务

[root@dcdfa0e9eb71 bin]# /www/server/redis/src/redis-server

3)客户端登陆主服务器并查看主从信息

[root@dcdfa0e9eb71 /]# redis-cli -p 6380                                                 127.0.0.1:6380> info replication                                                         # Replication                                                                             role:slave                                                                               master_host:127.0.0.1                                                                     master_port:6379                                                                         master_link_status:up                                                                      master_last_io_seconds_ago:4                                                             master_sync_in_progress:0                                                                 slave_read_repl_offset:70                                                                 slave_repl_offset:70                                                                     slave_priority:100 
...

4)启动6380端口的Redis服务器

  • 创建6380 ,6381目录。不然会启动失败
[root@dcdfa0e9eb71 redis]# mkdir 6380                                                     [root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server  /www/server/redis/redis6380.conf  

6379客户端查看链接信息

[root@dcdfa0e9eb71 /]# redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:2350fbf04aebef085c7def0684a7b8f052f13283
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=14,lag=1
master_failover_state:no-failover
master_replid:66026b04b66fe9cadf2c0e995afd85b09d5f0d88
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14

可以看到有一个已经连接了

7)测试

#6379 主
127.0.0.1:6379> set age 18
OK
#6380 从
127.0.0.1:6380> get age
"18"

主从复制原理

1)当启动一个从数据库后,从数据库会向主数据库发送SYNC命令

2)主数据库接收到SYNC命令后开始在后台保存快照(RDB持久化过程),并将保存快照期间接收的命令缓存起来。快照完成后,Redis会将快照文件和所有缓存的命令发送给从数据库

3)从数据库收到后,会载入快照文件并执行到缓存的命令

以上过程是❶复制的初始化。❷ 复制初始化结束后,主数据库每当接收到写命令时就会将命令同步给从数据库,从而保证主从数据库一致。

读写分离一致性

通过复制可以实现读写分离,可以提高服务器的负载能力。读频率大于写频率的应用场景中,当单机Redis无法应付大量的读请求时,可以通过复制功能建立多个从数据库节点,主数据库只进行写操作,而从数据库负责读操作。

从数据库持久化

持久化是比较耗时的操作,为了提高性能,可以通过复制功能建立一个或者多个 数据库,并在从数据库中开启持久化。同时主数据库中禁用持久化。当从数据库崩溃重启后主数据库会自动将数据同步过来,因此不用担心数据丢失

如果主数据库崩溃时,情况就变得复杂很多。严格按照以下步骤

1)在从数据库中使用replicaof no one 命令将从数据库提升成主数据库继续服务

2)启动之崩溃的主数据库,然后使用replicaof命令将其设置成新的主数据库的从数据库,即可将数据同步回来。

无硬盘复制

主从复制时基于RDB方式的持久化实现的,主数据库端在后台保存RDB快照,从数据库端则接受并载入快照文件。

增量复制

当主数据库连接断开后,从数据库会发送SYNC命令重新进行一次完整复制操作。这样即使断开期间数据库变化很小,也需要将数据库中的所有数据重新快照并传送一次。

增量复制基于以下三点实现:

1)从数据库会存储主数据库运行ID(run id)。每个Redis运行实例都会拥有一个唯一的运行ID,每当实例重启就会自动生成一个新的运行ID

2)在复制同步阶段,主数据库每将一个命令传送给从数据库时,都会同时把该命令存放到一个积压队列(backlog)中,并记录下当前积压队列中存放的命令的偏移量范围

3)从数据库接收到主数据库传来的命令时,会记录下该命令的偏移量

这三点是实现增量复制的基础。当主从连接准备就绪后,从数据库会发送一条PSYNC命令来告诉主数据库可以把所有的数据同步过来 。

格式为PSYNC 主数据库的运行ID 断开前最新的命令偏移量

步骤:

①首先,主数据库会判断从数据库传送来的运行ID是否和自己运行的ID相同。此步骤是为了确保从数据库之前确实是和自己同步的,以免从数据库拿到错误的数据

②其次判断从数据库最后同步成功的命令偏移量是否在积压队列中,如果在则可以执行增量复制,并将积压队列中相应的命令发送给从数据库

积压队列本质上是一个固定长度的循环队列默认情况下积压队列的大小为1MB,可以通过配置文件repl-backlog-size选项来调整。积压队列越大,允许的主从数据库断线的时间就越长。

Redis哨兵模式配置的实现

Redis哨兵模式是基于主从复制的。

当数据库宕机之后,哨兵会推选出一个新的主服务器,并把之前的主服务器变成新的从服务器。这一过程是哨兵自动完成,不需要人工参与。

什么是哨兵

哨兵是一个独立的进程。其原理是哨兵通过发送命令,等待redis服务器响应,从而监控运行的多个redis实例

哨兵的作用

  1. 通过发送命令,让服务器返回其运行状态,包括主服务器和从服务器
  2. 当检测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。

*可以使用一个哨兵,也可以使用多个哨兵进行监控,实际使用环境中使用多个哨兵(建议奇数)进行监控

实现步骤

1)配置并开启主从复制(上面已经实现,直接使用)

[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server  /www/server/redis/redis.conf
[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server  /www/server/redis/redis6380.conf
[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server  /www/server/redis/redis6381.conf
[root@dcdfa0e9eb71 redis]# redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=70,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=70,lag=1
master_failover_state:no-failover
master_replid:517ffcafa445b9cc6acc2b4a7ee5700773746bbf
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:70
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:70

2)使用配置文件配置哨兵

①准备工作

# 切换到redis目录
[root@192 redis]# cd /www/server/redis/

# 创建配置文件哨兵目录
[root@192 redis]# mkdir /www/server/redis/redis_sentinel_conf
# 复制默认的哨兵文件到目录
[root@192 redis]# cp ./sentinel.conf /www/server/redis//redis_sentinel_conf/sentinel_6379.conf
[root@192 redis]# cd /usr/local/bin/redis_sentinel_conf/

② 编辑哨兵配置文件

[root@192 redis]# cd /usr/local/bin/redis_sentinel_conf/
[root@192 redis_sentinel_conf]# vim sentinel_6379.conf

修改内容为:

# 端口
port 26379

# 是否守护进程启动
daemonize no

# 工作目录
dir /tmp

# 声明该哨兵的主库是mymaster,主库的ip和端口分别是127.0.0.1 和 6379
# 最后一个2的含义是:当哨兵发生领导选举时,需要2个哨兵通过才行
sentinel monitor mymaster 127.0.0.1 6379 2

# 在mymaster宕机30秒后进行主观下线
sentinel down-after-milliseconds mymaster 30000

# 指定在发生failover故障转移时最多可以有1个slave同时对新的master进行同步
sentinel parallel-syncs mymaster 1

# 设置故障转移超时时间为180秒
sentinel failover-timeout mymaster 180000

③复制sentinel_6379.conf为sentinel_6380.conf,sentinel_6381.conf并修改对应的端口为26380,26381

[root@192 redis_sentinel_conf]# cp sentinel_6379.conf ./sentinel_6380.conf  
[root@192 redis_sentinel_conf]# cp sentinel_6379.conf ./sentinel_6381.conf

3)启动配置好的三个哨兵服务

①启动26379服务并连接

[root@dcdfa0e9eb71 /]# /www/server/redis/src/redis-sentinel /www/server/redis/redis_sentinel_conf/sentinel_6379.conf

客户端使用26379端口连接哨兵服务器

[root@dcdfa0e9eb71 redis]# redis-cli -p 26379
可以使用info查看哨兵显示的信息 127.0.0.1:26379> info # Server redis_version:6.2.6 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:36599a7c3e389a76 redis_mode:sentinel os:Linux 5.10.47-linuxkit x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:c11-builtin gcc_version:8.4.1 process_id:73653 process_supervised:no run_id:82fa9039e9928d9809429365592ef2f732e2a94d tcp_port:26379 ...... # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:379,slaves=2,sentinels=2

可以看到配置文件自动发生改变

②启动26380哨兵服务

[root@dcdfa0e9eb71 redis_sentinel_conf]# /www/server/redis/src/redis-sentinel /www/server/redis/redis_sentinel_conf/sentinel_6380.conf

查看26379服务,已近多了几个配置

73653:X 29 Dec 2021 14:29:03.124 # Sentinel ID is a4fe46846ee24ff8a183c949ff813aa620d8ef90
73653:X 29 Dec 2021 14:29:03.125 # +monitor master mymaster 127.0.0.1 6379 quorum 2
73653:X 29 Dec 2021 14:29:03.126 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
73653:X 29 Dec 2021 14:29:03.180 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
73653:X 29 Dec 2021 14:41:22.742 * +sentinel sentinel 0b968d92eeb26074330ab631221c1ace39be42fe 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
73653:X 29 Dec 2021 14:43:29.244 * +sentinel sentinel bd67baf85fcc6a7664336bb06e725b9321082bac 127.0.0.1 26381 @ mymaster 127.0.0.1 6379

③启动26381哨兵服务

[root@dcdfa0e9eb71 /]#  /www/server/redis/src/redis-sentinel /www/server/redis/redis_sentinel_conf/sentinel_6381.conf
74632:X 29 Dec 2021 14:43:27.178 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
74632:X 29 Dec 2021 14:43:27.179 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=74632, just started
74632:X 29 Dec 2021 14:43:27.179 # Configuration loaded
74632:X 29 Dec 2021 14:43:27.180 * monotonic clock: POSIX clock_gettime
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 6.2.6 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26381
 |    `-._   `._    /     _.-'    |     PID: 74632
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           https://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

74632:X 29 Dec 2021 14:43:27.206 # Sentinel ID is bd67baf85fcc6a7664336bb06e725b9321082bac
74632:X 29 Dec 2021 14:43:27.206 # +monitor master mymaster 127.0.0.1 6379 quorum 2
74632:X 29 Dec 2021 14:43:27.208 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
74632:X 29 Dec 2021 14:43:27.228 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
74632:X 29 Dec 2021 14:43:28.993 * +sentinel sentinel a4fe46846ee24ff8a183c949ff813aa620d8ef90 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
74632:X 29 Dec 2021 14:43:29.032 * +sentinel sentinel 0b968d92eeb26074330ab631221c1ace39be42fe 127.0.0.1 26380 @ mymaster 127.0.0.1 6379

4)实战测试

① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨

①6379主服务器中写入一条数据

127.0.0.1:6379> set ziruchu hello
OK

②6380、6381从服务器中读取

127.0.0.1:6380> get ziruchu
"hello"
127.0.0.1:6381> get ziruchu
"hello"

③ 宕机6379主服务器

由于6379主服务并没有使用守护进程启动,因此直接使用Ctrl+c断开服务或者使用ps -aux查看6379服务的进程号然后kill -9 进程号 模拟宕机

④查看随意一个哨兵

[root@dcdfa0e9eb71 /]# /www/server/redis/src/redis-sentinel /www/server/redis/redis_sentinel_conf/sentinel_6381.conf 74632:X 29 Dec 2021 14:43:27.178 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 74632:X 29 Dec 2021 14:43:27.179 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=74632, just started 74632:X 29 Dec 2021 14:43:27.179 # Configuration loaded 74632:X 29 Dec 2021 14:43:27.180 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26381 | `-._ `._ / _.-' | PID: 74632 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'74632:X 29 Dec 2021 14:43:27.206 # Sentinel ID is bd67baf85fcc6a7664336bb06e725b9321082bac 74632:X 29 Dec 2021 14:43:27.206 # +monitor master mymaster 127.0.0.1 6379 quorum 2 74632:X 29 Dec 2021 14:43:27.208 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 14:43:27.228 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 14:43:28.993 * +sentinel sentinel a4fe46846ee24ff8a183c949ff813aa620d8ef90 127.0.0.1 26379 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 14:43:29.032 * +sentinel sentinel 0b968d92eeb26074330ab631221c1ace39be42fe 127.0.0.1 26380 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.518 # +sdown master mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.581 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2 74632:X 29 Dec 2021 15:18:10.581 # +new-epoch 1 74632:X 29 Dec 2021 15:18:10.581 # +try-failover master mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.613 # +vote-for-leader bd67baf85fcc6a7664336bb06e725b9321082bac 1 74632:X 29 Dec 2021 15:18:10.613 # a4fe46846ee24ff8a183c949ff813aa620d8ef90 voted for a4fe46846ee24ff8a183c949ff813aa620d8ef90 1 74632:X 29 Dec 2021 15:18:10.650 # 0b968d92eeb26074330ab631221c1ace39be42fe voted for a4fe46846ee24ff8a183c949ff813aa620d8ef90 1 74632:X 29 Dec 2021 15:18:10.859 # +config-update-from sentinel a4fe46846ee24ff8a183c949ff813aa620d8ef90 127.0.0.1 26379 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.859 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381 74632:X 29 Dec 2021 15:18:10.859 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381 74632:X 29 Dec 2021 15:18:10.859 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 74632:X 29 Dec 2021 15:18:40.861 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 74632:X 29 Dec 2021 15:21:53.454 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381

可以看到6381被选为主服务器

⑤6381服务器中设置一个值,然后从服务器中查看

#主服务器6381
[root@dcdfa0e9eb71 /]# redis-cli -p 6381
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=493296,lag=0
master_failover_state:no-failover
master_replid:4648e7de667cfee4e37c14b9a08c605f05e56681
master_replid2:517ffcafa445b9cc6acc2b4a7ee5700773746bbf
master_repl_offset:493296
second_repl_offset:470776
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:43
repl_backlog_histlen:493254
127.0.0.1:6381> set name 6381
OK

#从服务器6380
[root@dcdfa0e9eb71 redis]# redis-cli -p 6380
127.0.0.1:6380> get name
"a"
127.0.0.1:6380> get name
"b"
127.0.0.1:6380> get name
Error: Server closed the connection
127.0.0.1:6380> get name
"6381"
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_read_repl_offset:499356
slave_repl_offset:499356
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:4648e7de667cfee4e37c14b9a08c605f05e56681
master_replid2:517ffcafa445b9cc6acc2b4a7ee5700773746bbf
master_repl_offset:499356
second_repl_offset:470776
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:499356

⑥重新启动6379服务并用客户端连接

[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server 77182:C 29 Dec 2021 15:21:53.079 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 77182:C 29 Dec 2021 15:21:53.079 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=77182, just started 77182:C 29 Dec 2021 15:21:53.079 # Warning: no config file specified, using the default config. In order to specify a config file use /www/server/redis/src/redis-server /path/to/redis.conf 77182:M 29 Dec 2021 15:21:53.081 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 77182 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 77182:M 29 Dec 2021 15:21:53.082 # Server initialized 77182:M 29 Dec 2021 15:21:53.092 * Loading RDB produced by version 6.2.6 77182:M 29 Dec 2021 15:21:53.092 * RDB age 3404 seconds 77182:M 29 Dec 2021 15:21:53.092 * RDB memory usage when created 1.85 Mb 77182:M 29 Dec 2021 15:21:53.092 # Done loading RDB, keys loaded: 2, keys expired: 0. 77182:M 29 Dec 2021 15:21:53.092 * DB loaded from disk: 0.007 seconds 77182:M 29 Dec 2021 15:21:53.092 * Ready to accept connections 77182:S 29 Dec 2021 15:22:03.410 * Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer. 77182:S 29 Dec 2021 15:22:03.410 * Connecting to MASTER 127.0.0.1:6381 77182:S 29 Dec 2021 15:22:03.410 * MASTER <-> REPLICA sync started 77182:S 29 Dec 2021 15:22:03.411 * REPLICAOF 127.0.0.1:6381 enabled (user request from 'id=5 addr=127.0.0.1:34810 laddr=127.0.0.1:6379 fd=10 name=sentinel-0b968d92-cmd age=10 idle=0 flags=x db=0 sub=0 psub=0 multi=4 qbuf=196 qbuf-free=40758 argv-mem=4 obl=45 oll=0 omem=0 tot-mem=61468 events=r cmd=exec user=default redir=-1') 77182:S 29 Dec 2021 15:22:03.411 * Non blocking connect for SYNC fired the event. 77182:S 29 Dec 2021 15:22:03.412 * Master replied to PING, replication can continue... 77182:S 29 Dec 2021 15:22:03.412 * Trying a partial resynchronization (request e662599320845936adbf62088ab352d6137febea:1). 77182:S 29 Dec 2021 15:22:03.490 * Full resync from master: 4648e7de667cfee4e37c14b9a08c605f05e56681:516772 77182:S 29 Dec 2021 15:22:03.490 * Discarding previously cached master state. 77182:S 29 Dec 2021 15:22:03.604 * MASTER <-> REPLICA sync: receiving 199 bytes from master to disk 77182:S 29 Dec 2021 15:22:03.611 * MASTER <-> REPLICA sync: Flushing old data 77182:S 29 Dec 2021 15:22:03.611 * MASTER <-> REPLICA sync: Loading DB in memory 77182:S 29 Dec 2021 15:22:03.678 * Loading RDB produced by version 6.2.6 77182:S 29 Dec 2021 15:22:03.678 * RDB age 0 seconds 77182:S 29 Dec 2021 15:22:03.678 * RDB memory usage when created 2.03 Mb 77182:S 29 Dec 2021 15:22:03.678 # Done loading RDB, keys loaded: 2, keys expired: 0. 77182:S 29 Dec 2021 15:22:03.678 * MASTER <-> REPLICA sync: Finished with success

⑦6379客户端查看

[root@dcdfa0e9eb71 /]# redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:525629
slave_repl_offset:525629
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:4648e7de667cfee4e37c14b9a08c605f05e56681
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:525629
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:516773
repl_backlog_histlen:8857
127.0.0.1:6379> get name
"6381"

到这里,哨兵配置完成。

下面为补充信息-哨兵启动信息

6381哨兵显示信息 [root@dcdfa0e9eb71 /]# /www/server/redis/src/redis-sentinel /www/server/redis/redis_sentinel_conf/sentinel_6381.conf 74632:X 29 Dec 2021 14:43:27.178 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 74632:X 29 Dec 2021 14:43:27.179 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=74632, just started 74632:X 29 Dec 2021 14:43:27.179 # Configuration loaded 74632:X 29 Dec 2021 14:43:27.180 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26381 | `-._ `._ / _.-' | PID: 74632 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 74632:X 29 Dec 2021 14:43:27.206 # Sentinel ID is bd67baf85fcc6a7664336bb06e725b9321082bac 74632:X 29 Dec 2021 14:43:27.206 # +monitor master mymaster 127.0.0.1 6379 quorum 2 74632:X 29 Dec 2021 14:43:27.208 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 14:43:27.228 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 14:43:28.993 * +sentinel sentinel a4fe46846ee24ff8a183c949ff813aa620d8ef90 127.0.0.1 26379 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 14:43:29.032 * +sentinel sentinel 0b968d92eeb26074330ab631221c1ace39be42fe 127.0.0.1 26380 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.518 # +sdown master mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.581 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2 74632:X 29 Dec 2021 15:18:10.581 # +new-epoch 1 74632:X 29 Dec 2021 15:18:10.581 # +try-failover master mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.613 # +vote-for-leader bd67baf85fcc6a7664336bb06e725b9321082bac 1 74632:X 29 Dec 2021 15:18:10.613 # a4fe46846ee24ff8a183c949ff813aa620d8ef90 voted for a4fe46846ee24ff8a183c949ff813aa620d8ef90 1 74632:X 29 Dec 2021 15:18:10.650 # 0b968d92eeb26074330ab631221c1ace39be42fe voted for a4fe46846ee24ff8a183c949ff813aa620d8ef90 1 74632:X 29 Dec 2021 15:18:10.859 # +config-update-from sentinel a4fe46846ee24ff8a183c949ff813aa620d8ef90 127.0.0.1 26379 @ mymaster 127.0.0.1 6379 74632:X 29 Dec 2021 15:18:10.859 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381 74632:X 29 Dec 2021 15:18:10.859 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381 74632:X 29 Dec 2021 15:18:10.859 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 74632:X 29 Dec 2021 15:18:40.861 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 74632:X 29 Dec 2021 15:21:53.454 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 6380哨兵显示信息[root@dcdfa0e9eb71 redis_sentinel_conf]# /www/server/redis/src/redis-sentinel /www/server/redis/redis_sentinel_conf/sentinel_6380.conf 74461:X 29 Dec 2021 14:41:20.739 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 74461:X 29 Dec 2021 14:41:20.739 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=74461, just started 74461:X 29 Dec 2021 14:41:20.739 # Configuration loaded 74461:X 29 Dec 2021 14:41:20.742 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26380 | `-._ `._ / _.-' | PID: 74461 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 74461:X 29 Dec 2021 14:41:20.762 # Sentinel ID is 0b968d92eeb26074330ab631221c1ace39be42fe 74461:X 29 Dec 2021 14:41:20.762 # +monitor master mymaster 127.0.0.1 6379 quorum 2 74461:X 29 Dec 2021 14:41:20.766 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 74461:X 29 Dec 2021 14:41:20.785 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 74461:X 29 Dec 2021 14:41:22.600 * +sentinel sentinel a4fe46846ee24ff8a183c949ff813aa620d8ef90 127.0.0.1 26379 @ mymaster 127.0.0.1 6379 74461:X 29 Dec 2021 14:43:29.244 * +sentinel sentinel bd67baf85fcc6a7664336bb06e725b9321082bac 127.0.0.1 26381 @ mymaster 127.0.0.1 6379 74461:X 29 Dec 2021 15:18:10.496 # +sdown master mymaster 127.0.0.1 6379 74461:X 29 Dec 2021 15:18:10.627 # +new-epoch 1 74461:X 29 Dec 2021 15:18:10.649 # +vote-for-leader a4fe46846ee24ff8a183c949ff813aa620d8ef90 1 74461:X 29 Dec 2021 15:18:10.858 # +config-update-from sentinel a4fe46846ee24ff8a183c949ff813aa620d8ef90 127.0.0.1 26379 @ mymaster 127.0.0.1 6379 74461:X 29 Dec 2021 15:18:10.859 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381 74461:X 29 Dec 2021 15:18:10.859 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381 74461:X 29 Dec 2021 15:18:10.859 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 74461:X 29 Dec 2021 15:18:40.889 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 74461:X 29 Dec 2021 15:21:53.460 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 74461:X 29 Dec 2021 15:22:03.410 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 6379哨兵显示信息 [root@dcdfa0e9eb71 /]# /www/server/redis/src/redis-sentinel /www/server/redis/redis_sentinel_conf/sentinel_6379.conf 73653:X 29 Dec 2021 14:29:03.047 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 73653:X 29 Dec 2021 14:29:03.052 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=73653, just started 73653:X 29 Dec 2021 14:29:03.055 # Configuration loaded 73653:X 29 Dec 2021 14:29:03.056 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 73653 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 73653:X 29 Dec 2021 14:29:03.124 # Sentinel ID is a4fe46846ee24ff8a183c949ff813aa620d8ef90 73653:X 29 Dec 2021 14:29:03.125 # +monitor master mymaster 127.0.0.1 6379 quorum 2 73653:X 29 Dec 2021 14:29:03.126 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 14:29:03.180 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 14:41:22.742 * +sentinel sentinel 0b968d92eeb26074330ab631221c1ace39be42fe 127.0.0.1 26380 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 14:43:29.244 * +sentinel sentinel bd67baf85fcc6a7664336bb06e725b9321082bac 127.0.0.1 26381 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.514 # +sdown master mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.573 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2 73653:X 29 Dec 2021 15:18:10.573 # +new-epoch 1 73653:X 29 Dec 2021 15:18:10.573 # +try-failover master mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.601 # +vote-for-leader a4fe46846ee24ff8a183c949ff813aa620d8ef90 1 73653:X 29 Dec 2021 15:18:10.613 # bd67baf85fcc6a7664336bb06e725b9321082bac voted for bd67baf85fcc6a7664336bb06e725b9321082bac 1 73653:X 29 Dec 2021 15:18:10.650 # 0b968d92eeb26074330ab631221c1ace39be42fe voted for a4fe46846ee24ff8a183c949ff813aa620d8ef90 1 73653:X 29 Dec 2021 15:18:10.657 # +elected-leader master mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.657 # +failover-state-select-slave master mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.715 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.715 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.792 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.847 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.847 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:10.855 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:11.805 # -odown master mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:11.806 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:11.806 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:11.882 # +failover-end master mymaster 127.0.0.1 6379 73653:X 29 Dec 2021 15:18:11.882 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381 73653:X 29 Dec 2021 15:18:11.882 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381 73653:X 29 Dec 2021 15:18:11.882 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 73653:X 29 Dec 2021 15:18:41.891 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381

启动信息解释

+slave 表示发现了新的从数据库;

+sdown 表示哨兵主观认为主数据库停止了服务

_odown 表示哨兵客观认为主数据库停止了服务

+try-failover 表示哨兵开始进行故障恢复

+failover-end 表示哨兵完成故障恢复,期间涉及的内容比较复杂,包括领头哨兵的选举、备选数据库的选择等

+switch-master 表示主数据库人6379端口切换到6381端口,即6381升级为主数据库

举个栗子:当6380宕机后,推选出6381作为主数据库,此时6379还是宕机状态,当6379恢复之后,会转变为6381端口实例的从数据库来运行,因此,哨兵将6379的实例信息修改为了6381的从数据库。

哨兵实现原理

一个哨兵进程启动之后会读取配置文件的内容,通过# sentinel monitor <master-name> <ip> <redis-port> <quorum>找出需要监控的主数据库

例如:sentinel_6379.conf中 sentinel monitor mymaster 127.0.0.1 6381 2

一个redis哨兵节点可以同时监控多个Redis主从系统,其配置如下:

一个哨兵节点可以同时监控多个Redis主从系统,其配置如下:

#暂未配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel monitor othermaster 127.0.0.1 6380 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5JoBy6iA-1640782523496)(自如初博客redis学习.assets/image-20211229162243900.png)]

多个哨兵同时监控一个Redis主从系统。上一篇文章记录的就是多个哨兵监控一个主从。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ot8vWe60-1640782523498)(自如初博客redis学习.assets/image-20211229143529043.png)]

配置含义解释

sentinel monitor mymaster 127.0.0.1 6379 2

mymaster 表示要监控的主数据库名字。可以自定义,名字必须由小写字母、数字和’.-_'组成

127.0.0.1 表示主数据库地址

6379 表示主数据库端口号

2 表示最低通过票数。如当为3哨兵时,有2个认为主数据库挂了,那就是挂了


#sentinel.conf其他配置含义
`sentinel down-after-milliseconds mymaster 60000`	每隔一秒发送一次`ping`命令

`sentinel down-after-milliseconds othermaster 10000`	每陋600毫秒发送一次`ping`命令

哨兵原理可以理解为3个步骤:监控,通知和故障转移

  1. 监控

    哨兵启动后,会与监控的主数据库 建立两条连接,其中一条用来订阅主数据的_sentinel_:hello频道以获取其他同样监控该数据库的哨兵节点信息;

    哨兵也需要定期向主数据库发送INFO等命令来获取主数据库本身的信息。

    和主数据库建立连接完成之后, 哨兵会 使用另外一条连接来发送命令:

    • 每10s哨兵会向主数据库和从数据库发送info命令
    • 每2s哨兵会向主数据库和从数据库发送sentinel:hello频道发送自己的信息
    • 每1s哨兵会向主数据库,从数据库和其他哨兵节点发送ping命令

详细理解上面3步:

首先,发送info命令使得哨兵可以获得当前数据库相关信息(运行ID,复制等)从而实现新节点的自动发现。哨兵借助info命令来获取所有复制该主数据库的从库信息;

其次,启动后,哨兵会向主数据库发送info命令,通过解析返回结果来得知从数据库列表,然后对每个从数据库建立两条连接;

第三,哨兵会每10s定向的向已知的主从数据库发送info命令来获取信息更新并进行相关操作

第四,哨兵会向主从数据库的_sentinel_:hello频道发送信息,与同样监控该数据的哨兵分享自己的信息;发送消息的内容为哨兵地址+哨兵端口+哨兵的运行ID+哨兵的 配置版本+主数据库名字+主数据库地址+主数据库端口+主数据库配置版本

通知

配置完成就会进行监控,并发送ping 命令。

当超过down-after-milliseconds指定的时间后,如果被ping的数据库或者节点没有进行回复,哨兵认为主观下线

主管下线表示从当前的哨兵进程来看,该节点已经下线。如果该节点为主数据库,则哨兵进行下一步判断是否进行故障恢复:哨兵发送sentinel is-master-down-by-add命令通知其他哨兵节点,说,我发现这个地址的主数据库下线了,你们来看看是不是下线了,当达到指定数量的哨兵认为主数据库下线了,那么哨兵认为是客观下线

故障转移

哨兵认为主数据库下线之后,会进行内部投票,选出一个领头羊,故障恢复就由这个领头羊来操作,领头羊的选举有如下过程:

1.发现主数据库客观下线的哨兵节点(A节点)向每个哨兵节点发送命令,要求对方选自己成为新的 领头哨兵

2.如果目标哨兵节点没有选择其他的哨兵节点,则同意A节点为领头羊哨兵

3.如果A节点发现有超半数且超过quorum参数值的哨兵节点同意自己成为领头羊节点,那么A成功成为领头羊哨兵

4.当多个节点同时参选,则会发现没有任何哨兵节点可选。此时每个参选节点将等待一个随机时间重新发起参选请求,进行下一轮选举,直到成功为止

5.选出领头羊哨兵节点之后,领头羊开始对主数据库进行故障恢复,内容如下:

  • 5.1 所有在线的从数据库中,选择优先级最高的数据库。优先级通过slave-priority来设置;
  • 5.2 如果有多个优先级最高的数据库,则复制命令偏移量越大的越有先;
  • 5.3 如果条件都一样,则选择运行ID较小的从数据库

选出一个从数据库之后,领头羊哨兵向从数据库发送replicaof no one命令使其升级为主数据库。而领头羊则向其他从数据库发送repliaof命令使其成为新主数据库的从数据库。

最后进行更新内部记录,将已经停止的旧主数据库更新成为新的主数据库的从数据库身份继续服务。

redis集群

实现

1)**创建6个实例配置文件;**此处以9000.conf为例,其余5个配置文件复制9000.conf并修改端口

[root@dcdfa0e9eb71 redis]# pwd
/www/server/redis
[root@dcdfa0e9eb71 redis]# mkdir clusterConf
[root@dcdfa0e9eb71 redis]# cp /www/server/redis/redis.conf clusterConf/9000.conf
[root@dcdfa0e9eb71 clusterConf]# cp 9000.conf 9001.conf                                   [root@dcdfa0e9eb71 clusterConf]# cp 9000.conf 9002.conf                                   [root@dcdfa0e9eb71 clusterConf]# cp 9000.conf 9003.conf  
[root@dcdfa0e9eb71 clusterConf]# cp 9000.conf 9004.conf                                   [root@dcdfa0e9eb71 clusterConf]# cp 9000.conf 9005.conf                                   

小技巧:vi替换 文本 :【;%s/源字符串/目标字符串】 例如本例 :%s/9000/9005/

  • 由于是本地单台系统操作,因此省略了绑定地址操作,真是服务器系统中带上ip地址。

  • 由于是本地环境,为了学习记录的需要,没有开启后台守护启动,真实服务器环境中,开启守护进程启动。

2)启动配置好的6台服务器

[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server ./clusterConf/9000.conf &
[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server ./clusterConf/9001.conf &
[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server ./clusterConf/9002.conf &
[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server ./clusterConf/9003.conf &
[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server ./clusterConf/9004.conf &
[root@dcdfa0e9eb71 redis]# /www/server/redis/src/redis-server ./clusterConf/9005.conf &

启动完成之后查看redis进程

[root@dcdfa0e9eb71 clusterConf]# ps -aux | grep redis
root       85259  0.1  0.1  64540  5600 pts/11   Sl+  17:21   0:00 /www/server/redis/src/redis-server 127.0.0.1:9000 [cluster]
root       85305  0.1  0.1  64540  5608 pts/7    Sl+  17:22   0:00 /www/server/redis/src/redis-server 127.0.0.1:9001 [cluster]
root       85414  0.1  0.1  64540  5552 pts/2    Sl   17:23   0:00 /www/server/redis/src/redis-server 127.0.0.1:9002 [cluster]
root       85429  0.2  0.1  64540  5492 pts/2    Sl   17:23   0:00 /www/server/redis/src/redis-server 127.0.0.1:9003 [cluster]
root       85436  0.2  0.1  64540  5644 pts/2    Sl   17:23   0:00 /www/server/redis/src/redis-server 127.0.0.1:9004 [cluster]
root       85445  0.5  0.1  64540  5536 pts/2    Sl   17:24   0:00 /www/server/redis/src/redis-server 127.0.0.1:9005 [cluster]
root       85472  0.0  0.0  12136  1092 pts/2    S+   17:24   0:00 grep --color=auto redis

可见和常规的不一样,多了一个cluster集群的标志

3)创建集群

  1. 创建集群
[root@dcdfa0e9eb71 clusterConf]# redis-cli --cluster create 127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002 127.0.0.1:9003  127.0.0.1:9004 127.0.0.1:9005 --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 127.0.0.1:9004 to 127.0.0.1:9000
Adding replica 127.0.0.1:9005 to 127.0.0.1:9001
Adding replica 127.0.0.1:9003 to 127.0.0.1:9002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: e4256cad47611df0aa3f6a41c89d24b91c1aa89e 127.0.0.1:9000
   slots:[0-5460] (5461 slots) master
M: abc2fa081d403c505b5d5127d3c3af9ffaa38538 127.0.0.1:9001
   slots:[5461-10922] (5462 slots) master
M: f70b68962d8fb9b485cd420b1731b997feac12ad 127.0.0.1:9002
   slots:[10923-16383] (5461 slots) master
S: 0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c 127.0.0.1:9003
   replicates abc2fa081d403c505b5d5127d3c3af9ffaa38538
S: b8bf4748d7039934d4ba43fe558a464592958eff 127.0.0.1:9004
   replicates f70b68962d8fb9b485cd420b1731b997feac12ad
S: 1baffc136c23418c05bda6805614fc4a29d02004 127.0.0.1:9005
   replicates e4256cad47611df0aa3f6a41c89d24b91c1aa89e
Can I set the above configuration? (type 'yes' to accept): yes
  1. 输入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 127.0.0.1:9000)
M: e4256cad47611df0aa3f6a41c89d24b91c1aa89e 127.0.0.1:9000
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c 127.0.0.1:9003
   slots: (0 slots) slave
   replicates abc2fa081d403c505b5d5127d3c3af9ffaa38538
M: f70b68962d8fb9b485cd420b1731b997feac12ad 127.0.0.1:9002
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 1baffc136c23418c05bda6805614fc4a29d02004 127.0.0.1:9005
   slots: (0 slots) slave
   replicates e4256cad47611df0aa3f6a41c89d24b91c1aa89e
S: b8bf4748d7039934d4ba43fe558a464592958eff 127.0.0.1:9004
   slots: (0 slots) slave
   replicates f70b68962d8fb9b485cd420b1731b997feac12ad
M: abc2fa081d403c505b5d5127d3c3af9ffaa38538 127.0.0.1:9001
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
  1. 查看nodes_9000.conf配置文件信息(自动生成的)
[root@dcdfa0e9eb71 9000]# cat nodes-9000.conf
0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c 127.0.0.1:9003@19003 slave abc2fa081d403c505b5d5127d3c3af9ffaa38538 0 1640769962000 2 connected
f70b68962d8fb9b485cd420b1731b997feac12ad 127.0.0.1:9002@19002 master - 0 1640769963139 3 connected 10923-16383
1baffc136c23418c05bda6805614fc4a29d02004 127.0.0.1:9005@19005 slave e4256cad47611df0aa3f6a41c89d24b91c1aa89e 0 1640769962000 1 connected
b8bf4748d7039934d4ba43fe558a464592958eff 127.0.0.1:9004@19004 slave f70b68962d8fb9b485cd420b1731b997feac12ad 0 1640769962000 3 connected
abc2fa081d403c505b5d5127d3c3af9ffaa38538 127.0.0.1:9001@19001 master - 0 1640769962134 2 connected 5461-10922
e4256cad47611df0aa3f6a41c89d24b91c1aa89e 127.0.0.1:9000@19000 myself,master - 0 1640769961000 1 connected 0-5460

4) 实测

1.客户端连接9000服务器

[root@dcdfa0e9eb71 9000]# redis-cli -c -p 9000
127.0.0.1:9000> set cluster success
-> Redirected to slot [14041] located at 127.0.0.1:9002
OK
127.0.0.1:9002> get cluster
"success"
127.0.0.1:9002>

可以看到和普通的写入方式不一样。这里把写入的数据重定向到 14041这个槽中

  1. 客户端连接9000对应的9003库
[root@dcdfa0e9eb71 /]# redis-cli -c -p 9003
127.0.0.1:9003> get cluster
-> Redirected to slot [14041] located at 127.0.0.1:9002
"success"
127.0.0.1:9002>

可以看到9003从客户端获取数据时,数据重定向到了14041这个插槽中获取数据

4)故障恢复

从库宕机测试

[root@dcdfa0e9eb71 /]# redis-cli -p 9003 shutdown

停止之后,其他主库会收到信息,查看9001日志文件,如下:

#从库9003丢失了
85305:M 29 Dec 2021 17:33:55.819 # Connection with replica 127.0.0.1:9003 lost.

85305:M 29 Dec 2021 17:34:15.057 * Marking node 0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c as failing (quorum reached).


#重新启动9003从库后收到恢复信息
85305:M 29 Dec 2021 17:36:08.901 * Clear FAIL state for node 0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c: replica is reachable again.
85305:M 29 Dec 2021 17:36:08.919 * Replica 127.0.0.1:9003 asks for synchronization
85305:M 29 Dec 2021 17:36:08.923 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for 'e67366f3da7d5999da896d02ff440b6a58d8280d', my replication IDs are 'd05670eb8e506f706cec220981513baa93ece8d3' and '0000000000000000000000000000000000000000')
85305:M 29 Dec 2021 17:36:08.927 * Starting BGSAVE for SYNC with target: disk
85305:M 29 Dec 2021 17:36:08.943 * Background saving started by pid 86375
86375:C 29 Dec 2021 17:36:08.986 * DB saved on disk
86375:C 29 Dec 2021 17:36:08.994 * RDB: 0 MB of memory used by copy-on-write
85305:M 29 Dec 2021 17:36:09.060 * Background saving terminated with success
85305:M 29 Dec 2021 17:36:09.082 * Synchronization with replica 127.0.0.1:9003 succeeded

主库宕机测试

停止主库9000

[root@192 /]# redis-cli -p 9000 shutdown

查看从库9005日志

......
#主库断开
85445:S 29 Dec 2021 17:37:39.203 * Connecting to MASTER 127.0.0.1:9000
85445:S 29 Dec 2021 17:37:39.207 * MASTER <-> REPLICA sync started
85445:S 29 Dec 2021 17:37:39.210 # Error condition on socket for SYNC: Connection refused
85445:S 29 Dec 2021 17:37:40.216 * Connecting to MASTER 127.0.0.1:9000
85445:S 29 Dec 2021 17:37:40.220 * MASTER <-> REPLICA sync started
85445:S 29 Dec 2021 17:37:40.224 # Error condition on socket for SYNC: Connection refused
85445:S 29 Dec 2021 17:37:41.229 * Connecting to MASTER 127.0.0.1:9000
85445:S 29 Dec 2021 17:37:41.235 * MASTER <-> REPLICA sync started
85445:S 29 Dec 2021 17:37:41.238 # Error condition on socket for SYNC: Connection refused
85445:S 29 Dec 2021 17:37:42.243 * Connecting to MASTER 127.0.0.1:9000
85445:S 29 Dec 2021 17:37:40.224 # Error condition on socket for SYNC: Connection refused
85445:S 29 Dec 2021 17:37:41.229 * Connecting to MASTER 127.0.0.1:9000
85445:S 29 Dec 2021 17:37:41.235 * MASTER <-> REPLICA sync started
85445:S 29 Dec 2021 17:37:41.238 # Error condition on socket for SYNC: Connection refused
85445:S 29 Dec 2021 17:37:42.243 * Connecting to MASTER 127.0.0.1:9000
85445:S 29 Dec 2021 17:37:42.247 * MASTER <-> REPLICA sync started
85445:S 29 Dec 2021 17:37:42.251 # Error condition on socket for SYNC: Connection refused
85445:S 29 Dec 2021 17:37:42.750 * FAIL message received from abc2fa081d403c505b5d5127d3c3af9ffaa38538 about e4256cad47611df0aa3f6a41c89d24b91c1aa89e
85445:S 29 Dec 2021 17:37:42.758 # Start of election delayed for 589 milliseconds (rank #0, offset 952).
85445:S 29 Dec 2021 17:37:42.790 # Cluster state changed: fail
85445:S 29 Dec 2021 17:37:43.284 * Connecting to MASTER 127.0.0.1:9000
85445:S 29 Dec 2021 17:37:43.287 * MASTER <-> REPLICA sync started
85445:S 29 Dec 2021 17:37:43.290 # Error condition on socket for SYNC: Connection refused
85445:S 29 Dec 2021 17:37:43.392 # Starting a failover election for epoch 7.
85445:S 29 Dec 2021 17:37:43.407 # Failover election won: I'm the new master.
#升级为主库
85445:S 29 Dec 2021 17:37:43.410 # configEpoch set to 7 after successful failover
85445:M 29 Dec 2021 17:37:43.413 * Discarding previously cached master state.
85445:M 29 Dec 2021 17:37:43.416 # Setting secondary replication ID to 7a352c533381b86b535eb666c7e9732ad531c23d, valid up to offset: 953. New replication ID is 27d71ccdd20522d54e8190f70c6bf1f64c454f92
85445:M 29 Dec 2021 17:37:43.420 # Cluster state changed: ok

概念

使用集群需要将相关配置打开,这些配置建议写在单独的配置中

cluster-enabled 是否开启集群

cluster-config-file 集群节点配置文件路径(启动后自动生成该文件)

cluster-node-timeout 超时时间

插槽分配

在一个集群中,所有键都会被分配给16384个插槽(slot),而每个数据库会负责处理其中的一部分插槽。

127.0.0.1:9001> cluster nodes
b8bf4748d7039934d4ba43fe558a464592958eff 127.0.0.1:9004@19004 slave f70b68962d8fb9b485cd420b1731b997feac12ad 0 1640770784000 3 connected
abc2fa081d403c505b5d5127d3c3af9ffaa38538 127.0.0.1:9001@19001 myself,master - 0 1640770785000 2 connected 5461-10922
e4256cad47611df0aa3f6a41c89d24b91c1aa89e 127.0.0.1:9000@19000 master,fail - 1640770645974 1640770641000 1 disconnected
1baffc136c23418c05bda6805614fc4a29d02004 127.0.0.1:9005@19005 master - 0 1640770784709 7 connected 0-5460
0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c 127.0.0.1:9003@19003 slave abc2fa081d403c505b5d5127d3c3af9ffaa38538 0 1640770782000 2 connected
f70b68962d8fb9b485cd420b1731b997feac12ad 127.0.0.1:9002@19002 master - 0 1640770785716 3 connected 10923-16383

可以看到,9000负责0-5460个插槽。初始化集群时分配给每个节点的插槽都是连续的 ,事实上redis对此并没有限制,可以将任意插槽分配任意节点负责。

键与插槽关系 Redis将每个键的键名的有效部分使用CRC16算法计算出哈希值,然后对剩余的16834取余。

查看集群中所有节点信息

[root@dcdfa0e9eb71 redis]# redis-cli -p 9001
127.0.0.1:9001> cluster nodes
b8bf4748d7039934d4ba43fe558a464592958eff 127.0.0.1:9004@19004 slave f70b68962d8fb9b485cd420b1731b997feac12ad 0 1640770784000 3 connected
abc2fa081d403c505b5d5127d3c3af9ffaa38538 127.0.0.1:9001@19001 myself,master - 0 1640770785000 2 connected 5461-10922
e4256cad47611df0aa3f6a41c89d24b91c1aa89e 127.0.0.1:9000@19000 master,fail - 1640770645974 1640770641000 1 disconnected
1baffc136c23418c05bda6805614fc4a29d02004 127.0.0.1:9005@19005 master - 0 1640770784709 7 connected 0-5460
0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c 127.0.0.1:9003@19003 slave abc2fa081d403c505b5d5127d3c3af9ffaa38538 0 1640770782000 2 connected
f70b68962d8fb9b485cd420b1731b997feac12ad 127.0.0.1:9002@19002 master - 0 1640770785716 3 connected 10923-16383

查看集群是否启用

127.0.0.1:9002> info cluster
# Cluster
cluster_enabled:1  #1表示启用

查看插槽分配情况

127.0.0.1:9002> cluster slots
1) 1) (integer) 0
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 9005
      3) "1baffc136c23418c05bda6805614fc4a29d02004"
2) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 9001
      3) "abc2fa081d403c505b5d5127d3c3af9ffaa38538"
   4) 1) "127.0.0.1"
      2) (integer) 9003
      3) "0071f328a2c0a6a58cfe373ce0d8c6137bf15e0c"
3) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 9002
      3) "f70b68962d8fb9b485cd420b1731b997feac12ad"
   4) 1) "127.0.0.1"
      2) (integer) 9004
      3) "b8bf4748d7039934d4ba43fe558a464592958eff"

故障恢复

在上一个集群中,每个节点都会定期的向其他节点发送ping名,并通过有没有收到回复来判断目标是否下线。

具体来说,集群中每个节点每隔一秒就会随机选择5个节点,然后选择最久没有响应节点发送ping命令。

一定时间内节点没有响应回复,则发起PING命令的节点会认为目标节点疑似下线。

如何确定某个节点下线?

1)一旦节点A认为节点B疑似下线,就会在集群中广播该消息,所有其他节点收到该消息后都会记录这一信息;

2)当集群中的某一个节点C收到半数以上的的节点认为B是疑似下线的状态,就会将B标记为下线,并且向集群中的其他节点传播该信息,从而使B在整个集群下线。

  • 在集群中,当一个主数据库下线时,就会出现一部分插槽无法写入的问题。如果该主数据库至少有一个从数据库,集群就进行故障恢复操作来将其中一个从数据库切换为主数据库。
服务类型 是否是主服务器 IP地址 端口
Redis-server 127.0.0.1 6379
Redis-server 127.0.0.1 6380
Redis-server 127.0.0.1 6381
Sentinel - 127.0.0.1 26379
Sentinel - 127.0.0.1 26380
Sentinel - 127.0.0.1 26381
上一篇:NFS网络存储,搭建考试系统,配合NFS实现文件共享


下一篇:Cisco Firepower 9300 Series FTD Software 7.1.0 & ASA Software 9.17.1 下载