Redis学习笔记

Redis学习笔记

启动redis

redis中的OPS 即operation per second 每秒操作次数。意味着每秒对Redis的持久化操作。

启动redis服务

redis-server ./redis.conf

启动redis客户端

-p: 端口号
要在 redis-cli 后面加上 --raw ,避免中文乱码

redis-cli -p 6379 --raw

测试redis是否正常启动

127.0.0.1:6379 > ping
PONG

显示pong说明服务已经正常运行

查看进程中redis服务和客户端

ps -ef|grep redis

在这里插入图片描述

关闭redis服务

127.0.0.1:6379 > shutdown
not connected > exit

性能测试benchmark

在这里插入图片描述

redis-benchmark

redis基本操作

# To enable logging to the system logger, just set 'syslog-enabled' to yes,
# and optionally update the other syslog parameters to suit your needs.
# syslog-enabled no

# Specify the syslog identity.
# syslog-ident redis

# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16

有16个数据库 默认使用第0个

select 切换数据库

选择第4个数据库

127.0.0.1:6379 > select 3
OK

dbsize 查看数据库大小

数据库大小为0

127.0.0.1:6379[3]> dbsize
(integer) 0

keys 查找所有给定模式的key

查找所有的key

127.0.0.1:6379[3]> keys *
1) "name"

flush 清空数据库

清空当前数据库

127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array)

清空所有数据库

127.0.0.1:6379[3]> flushall
OK
127.0.0.1:6379[3]> select 0
OK
127.0.0.1:6379 > keys *
(empty array)

exists 查找数据库中是否存在key

127.0.0.1:6379 > EXISTS age
(integer) 1

move 把当前数据库中的数据移动到指定数据库

127.0.0.1:6379 > move age 1
(integer) 1
127.0.0.1:6379 > move name 1
(integer) 1
127.0.0.1:6379 > select 1
OK
127.0.0.1:6379[1]> keys *
1) "name"
2) "age"

expire 设置键值的到期时间

127.0.0.1:6379[1]> EXPIRE name 10
(integer) 1

ttl 查看键值的存活时间

time to live

127.0.0.1:6379[1]> ttl name
(integer) 6
127.0.0.1:6379[1]> ttl name
(integer) 5
127.0.0.1:6379[1]> ttl name
(integer) 4
127.0.0.1:6379[1]> ttl name
(integer) 2
127.0.0.1:6379[1]> ttl name
(integer) -2
127.0.0.1:6379[1]> keys *
1) "age"

del 删除指定的键值

127.0.0.1:6379[1]> keys *
1) "age"
127.0.0.1:6379[1]> del age
(integer) 1
127.0.0.1:6379[1]> keys *
(empty array)

type 查看指定值的数据类型

127.0.0.1:6379[1]> type name
string

append 在key对应值后添加

如果key存在,则在值后添加

127.0.0.1:6379[1]> APPEND name hello
(integer) 8 #显示添加后的值长度
127.0.0.1:6379[1]> get name
"zwbhello"

如果key不存在,则新建一个键值(相当于set key)

127.0.0.1:6379[1]> APPEND name1 hello
(integer) 5
127.0.0.1:6379[1]> keys *
1) "age"
2) "name"
3) "name1"

redis五大数据类型

Redis 数据结构使用场景

String(字符串)

结构:key-value

set和get字符串

127.0.0.1:6379 > set name zwb
OK
127.0.0.1:6379 > get name
"zwb"

strlen 获得key对应值长度

127.0.0.1:6379[1]> STRLEN name
(integer) 13

incr 对key对应值+1(i++)

127.0.0.1:6379[1]> INCR views
(integer) 1 #显示+1后的值
127.0.0.1:6379[1]> INCR views
(integer) 2
127.0.0.1:6379[1]> get views
"2"

decr 对key对应值-1(i–)

127.0.0.1:6379[1]> DECR views
(integer) 1 #显示-1后的值
127.0.0.1:6379[1]> DECR views
(integer) 0
127.0.0.1:6379[1]> get views
"0"

incrby 对key对应值+指定值(i+=)

127.0.0.1:6379[1]> INCRBY views 10
(integer) 10
127.0.0.1:6379[1]> INCRBY views 10
(integer) 20
127.0.0.1:6379[1]> get views
"20"

decrby 对key对应值-指定值(i-=)

127.0.0.1:6379[1]> DECRBY views 10
(integer) 10
127.0.0.1:6379[1]> DECRBY views 10
(integer) 0
127.0.0.1:6379[1]> get views
"0"

getrange 获得key对应值的范围

127.0.0.1:6379[1]> GETRANGE name 0 5
"zwbhel" #0到5输出6个字符[0,5]
127.0.0.1:6379[1]> GETRANGE name 0 -1
"zwbhellohello" #0到-1输出整个字符串

setrange 设置key对应值从x开始的范围

127.0.0.1:6379[1]> SETRANGE name 3 byebye #从3开始后面的字符替换成byebye但其余不变,长度还是13
(integer) 13
127.0.0.1:6379[1]> get name
"zwbbyebyeello"

setex 设置键值并设置其过期时间

set and expire

127.0.0.1:6379[1]> SETEX key1 20 value1
OK
127.0.0.1:6379[1]> ttl key1
(integer) 14
127.0.0.1:6379[1]> keys *
1) "views"
2) "name"
3) "age"
4) "key1"
5) "name1"
127.0.0.1:6379[1]> ttl key1
(integer) -2
127.0.0.1:6379[1]> keys *
1) "views"
2) "name"
3) "age"
4) "name1"

setnx 设置键值如果键值不存在

set not exist 分布式锁中常用

127.0.0.1:6379[1]> SETNX name "zwb"
(integer) 0 #没有设置成功返回0
127.0.0.1:6379[1]> get name
"zwbbyebyeello"
127.0.0.1:6379[1]> SETNX key2 "value2"
(integer) 1 #设置成功返回1
127.0.0.1:6379[1]> get key2
"value2"

mset和mget 批量设置和获取键值

127.0.0.1:6379[1]> mset key1 value1 key2 value2 key3 value3
OK
127.0.0.1:6379[1]> mget key1 key2 key3
1) "value1"
2) "value2"
3) "value3"

msetnx 批量设置键值如果键值不存在

msetnx是原子性操作,要么都成功,要么都失败

127.0.0.1:6379[1]> MSETNX key1 value1 key4 value4
(integer) 0 #设置失败
127.0.0.1:6379[1]> keys *
1) "key3"
2) "key1"
3) "key2" #没有key4

set/mset和get/mget设置对象

127.0.0.1:6379[1]> set user:1 {name:zwb,age:20}
OK
127.0.0.1:6379[1]> get user:1
"{name:zwb,age:20}"
127.0.0.1:6379[1]> mset user:1:name zwb user:1:age 20
OK
127.0.0.1:6379[1]> mget user:1:name user:1:age
1) "zwb"
2) "20"

getset 先get值再set值

127.0.0.1:6379[1]> GETSET name zwb2
(nil) #如果不存在返回nil并设置值
127.0.0.1:6379[1]> GETSET name zwb3
"zwb2" #如果存在返回值并设置值
127.0.0.1:6379[1]> GETSET name zwb4
"zwb3"

List(列表)

结构:key value1-value2-value3
底层数据结构为双向链表,有序的

lpush 从队列左边插入元素(头插)

127.0.0.1:6379[1]> LPUSH list A
(integer) 1
127.0.0.1:6379[1]> LPUSH list B
(integer) 2
127.0.0.1:6379[1]> LPUSH list C
(integer) 3

rpush 从队列右边插入元素(尾插)

lrange 从队列中获取元素值

127.0.0.1:6379[1]> LRANGE list 0 -1 #从左开始
1) "C"
2) "B"
3) "A"
127.0.0.1:6379[1]> lrange list 0 -1
1) "A"
2) "B"
3) "A"
4) "B"
5) "C"
6) "D"
127.0.0.1:6379[1]> LRANGE list -3 -1 # 从右开始
1) "B"
2) "C"
3) "D"

lpop 从队列左边取出元素

127.0.0.1:6379[1]> LPOP list
"C"
127.0.0.1:6379[1]> LRANGE list 0 -1
1) "B"
2) "A"

rpop 从队列右边取出元素

lindex 通过索引在队列中获取元素

127.0.0.1:6379[1]> LRANGE list 0 -1
1) "D"
2) "C"
3) "A"
4) "B"
127.0.0.1:6379[1]> LINDEX list 0 #=0为第一个元素
"D"
127.0.0.1:6379[1]> LINDEX list 2 #>=0从左边开始数
"A"
127.0.0.1:6379[1]> LINDEX list -1 #<0从右边开始数
"B"
127.0.0.1:6379[1]> LINDEX list -2
"A"
127.0.0.1:6379[1]> LINDEX list -3
"C"

llen 获取队列的长度

127.0.0.1:6379[1]> llen list
(integer) 4

lrem 移除队列中指定元素

127.0.0.1:6379[1]> LRANGE list 0 -1
1) "D"
2) "D"
3) "D"
4) "D"
5) "C"
6) "A"
7) "B"
127.0.0.1:6379[1]> LREM list 1 D #count>0 从左往右移除1个
(integer) 1
127.0.0.1:6379[1]> LRANGE list 0 -1
1) "D"
2) "D"
3) "D"
4) "C"
5) "A"
6) "B"
127.0.0.1:6379[1]> LREM list -1 D #count<0 从右往左移除1个
(integer) 1
127.0.0.1:6379[1]> LRANGE list 0 -1
1) "D"
2) "D"
3) "C"
4) "A"
5) "B"
127.0.0.1:6379[1]> LREM list 0 D #count=0 移除全部符合的元素
(integer) 2
127.0.0.1:6379[1]> LRANGE list 0 -1
1) "C"
2) "A"
3) "B"

ltrim 通过索引截取队列

127.0.0.1:6379[1]> lrange list 0 -1
1) "A"
2) "B"
3) "C"
4) "D"
127.0.0.1:6379[1]> LTRIM list 1 2 #从左开始
OK
127.0.0.1:6379[1]> lrange list 0 -1
1) "B"
2) "C"
127.0.0.1:6379[1]> lrange list 0 -1
1) "D"
2) "A"
3) "B"
127.0.0.1:6379[1]> LTRIM list -2 -1 #从右开始
OK
127.0.0.1:6379[1]> lrange list 0 -1
1) "A"
2) "B"

rpoplpush 从队列右边pop元素push进另一个队列的左边

如果队列不存在,则创建队列

127.0.0.1:6379[1]> lrange list 0 -1
1) "A"
2) "B"
3) "A"
4) "B"
5) "C"
6) "D"
127.0.0.1:6379[1]> RPOPLPUSH list list2
"D"
127.0.0.1:6379[1]> RPOPLPUSH list list2
"C"
127.0.0.1:6379[1]> RPOPLPUSH list list2
"B"
127.0.0.1:6379[1]> lrange list 0 -1
1) "A"
2) "B"
3) "A"
127.0.0.1:6379[1]> lrange list2 0 -1
1) "B"
2) "C"
3) "D"

lset 设置队列中索引的元素值

如果找不到key或找不到队列中的索引值则报错

127.0.0.1:6379[1]> LRANGE list 0 -1
1) "A"
2) "B"
3) "A"
127.0.0.1:6379[1]> LSET list 2 C #从左开始
OK
127.0.0.1:6379[1]> LRANGE list 0 -1
1) "A"
2) "B"
3) "C"
127.0.0.1:6379[1]> LSET list -1 D
OK
127.0.0.1:6379[1]> LRANGE list 0 -1 #从右开始
1) "A"
2) "B"
3) "D"

linsert 通过元素值的前和后将新元素插入队列中

127.0.0.1:6379[1]> LRANGE list 0 -1
1) "A"
2) "B"
3) "D"
127.0.0.1:6379[1]> LINSERT list before D C #D的before插入C
(integer) 4
127.0.0.1:6379[1]> LRANGE list 0 -1
1) "A"
2) "B"
3) "C"
4) "D"
127.0.0.1:6379[1]> LINSERT list after A C  #A的after插入C
(integer) 5
127.0.0.1:6379[1]> LRANGE list 0 -1
1) "A"
2) "C"
3) "B"
4) "C"
5) "D"

Set(集合)

结构:key value1,value2,value3
set是不可重复的,无序的

sadd 往集合中添加元素

127.0.0.1:6379[1]> SADD set A
(integer) 1
127.0.0.1:6379[1]> SADD set B C
(integer) 2

smembers 查看集合中所有元素

127.0.0.1:6379[1]> SMEMBERS set
1) "C"
2) "B"
3) "A"

scard 获取集合中元素的数量

127.0.0.1:6379[1]> scard set
(integer) 3

srem 移除集合中指定元素

127.0.0.1:6379[1]> SREM set B
(integer) 1
127.0.0.1:6379[1]> SMEMBERS set
1) "C"
2) "A"

srandmember 从集合中随机抽取指定数量元素

127.0.0.1:6379[1]> SMEMBERS set
1) "D"
2) "F"
3) "A"
4) "C"
5) "B"
6) "E"
127.0.0.1:6379[1]> SRANDMEMBER set
"B"
127.0.0.1:6379[1]> SRANDMEMBER set
"A"
127.0.0.1:6379[1]> SRANDMEMBER set
"C"
127.0.0.1:6379[1]> SRANDMEMBER set 2
1) "D"
2) "E"
127.0.0.1:6379[1]> SRANDMEMBER set 2
1) "B"
2) "C"

spop 从集合中随机删除指定数量元素

127.0.0.1:6379[1]> SMEMBERS set
1) "F"
2) "A"
3) "C"
4) "D"
5) "B"
6) "E"
127.0.0.1:6379[1]> SPOP set
"B"
127.0.0.1:6379[1]> SPOP set 2
1) "F"
2) "A"
127.0.0.1:6379[1]> SMEMBERS set
1) "C"
2) "D"
3) "E"

smove 在集合中移动指定元素到另一个集合中

如果目标集合不存在,则创建集合

127.0.0.1:6379[1]> SMOVE set set2 C
(integer) 1
127.0.0.1:6379[1]> SMEMBERS set
1) "D"
2) "E"
127.0.0.1:6379[1]> SMEMBERS set2
1) "C"

sdiff 获得两个集合的差集

127.0.0.1:6379[1]> SMEMBERS set
1) "C"
2) "D"
3) "E"
127.0.0.1:6379[1]> SMEMBERS set2
1) "A"
2) "AB"
3) "C"
127.0.0.1:6379[1]> SDIFF set set2
1) "D"
2) "E"

sinter 获得两个集合的交集

127.0.0.1:6379[1]> SMEMBERS set
1) "C"
2) "D"
3) "E"
127.0.0.1:6379[1]> SMEMBERS set2
1) "A"
2) "AB"
3) "C"
127.0.0.1:6379[1]> sinter set set2
1) "C"

sunion 获得两个集合的并集

127.0.0.1:6379[1]> SMEMBERS set
1) "C"
2) "D"
3) "E"
127.0.0.1:6379[1]> SMEMBERS set2
1) "A"
2) "AB"
3) "C"
127.0.0.1:6379[1]> sunion set set2
1) "D"
2) "E"
3) "C"
4) "A"
5) "AB"

hash(哈希)

结构:KEY key1:value1,key2:value2 ,key3:value3

使用场景: 存储对象

hset和hget/hmget元素

127.0.0.1:6379[1]> hset hash key1 value1
(integer) 1
127.0.0.1:6379[1]> HGET hash key1
"value1"
127.0.0.1:6379[1]> HMGET hash key1 key2 key3
1) "value1"
2) "value2"
3) "value3"

hgetall 获取hash中所有键值对

127.0.0.1:6379[1]> HGETALL hash
1) "key1"
2) "value1"
3) "key2"
4) "value2"
5) "key3"
6) "value3"

hdel 删除指定hash中的key值

127.0.0.1:6379[1]> HDEL hash key2
(integer) 1
127.0.0.1:6379[1]> HGETALL hash
1) "key1"
2) "value1"
3) "key3"
4) "value3"

hlen 获取指定hash中的长度

127.0.0.1:6379[1]> HGETALL hash
1) "key1"
2) "value1"
3) "key3"
4) "value3"
127.0.0.1:6379[1]> HLEN hash
(integer) 2 #有两个键值对

hexists 判断指定hash中是否存在key

127.0.0.1:6379[1]> HGETALL hash
1) "key1"
2) "value1"
3) "key3"
4) "value3"
5) "key2"
6) "value2"
127.0.0.1:6379[1]> HEXISTS hash key3
(integer) 1

hkeys 获取指定hash中所有key值

127.0.0.1:6379[1]> HKEYS hash
1) "key1"
2) "key3"
3) "key2"

hvals 获取指定hash中所有value值

127.0.0.1:6379[1]> HVALS hash
1) "value1"
2) "value3"
3) "value2"

hincrby 对指定hash的key值加一个值

127.0.0.1:6379[1]> hset hash key4 0
(integer) 1
127.0.0.1:6379[1]> HINCRBY hash key4 1
(integer) 1
127.0.0.1:6379[1]> HINCRBY hash key4 1
(integer) 2
127.0.0.1:6379[1]> HINCRBY hash key4 -1
(integer) 1
127.0.0.1:6379[1]> HINCRBY hash key4 -1
(integer) 0

hsetnx 如果存在则设置 否则不设置

127.0.0.1:6379[1]> HSETNX hash key4 3
(integer) 0
127.0.0.1:6379[1]> HSETNX hash key5 3
(integer) 1

hset 设置对象

127.0.0.1:6379[1]> HSET user:1 name zwb age 20
(integer) 2
127.0.0.1:6379[1]> HGET user:1 name
"zwb"
127.0.0.1:6379[1]> HGET user:1 age
"20"

ZSet(有序集合)

结构:key score1-value1,score2-value2,score3-value3
有序集合底层数据结构是跳跃链表

使用场景: 带权重的集合,统计班级成绩,工资

zadd 往有序集合中添加元素

XX: 仅仅更新存在的成员,不添加新成员。
NX: 不更新存在的成员。只添加新成员。
CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。 所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,ZADD返回值只计算新添加成员的数量。
INCR: 当ZADD指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作。

127.0.0.1:6379[1]> ZADD zset 8000 zwb
(integer) 1
127.0.0.1:6379[1]> ZADD zset 5000 A 6000 B 
(integer) 2

zrangebyscore 根据score取出集合中的范围值

127.0.0.1:6379[1]> ZRANGEBYSCORE zset -inf +inf  withscores #-inf 负无穷 +inf 正无穷
1) "A"
2) "5000"
3) "B"
4) "6000"
5) "zwb"
6) "8000"

zrange 根据索引取出集合中的范围值(从小到大)

127.0.0.1:6379[1]> zrange zset  0 -1 
1) "A"
2) "B"
3) "zwb"

zrevrange 根据索引反转取出集合中的范围值(从大到小)

127.0.0.1:6379[1]> ZREVRANGE zset 0 -1
1) "zwb"
2) "A"

zrem 删除指定集合中的元素

127.0.0.1:6379[1]> ZREM zset B
(integer) 1
127.0.0.1:6379[1]> zrange zset  0 -1 
1) "A"
2) "zwb"

zcard 获取集合中的元素个数

127.0.0.1:6379[1]> ZCARD zset
(integer) 2

zcount 获取集合中指定score范围的个数

127.0.0.1:6379[1]> ZREVRANGE zset 0 -1 withscores
 1) "D"
 2) "10000"
 3) "zwb"
 4) "8000"
 5) "A"
 6) "5000"
 7) "C"
 8) "4000"
 9) "B"
10) "4000"
127.0.0.1:6379[1]> ZCOUNT zset 6000 10000
(integer) 2

三大特殊数据类型

geospatial 地理位置

底层用了ZSet,因此可以用ZSet的命令进行操作

使用场景: 定位,附近的人

geoadd 添加地理位置信息到key中

127.0.0.1:6379[1]> GEOADD china:city 104.065735 30.659462 chengdu
(integer) 1

geopos 从key中获得地理位置

127.0.0.1:6379[1]> GEOPOS china:city chengdu
1) 1) "104.06573742628097534"
   2) "30.65946118872339099"

geodist 获得两个地理位置之间的距离

127.0.0.1:6379[1]> GEODIST china:city chengdu foshan km
"1235.2189"

georadius 获得当前点的指定半径内的地理位置

127.0.0.1:6379[1]> GEORADIUS china:city 110 30 1000 km withdist withcoord count 1
1) 1) "chengdu"
   2) "574.3396"
   3) 1) "104.06573742628097534"
      2) "30.65946118872339099"

georadiusbymember 获得当前成员地理位置的指定半径内的地理位置

127.0.0.1:6379[1]> GEORADIUSBYMEMBER china:city chengdu 1000 km withdist withcoord
1) 1) "chengdu"
   2) "0.0000"
   3) 1) "104.06573742628097534"
      2) "30.65946118872339099"

geohash 获得地理位置的11位geohash字符串

127.0.0.1:6379[1]> GEOHASH china:city chengdu foshan
1) "wm6n2j6k730"
2) "ws07n0m2q20"

Hyperloglog 基数统计

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

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

hyperloglog不支持删除操作,因为无法判断已标记的二进制位是哪个元素生成

使用场景: 统计网页的UV(unique view:一个人访问多次但只算作一个人)

pfadd 添加任意元素到基数统计中

127.0.0.1:6379[1]> PFADD UV1 A B C D E
(integer) 1
127.0.0.1:6379[1]> PFADD UV2 C D F G H C
(integer) 1

pfcount 统计基数统计中的元素个数

127.0.0.1:6379[1]> PFCOUNT UV1
(integer) 5
127.0.0.1:6379[1]> PFCOUNT UV2
(integer) 5

pfmerge 合并多个基数统计中的值(并集)

127.0.0.1:6379[1]> PFMERGE UV3 UV1 UV2
OK
127.0.0.1:6379[1]> PFCOUNT UV3
(integer) 8

Bitmaps 位图

数据结构是操作二进制位进行记录,只有0和1两个状态,实质是string

使用场景: 保存两种状态的,比如是否登录,是否打卡,是否活跃

setbit 设置位图中的指定偏移量的位

模拟一周打卡

127.0.0.1:6379[1]> SETBIT sign 1 0
(integer) 0
127.0.0.1:6379[1]> SETBIT sign 2 0
(integer) 0
127.0.0.1:6379[1]> SETBIT sign 3 1
(integer) 0
127.0.0.1:6379[1]> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379[1]> SETBIT sign 5 1
(integer) 0
127.0.0.1:6379[1]> SETBIT sign 6 1
(integer) 0
127.0.0.1:6379[1]> SETBIT sign 7 0
(integer) 0

getbit 获得位图中的指定偏移量的位

127.0.0.1:6379[1]> GETBIT sign 4
(integer) 1
127.0.0.1:6379[1]> GETBIT sign 7
(integer) 0

bitcount 统计位图中1位的个数

127.0.0.1:6379[1]> BITCOUNT sign 
(integer) 4

redis事务

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务
  • 命令入队
  • 执行事务

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

multi 开启事务和exec执行事务

每个事务只能执行一次

127.0.0.1:6379 > MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED #命令入队
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) OK
3) "v2"
4) OK

discard 取消事务

127.0.0.1:6379 > MULTI
OK
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK
127.0.0.1:6379 > get k4
(nil)

编译时异常(所有命令都不会执行)

127.0.0.1:6379 > MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

运行时异常(只有异常的命令不会执行)

127.0.0.1:6379 > MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> INCR k1
QUEUED
127.0.0.1:6379(TX)> mget k1 k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range
4) 1) "v1"
   2) "v2"

watch 监控key值

如果在事务执行之前key被其他命令所改动,那么事务将被打断(乐观锁,比较version值,如果被更改则失败)

127.0.0.1:6379[1]> set money 1000
OK
127.0.0.1:6379[1]> set out 0
OK
127.0.0.1:6379[1]> WATCH money
OK
127.0.0.1:6379[1]> MULTI
OK
127.0.0.1:6379[1](TX)> DECRBY money 50
QUEUED
127.0.0.1:6379[1](TX)> INCRBY out 50
QUEUED
#在这时事务没有被执行,money值在其它客户端被其他命令进行更改
127.0.0.1:6379[1]> INCRBY money 1000
(integer) 2000
#这时执行命令,发现监控的money值被更改,事务执行失败
127.0.0.1:6379[1](TX)> EXEC
(nil)

exec和discard自动解锁,因此需要重新上锁

unwatch 取消监控key值

127.0.0.1:6379[1]> WATCH money
OK
127.0.0.1:6379[1]> UNWATCH
OK
127.0.0.1:6379[1]> MULTI
OK
127.0.0.1:6379[1](TX)> INCRBY money 1000
QUEUED
127.0.0.1:6379[1](TX)> EXEC
1) (integer) 2000

Jedis

连接服务器的redis

1、开放linux防火墙端口6379和阿里云安全组端口6379

2、设置redis服务的配置文件
redis配置文件设置
设置完配置文件后记得重启redis服务

3、添加依赖

		<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>

4、测试连接

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("阿里云公网ip",6379);
        jedis.auth("123456");
        System.out.println(jedis.ping());
    }
}

Jedis事务

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("端口号",6379);
        jedis.auth("123456");
        //开启事务
        Transaction multi = jedis.multi();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name","zwb");
        jsonObject.put("age","20");
        String string = jsonObject.toJSONString();
        //监控name值
        //jedis.watch("name");
        try {
            multi.set("user1",string);
            //会产生运行时错误,但不影响其他语句执行
            multi.incr("user1");
            multi.exec();
        } catch (Exception e) {
            //产生编译时异常时,关闭事务
            multi.discard();
            e.printStackTrace();
        } finally {
            //执行事务后测试值
            System.out.println(jedis.get("user1"));
            jedis.flushAll();
            jedis.close();
        }
    }
}

springboot整合redis

1、设置application.properties

spring.redis.host=端口号
spring.redis.port=6379
spring.redis.password=123456

2、测试

@SpringBootTest
class Redis02SpringbootApplicationTests {

	@Autowired
	private  RedisTemplate redisTemplate;

	@Test
	void contextLoads() throws JsonProcessingException {
		User user1 = new User("zwb",20);
		User user2 = new User("A",18);
		//将对象序列化为string类型
		String s1 = new ObjectMapper().writeValueAsString(user1);
		String s2 = new ObjectMapper().writeValueAsString(user2);
		redisTemplate.opsForValue().set("user1",s1);
		redisTemplate.opsForValue().set("user2",s2);
		System.out.println(redisTemplate.opsForValue().get("user1"));
		System.out.println(redisTemplate.opsForValue().get("user2"));
	}
}

redis.conf详解

单位

#Note on units: when memory size is needed, it is possible to specify
#it in the usual form of 1k 5GB 4M and so forth:
#1k => 1000 bytes
#1kb => 1024 bytes
#1m => 1000000 bytes
#1mb => 10241024 bytes
#1g => 1000000000 bytes
#1gb => 1024
1024*1024 bytes

单位不区分大小写
#units are case insensitive so 1GB 1Gb 1gB are all the same.

INCLUDE

使用多个配置文件

#include /path/to/local.conf
#include /path/to/other.conf

NETWORK

#绑定ip
#bind 127.0.0.1 

#保护模式
protected-mode no 

GENERAL

#守护进程的方式运行(后台运行)
daemonize yes 

#后台方式运行需要绑定pid文件
pidfile /www/server/redis/redis.pid 

# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
#日志输出级别
loglevel notice 

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

#数据库大小
databases 16 

SNAPSHOTTING

#900s内如果有1个以上key进行修改,进行持久化
save 900 1 
#300s内如果有10个以上key进行修改,进行持久化
save 300 10 
#60s内如果有10000个以上key进行修改,进行持久化
save 60 10000 

#如果持久化出错,是否停止写入
stop-writes-on-bgsave-error yes

#是否压缩rdb文件(消耗cpu资源)
rdbcompression yes

#保存rdb文件时是否检查文件
rdbchecksum yes

#保存rdb文件的名字
dbfilename dump.rdb

#文件保存目录
dir /www/server/redis/

REPLICATION

SECURITY

#设置密码
#requirepass foobared

CLIENTS

#最大客户端连接数
# maxclients 10000

#redis最大内存
# maxmemory <bytes>

#内存达到上限后的处理策略
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
#
# LRU means Least Recently Used
# LFU means Least Frequently Used
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
#       operations, when there are no suitable keys for eviction.
#
#       At the date of writing these commands are: set setnx setex append
#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
#       getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy noeviction

APPEND ONLY MODE

#是否开始aof模式,默认使用rdb
appendonly no

#保存aof文件的名字
appendfilename "appendonly.aof"

#每次修改都会同步
# appendfsync always

#每秒执行同步
appendfsync everysec

#不执行同步
# appendfsync no

redis持久化

RDB和AOF的区别

RDB(Redis DataBase)

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
在这里插入图片描述

触发规则

1、主动Save或Bgsave(Backgroundsave)达到配置文件中被动触发规则

  • SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
  • BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。 Redis 服务器在BGSAVE 执行期间仍然可以继续处理客户端的请求。

Save是阻塞方式的;bgsave是非阻塞方式的。

2、执行flushall或flushdb

3、正常退出redis

优点

  • Redis只包含一个文件,灾难恢复非常容易,并且可以十分轻松地进行备份
  • 性能高,只需fork出子进程,让子进程完成持久化操作,可以避免服务进程(主进程)降低效率去进行IO操作
  • 相比于AOF机制,如果数据集很大,RDB的启动效率会更高

缺点

Redis持久化之大数据服务暂停问题

  • 系统在定时持久化的时候出现宕机的情况,最后一次持久化之后未保存的数据将会丢失,无法最大程度的避免数据丢失
  • 如果当数据集较大时,可能会导致整个服务器停止服务

AOF(AppendOnlyFile)

在这里插入图片描述
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

同时启用的话,redis服务器重启时优先加载AOF文件(安全性高),主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。

优点

  • 带来了更高的数据安全性,可以保证每次数据的安全,如果产生宕机最多一秒内的数据丢失
  • 对日志文件的写入采用append模式,因此出现宕机不会破坏日志文件已存在的内容
  • AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作,导出(export) AOF 文件也非常简单:如果不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到FLUSHALL 执行之前的状态

缺点

  • 在相同数量的数据集情况下,AOF文件通常大于RDB文件,RDB恢复大数据集时速度比AOF快
  • AOF运行时效率比RDB低

redis发布订阅

使用场景:
1、实时消息系统
2、订阅关注
在这里插入图片描述
此时客户端client2、client5、client1订阅了频道channel1
在这里插入图片描述
对频道channel1发布消息,channel1将消息发送给订阅的三个客户端
在这里插入图片描述
subscribe订阅频道后,redis维护了字典,字典的键为各个频道,字典的值为一个订阅当前频道的订阅者链表,subscribe命令就是将客户端添加到频道的链表中。

publish向订阅者发布消息时,redis会以当前频道作为键,在维护的字典中查找当前频道的所有订阅者的链表,遍历该链表,将消息发布给所有客户端
在这里插入图片描述

为什么在 redis-cli 中使用了 subscribe 命令之后无法再执行 unsubscribe 等的命令

subscribe和publish 订阅和发布消息

A客户端上同一服务(订阅频道)

SUBSCRIBE zwb A B
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "zwb"
3) (integer) 1
1) "subscribe"
2) "A"
3) (integer) 2
1) "subscribe"
2) "B"
3) (integer) 3
1) "message"
2) "zwb"
3) "hello world!"

B客户端上同一服务(发布消息)

127.0.0.1:6379> PUBLISH zwb "hello world!"
(integer) 1

psubscribe 以*作为匹配符的模式进行订阅

A客户端同一服务(订阅频道)

127.0.0.1:6379 > PSUBSCRIBE zw*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "zw*"
3) (integer) 1
1) "pmessage"
2) "zw*"
3) "zwb"
4) "hello zwb"

B客户端上同一服务(发布消息)

127.0.0.1:6379 > PUBLISH zwb "hello zwb"
(integer) 1

redis主从复制

指将一台redis服务器数据复制到其他redis服务器,前者为主节点(master/leader),后者为从节点(slave/follower),数据复制为单向,只能从主节点到从节点。主节点以写为主,从节点以读为主。

默认每台redis服务器都是主节点,一个主节点可以有多个从节点,一个从节点只能有一个主节点

主从复制的作用:

  • 数据冗余:实现了热备份,持久化之外的另一种数据冗余的方式
  • 故障恢复:主节点出现问题,可以让从节点代替主节点,实现快速故障恢复,也是服务冗余
  • 负载均衡:实现了读写分离,由主节点提供写服务,从节点提供读服务,分担服务器负载。尤其读多写少的场景下,多个从节点可以分担读负载,大大提高redis服务器的并发量
  • 高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案在这里插入图片描述

工作流程

在这里插入图片描述

建立连接

在这里插入图片描述

数据同步

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

命令传播

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

在这里插入图片描述

心跳机制

在这里插入图片描述

在这里插入图片描述

常见问题

频繁的全量复制

在这里插入图片描述
在这里插入图片描述

频繁的网络中断

在这里插入图片描述
在这里插入图片描述

数据不一致

在这里插入图片描述

复制原理

从机启动成功连接到主机后会发送sync同步命令,主机接到命令后启动存盘进程,同时收集所有修改数据集命令,在后台进程执行完毕后,主机将传送整个数据文件到从机,并完成一次全量复制

全量复制:从机在接收到数据文件后,将其存盘并加载到内存中。

增量复制:主机继续将收集到的修改命令依次传给从机,完成同步

从机重新连接到主机,一次全量复制将自动执行

slaveof 将当前主机设置为指定地址指定端口号的从机

127.0.0.1:6380 > SLAVEOF 127.0.0.1 6379 
OK

info replication 获得当前主机的主从信息

主机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=76437,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=76437,lag=0
master_failover_state:no-failover
master_replid:bcae5c3226a40515bff5f6ef3dc647209247a663
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:76437
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:76437

从机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:8
master_sync_in_progress:0
slave_repl_offset:76437
slave_priority:100
slave_read_only:1
connected_slaves:0
master_failover_state:no-failover
master_replid:bcae5c3226a40515bff5f6ef3dc647209247a663
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:76437
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:76437

slaveof no one 当前从机断开与主机的连接

如果主机断开了连接,可以用slaveof no one让自己变成主机,其他节点手动连接最新的主节点。

127.0.0.1:6380 > SLAVEOF no one
OK
127.0.0.1:6380 > info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:a8f9688e665b120d808b92060a51669d8e32b308
master_replid2:bcae5c3226a40515bff5f6ef3dc647209247a663
master_repl_offset:148089
second_repl_offset:148090
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:148089

哨兵模式

Redis哨兵的详解
哨兵的作用:

  • 集群监控:负责监控redis master和slave进程是否正常工作
  • 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  • 故障转移:如果master node挂掉了,会自动转移到slave node上
  • 配置中心:如果故障转移发生了,通知client客户端新的master地址

后台监控主机是否断连,当主服务器宕机后,根据投票数自动将从机转换为主机。
哨兵是独立的进程,哨兵向redis服务器发送命令,等待redis服务器响应,从而监控运行的多个redis实例
在这里插入图片描述
当哨兵检测到主机宕机,自动投票选取一个从机变成主机,然后通过发布订阅模式通知其他从机修改配置文件,让其他从机切换主机。

一般使用多个哨兵进行监控,各个哨兵之间也会进行监控,形成多哨兵模式
在这里插入图片描述
如果主机宕机,哨兵1先检测到,系统并不会马上failover,仅仅是哨兵1主观认为主机不可用,称之为主观下线。当其余哨兵也检测到主机不可用,并数量达到设定的值,哨兵会进行投票,投票结果由一个哨兵发起,进行failover(故障转移)。切换成功后,通过发布订阅模式,让每个哨兵把自己监控的从机切换成主机,称之为客观下线

工作原理

监控

在这里插入图片描述
在这里插入图片描述

通知

在这里插入图片描述

故障转移

在这里插入图片描述
挑选哨兵leader
在这里插入图片描述
让哨兵leader选择从机当选主机,以挑选原则进行筛选抉择
在这里插入图片描述

sentinel.conf详解

Redis及其Sentinel配置项详细说明

#当至少quorum个哨兵监测到主机下线时,进行failover
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6380 1
#主机需要在多少时间后才会被哨兵主动认为不可用,默认30s
sentinel down-after-milliseconds <master-name> <milliseconds>
#指定了在发生failover主从切换时最多可以有多少个从机同时对新的主机进行同步,如果并发数量过多,会导致更多的从机因为同步而不可用
sentinel parallel-syncs <master-name> <numreplicas>
#failover-timeout 可以用在以下这些方面: 
1. 同一个sentinel对同一个master两次failover之间的间隔时间。
2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
3.当想要取消一个正在进行的failover所需要的时间。  
4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了。
#默认为3min
sentinel failover-timeout <master-name> <milliseconds>

启动哨兵

如果主从机中有密码,需要在从机redis.conf中配置主机密码和在sentinel.conf中配置主从机密码

在sentinel.conf文件配置(设置哨兵连接主机和从机)

sentinel auth-pass mymaster 密码

在从机的redis.conf中配置主机的密码(设置从机连接主机)

masterauth 密码

当前三台服务器结构
在这里插入图片描述

成功连接主机和从机

[root@iZwz99986fr8n033m5btqnZ redis]# redis-sentinel sentinel.conf 
29999:X 13 Jun 2021 16:58:53.472 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
29999:X 13 Jun 2021 16:58:53.472 # Redis version=6.2.1, bits=64, commit=00000000, modified=0, pid=29999, just started
29999:X 13 Jun 2021 16:58:53.472 # Configuration loaded
29999:X 13 Jun 2021 16:58:53.473 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.1 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 29999
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

29999:X 13 Jun 2021 16:58:53.476 # Sentinel ID is 8a1e6784906f54fff8f334f20cd678e6c399b7f7
#监控主机 quorum确认客观下线的最少哨兵数为1
29999:X 13 Jun 2021 16:58:53.476 # +monitor master mymaster 127.0.0.1 6379 quorum 1
#监控到从机为6380 其主机为6379
29999:X 13 Jun 2021 16:58:53.477 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379

故障转移

当6379断连时,进行故障转移,将6380设置为主机

[root@iZwz99986fr8n033m5btqnZ redis]# redis-sentinel sentinel.conf 
29999:X 13 Jun 2021 16:58:53.472 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
29999:X 13 Jun 2021 16:58:53.472 # Redis version=6.2.1, bits=64, commit=00000000, modified=0, pid=29999, just started
29999:X 13 Jun 2021 16:58:53.472 # Configuration loaded
29999:X 13 Jun 2021 16:58:53.473 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.1 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 29999
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

29999:X 13 Jun 2021 16:58:53.476 # Sentinel ID is 8a1e6784906f54fff8f334f20cd678e6c399b7f7
29999:X 13 Jun 2021 16:58:53.476 # +monitor master mymaster 127.0.0.1 6379 quorum 1
29999:X 13 Jun 2021 16:58:53.477 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
#监测到主机6379主观下线
29999:X 13 Jun 2021 17:04:48.071 # +sdown master mymaster 127.0.0.1 6379
#监测到主机6379客观下线,因为达到quorum客观下线的哨兵数
29999:X 13 Jun 2021 17:04:48.071 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
#epoch纪元被更新,epoch用于给事件增加版本号(相当于投票次数)
29999:X 13 Jun 2021 17:04:48.071 # +new-epoch 1
#尝试故障转移
29999:X 13 Jun 2021 17:04:48.071 # +try-failover master mymaster 127.0.0.1 6379
#投票选举哨兵的leader
29999:X 13 Jun 2021 17:04:48.074 # +vote-for-leader 8a1e6784906f54fff8f334f20cd678e6c399b7f7 1
#已选举出哨兵leader
29999:X 13 Jun 2021 17:04:48.074 # +elected-leader master mymaster 127.0.0.1 6379
#选择6379主机下可用的从机
29999:X 13 Jun 2021 17:04:48.074 # +failover-state-select-slave master mymaster 127.0.0.1 6379
#选择到从机6380,是6379的从机
29999:X 13 Jun 2021 17:04:48.157 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
#向从机6380发送slaveof no one命令,让6380不是从机,成为主机
29999:X 13 Jun 2021 17:04:48.157 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
#等待从机晋升为主机
29999:X 13 Jun 2021 17:04:48.257 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
#从机6380成功晋升为主机
29999:X 13 Jun 2021 17:04:48.635 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
#对原主机6379的从机重新配置(场景下6379只有6380为从机,因此不用重新配置)
29999:X 13 Jun 2021 17:04:48.636 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
#对6379故障转移结束
29999:X 13 Jun 2021 17:04:48.685 # +failover-end master mymaster 127.0.0.1 6379
#原主机6379和现主机6380切换,让原主机6379变为6380的从机
29999:X 13 Jun 2021 17:04:48.685 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
#6380增加从机6379
29999:X 13 Jun 2021 17:04:48.685 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
#6380增加主机6381
29999:X 13 Jun 2021 17:04:49.192 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
#监测到从机6379主观下线
29999:X 13 Jun 2021 17:05:18.753 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
#此时6379重连
29999:X 13 Jun 2021 17:47:02.993 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
#6379已转变为6380的从机
29999:X 13 Jun 2021 17:47:12.933 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

6379的主从信息

127.0.0.1:6379 > info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:235991
slave_priority:100
slave_read_only:1
connected_slaves:0
master_failover_state:no-failover
master_replid:f9e2117f537d8d94006700c06724dba66bb401bc
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:235991
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:234065
repl_backlog_histlen:1927

6380的主从信息

127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=236656,lag=0
slave1:ip=127.0.0.1,port=6379,state=online,offset=236656,lag=0
master_failover_state:no-failover
master_replid:f9e2117f537d8d94006700c06724dba66bb401bc
master_replid2:d51147c500ee4ce413195e524416d926ac150d37
master_repl_offset:236656
second_repl_offset:22641
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:236656

redis集群

在这里插入图片描述
在这里插入图片描述

集群结构设计

数据存储设计

在这里插入图片描述
在这里插入图片描述

内部通讯设计

最多两次即可命中数据库
在这里插入图片描述

搭建集群

部署Redis Cluster 6.0

Not all 16384 slots are covered by nodes

#cli集成的fix方法
redis-cli --cluster fix 127.0.0.1:6379

Node is not empty. Either the node already knows other nodes

redis6把ruby集成在cli,可以不用ruby进行搭建

集群数量至少要6个以上,因为主机数量必须为3个以上,如果每个主机都只有一个从机,那么需要6个服务器以上。

1、修改redis.conf配置

#开启cluster集群
cluster-enabled yes
#配置文件名字
cluster-config-file nodes-6379.conf
#节点无法访问被视为故障的毫秒数
cluster-node-timeout 15000

2、开启所有redis服务

[root@iZwz99986fr8n033m5btqnZ redis]# ps -ef|grep redis
root     17413     1  0 16:09 ?        00:00:04 redis-server *:6379 [cluster]
root     18050     1  0 16:14 ?        00:00:04 redis-server *:6380 [cluster]
root     18060     1  0 16:14 ?        00:00:04 redis-server *:6382 [cluster]
root     18739     1  0 16:20 ?        00:00:03 redis-server *:6381 [cluster]
root     18748     1  0 16:20 ?        00:00:04 redis-server *:6383 [cluster]
root     18966     1  0 16:22 ?        00:00:03 redis-server *:6384 [cluster]
root     21465 16310  0 16:45 pts/2    00:00:00 grep --color=auto redis

3、开启cluster集群

#redis6版本不需要ruby,直接在cli中搭建集群
#--cluster create搭建集群输入ip端口号
#--cluster-replicas集群中主机的从机数量
redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1 -a 密码

Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> 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:6383 to 127.0.0.1:6379
Adding replica 127.0.0.1:6384 to 127.0.0.1:6380
Adding replica 127.0.0.1:6382 to 127.0.0.1:6381
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 8f05addc7e369d5d03e3beb9964603ec486e4390 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
M: 9c9b9c1a663a16b5d9cdb070a5876b343df195d0 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
M: d3e531486f42af8df14cfac926ba87442b270c7a 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
S: 6fbbe5c44b94b8c477dfbb60ad249b5c81fe5bd7 127.0.0.1:6382
   replicates 9c9b9c1a663a16b5d9cdb070a5876b343df195d0
S: 17e277d0b839ae0a7eace9ec8e2030a56313bd3d 127.0.0.1:6383
   replicates d3e531486f42af8df14cfac926ba87442b270c7a
S: 3669417a606673067ae338877fda119b71813e8d 127.0.0.1:6384
   replicates 8f05addc7e369d5d03e3beb9964603ec486e4390
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 127.0.0.1:6379)
M: 8f05addc7e369d5d03e3beb9964603ec486e4390 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 17e277d0b839ae0a7eace9ec8e2030a56313bd3d 127.0.0.1:6383
   slots: (0 slots) slave
   replicates d3e531486f42af8df14cfac926ba87442b270c7a
S: 3669417a606673067ae338877fda119b71813e8d 127.0.0.1:6384
   slots: (0 slots) slave
   replicates 8f05addc7e369d5d03e3beb9964603ec486e4390
M: 9c9b9c1a663a16b5d9cdb070a5876b343df195d0 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 6fbbe5c44b94b8c477dfbb60ad249b5c81fe5bd7 127.0.0.1:6382
   slots: (0 slots) slave
   replicates 9c9b9c1a663a16b5d9cdb070a5876b343df195d0
M: d3e531486f42af8df14cfac926ba87442b270c7a 127.0.0.1:6381
   slots:[10923-16383] (5461 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.

4、查看nodes结点信息

显示各个主机的槽和从机信息

[root@iZwz99986fr8n033m5btqnZ redis]# cat nodes-6379.conf 
17e277d0b839ae0a7eace9ec8e2030a56313bd3d 127.0.0.1:6383@16383 slave d3e531486f42af8df14cfac926ba87442b270c7a 0 1623745372000 3 connected
3669417a606673067ae338877fda119b71813e8d 127.0.0.1:6384@16384 slave 8f05addc7e369d5d03e3beb9964603ec486e4390 0 1623745372000 1 connected
9c9b9c1a663a16b5d9cdb070a5876b343df195d0 127.0.0.1:6380@16380 master - 0 1623745373000 2 connected 5461-10922
6fbbe5c44b94b8c477dfbb60ad249b5c81fe5bd7 127.0.0.1:6382@16382 slave 9c9b9c1a663a16b5d9cdb070a5876b343df195d0 0 1623745374349 2 connected
8f05addc7e369d5d03e3beb9964603ec486e4390 127.0.0.1:6379@16379 myself,master - 0 1623745371000 1 connected 0-5460
d3e531486f42af8df14cfac926ba87442b270c7a 127.0.0.1:6381@16381 master - 0 1623745374000 3 connected 10923-16383
vars currentEpoch 6 lastVoteEpoch 0

集群set数据

[root@iZwz99986fr8n033m5btqnZ redis]# redis-cli -p 6379
127.0.0.1:6379 > set name zwb
#发现计算出来key值应在5798号槽,无法set当前6379号主机,提示该槽在6380
(error) MOVED 5798 127.0.0.1:6380
#-c代表操作集群
[root@iZwz99986fr8n033m5btqnZ redis]# redis-cli -p 6379 -c -a 密码
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379 > set name zwb
#根据计算出的槽号进行重定向到6380继续进行set
-> Redirected to slot [5798] located at 127.0.0.1:6380
OK
127.0.0.1:6380 > keys *
1) "name"

集群get数据

redis原生cluster中的slave不会处理读写请求,即便get操作,客户端也会收到重定向到master的指令(不进行读写分离,从机只做备份)

[root@iZwz99986fr8n033m5btqnZ redis]# redis-cli -p 6382 -c -a 密码
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6382 > keys *
1) "name"
127.0.0.1:6382 > get name
#从机只做备份,当读取该数据的时候,数据还是会重定向到主机进行读取
-> Redirected to slot [5798] located at 127.0.0.1:6380
"zwb"

集群主机下线和主从切换

127.0.0.1:6380 > CLUSTER nodes
6fbbe5c44b94b8c477dfbb60ad249b5c81fe5bd7 127.0.0.1:6382@16382 slave 9c9b9c1a663a16b5d9cdb070a5876b343df195d0 0 1623753044000 2 connected
17e277d0b839ae0a7eace9ec8e2030a56313bd3d 127.0.0.1:6383@16383 slave d3e531486f42af8df14cfac926ba87442b270c7a 0 1623753045408 3 connected
3669417a606673067ae338877fda119b71813e8d 127.0.0.1:6384@16384 slave 8f05addc7e369d5d03e3beb9964603ec486e4390 0 1623753046410 1 connected
9c9b9c1a663a16b5d9cdb070a5876b343df195d0 127.0.0.1:6380@16380 myself,master - 0 1623753044000 2 connected 5461-10922
8f05addc7e369d5d03e3beb9964603ec486e4390 127.0.0.1:6379@16379 master - 0 1623753045000 1 connected 0-5460
d3e531486f42af8df14cfac926ba87442b270c7a 127.0.0.1:6381@16381 master - 0 1623753044000 3 connected 10923-16383
127.0.0.1:6380 > SHUTDOWN
not connected> exit
#这是6380主机已下线
[root@iZwz99986fr8n033m5btqnZ redis]# redis-cli -p 6382 -c -a 密码
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6382 > CLUSTER NODES
8f05addc7e369d5d03e3beb9964603ec486e4390 127.0.0.1:6379@16379 master - 0 1623753334978 1 connected 0-5460
3669417a606673067ae338877fda119b71813e8d 127.0.0.1:6384@16384 slave 8f05addc7e369d5d03e3beb9964603ec486e4390 0 1623753332000 1 connected
#原主机的从机6382变为主机
6fbbe5c44b94b8c477dfbb60ad249b5c81fe5bd7 127.0.0.1:6382@16382 myself,master - 0 1623753332000 7 connected 5461-10922
d3e531486f42af8df14cfac926ba87442b270c7a 127.0.0.1:6381@16381 master - 0 1623753333000 3 connected 10923-16383
#发现原主机6380下线
9c9b9c1a663a16b5d9cdb070a5876b343df195d0 127.0.0.1:6380@16380 master,fail - 1623753303866 1623753297000 2 disconnected
17e277d0b839ae0a7eace9ec8e2030a56313bd3d 127.0.0.1:6383@16383 slave d3e531486f42af8df14cfac926ba87442b270c7a 0 1623753333975 3 connected
#此时原主机6380上线
127.0.0.1:6382 > CLUSTER NODES
8f05addc7e369d5d03e3beb9964603ec486e4390 127.0.0.1:6379@16379 master - 0 1623753379187 1 connected 0-5460
3669417a606673067ae338877fda119b71813e8d 127.0.0.1:6384@16384 slave 8f05addc7e369d5d03e3beb9964603ec486e4390 0 1623753376168 1 connected
6fbbe5c44b94b8c477dfbb60ad249b5c81fe5bd7 127.0.0.1:6382@16382 myself,master - 0 1623753376000 7 connected 5461-10922
d3e531486f42af8df14cfac926ba87442b270c7a 127.0.0.1:6381@16381 master - 0 1623753378179 3 connected 10923-16383
#发现原主机6380变为主机6382的从机
9c9b9c1a663a16b5d9cdb070a5876b343df195d0 127.0.0.1:6380@16380 slave 6fbbe5c44b94b8c477dfbb60ad249b5c81fe5bd7 0 1623753377171 7 connected
17e277d0b839ae0a7eace9ec8e2030a56313bd3d 127.0.0.1:6383@16383 slave d3e531486f42af8df14cfac926ba87442b270c7a 0 1623753378000 3 connected

redis缓存预热

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

redis缓存雪崩

大量key在一小段时间内集中过期,导致数据库查询压力过大
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

redis缓存击穿

某个key访问量非常大,key在redis中某个时间过期导致大量请求直接击中数据库
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

redis缓存穿透

请求数据库没有的数据,大量非正常请求直接穿过redis访问数据库
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

性能指标监控

Redis性能指标监控

监控指标

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

监控方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

热key问题

谈谈redis的热key问题如何解决

布隆过滤器

布隆过滤器

springboot整合docker的redis集群

docker run(仅供参考)

springboot连接docker的redis映射

注意:需要配置docker的容器ip为本机ip 否则springboot无法映射到docker的redis集群

--net=host

docker run --name redis1 --restart always -p 6479:6479  -v /home/redis-docker:/usr/local/etc/redis -v /home/redis-docker/data:/redis  --net=host -d redis
docker run --name redis2 --restart always -p 6480:6480  -v /home/redis-docker:/usr/local/etc/redis -v /home/redis-docker/data:/redis --net=host -d redis
docker run --name redis3 --restart always -p 6481:6481  -v /home/redis-docker:/usr/local/etc/redis -v /home/redis-docker/data:/redis --net=host -d redis
docker run --name redis4 --restart always -p 6482:6482  -v /home/redis-docker:/usr/local/etc/redis -v /home/redis-docker/data:/redis --net=host -d redis
docker run --name redis5 --restart always -p 6483:6483  -v /home/redis-docker:/usr/local/etc/redis -v /home/redis-docker/data:/redis --net=host -d redis
docker run --name redis6 --restart always -p 6484:6484  -v /home/redis-docker:/usr/local/etc/redis -v /home/redis-docker/data:/redis --net=host -d redis

sed脚本修改文本

sed "s/6379/6479/g" redis79.conf > ../redis-docker/redis79.conf
sed "s/6380/6480/g" redis80.conf > ../redis-docker/redis80.conf
sed "s/6381/6481/g" redis81.conf > ../redis-docker/redis81.conf
sed "s/6382/6482/g" redis82.conf > ../redis-docker/redis82.conf
sed "s/6383/6483/g" redis83.conf > ../redis-docker/redis83.conf
sed "s/6384/6484/g" redis84.conf > ../redis-docker/redis84.conf

前台新建终端执行docker容器

docker exec -it redis1 bash
docker exec -it redis2 bash
docker exec -it redis3 bash
docker exec -it redis4 bash
docker exec -it redis5 bash
docker exec -it redis6 bash

redis配置目录

cd /usr/local/etc/redis

redis服务启动

redis-server /usr/local/etc/redis/redis79.conf
redis-server /usr/local/etc/redis/redis80.conf
redis-server /usr/local/etc/redis/redis81.conf
redis-server /usr/local/etc/redis/redis82.conf
redis-server /usr/local/etc/redis/redis83.conf
redis-server /usr/local/etc/redis/redis84.conf

redis开启集群

redis集群 Waiting for the cluster to join 一直等待

集群需要开启集群总线端口

集群总线端口为redis客户端连接的端口 + 10000

redis-cli --cluster create  47.106.175.175:6479  47.106.175.175:6480 47.106.175.175:6481 47.106.175.175:6482 47.106.175.175:6483 47.106.175.175:6484 --cluster-replicas 1 -a 密码

springboot配置yml

  redis:
    host: 47.106.175.175
    database: 0
    port: 6479
    password: 密码
    timeout: 1000ms
    cluster:
      nodes:
      - 47.106.175.175:6479
      - 47.106.175.175:6480
      - 47.106.175.175:6481
      - 47.106.175.175:6482
      - 47.106.175.175:6483
      - 47.106.175.175:6484
      max-redirects: 6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值