目 录CONTENT

文章目录

redis从入门到精通

gsh456
2024-06-20 / 0 评论 / 0 点赞 / 32 阅读 / 0 字

1、安装启动

1.1安装

# 安装gcc环境 如果没有安装gcc会出现错误(此时需要删除文件夹重新解压)
yum install gcc-c++
# 将redis-5.0.9.tar.gz下载到/home,该版本为最后一个 5.0
cd /home
wget https://download.redis.io/releases/redis-5.0.9.tar.gz
 tar xzf redis-5.0.9.tar.gz
 cd redis-5.0.9
 # 编译
 make
 # 安装到/usr/local/redis目录下
 make PREFIX=/usr/local/redis install
 # 拷贝配置文件
 cp redis.conf /usr/local/redis/
 #进入安装思路
 cd /usr/local/redis/
​
​

1.2修改配置文件

 #调整配置文件
 vim /usr/local/redis/redis.conf
 #修改配置文件,后台启动改为yes
 #daemonize yes
 #开启外网访问 ,注释这个内容
 #bind 127.0.0.1
 #密码 
 # requirepass foobared

1.3 启动

#启动命令
./bin/redis-server ./redis.conf
# 启动后查看redis默认端口号为6379
ps -ef | grep -i redis
# 关闭redis
./bin/redis-cli shutdown
#测试 ./bin/redis-cli -h 192.168.182.137 -p 6379 ,-h默认为本地,可以不写
./bin/redis-cli -p 6379
ping 

截图

2.Key操作

redis默认有16个数据库

可以使用

2.2.1 select

切换数据库

#切换数据库
127.0.0.1:6379> select 1

2.2.2 set

设置值

#设置一个key
127.0.0.1:6379> set k1 v1
127.0.0.1:6379> del key

2.2.3 EXISTS

判断key是否存在

127.0.0.1:6379> EXISTS k1

keys

#查看是否有 k1,并返回
127.0.0.1:6379> keys k1
#查看所有key
127.0.0.1:6379> keys * 

move expire ttl type flushdb

#把 k1从当前数据库,移动到 数据库2
127.0.0.1:6379>move k1 1
127.0.0.1:6379> expire k1 10  #设置k1的有效期为10s
127.0.0.1:6379> ttl k1 #查看k1的剩余有效期
127.0.0.1:6379> type k1 #查看类型,这个返回是 string
127.0.0.1:6379> flushdb #清空当前的数据库
127.0.0.1:6379> flushall #清空所有数据库

2.五大数据类型

1 String字符串操作

1append

127.0.0.1:6379> APPEND k1 " hello" #字符串追加   返回字符串的长度,如果key不存在则创建

2 strlen

127.0.0.1:6379> strlen k1  #获取字符串的长度

3 incr

# 设置 views为浏览量,使用redis的 i++方法
127.0.0.1:6379>  set views 0
127.0.0.1:6379> incr views  #把 views的值进行+1,调用一次加一次,不存在则新建,默认是在0的基础上+1

4 decr

127.0.0.1:6379> decr views #把 views的值进行-1,调用一次减一次,不存在则新建,默认是在0的基础上-1

5 getRange

##字符串范围
127.0.0.1:6379> set k1 gsh456
OK
127.0.0.1:6379> GETRANGE k1 0 -1  #获取k1的全部内容
"gsh456"
127.0.0.1:6379> GETRANGE k1 0 2 #获取k1的指定区间内容
"gsh"

6 setRange

127.0.0.1:6379> set k2 abcdefghi
OK
127.0.0.1:6379> get k2
"abcdefghi"
127.0.0.1:6379> SETRANGE k2 3 gsh  #从第4个元素进行 替换字符串
(integer) 9
127.0.0.1:6379> get k2
"abcgshghi"

7 setex

setex(set with expire) #设置一个值(带过期时间)

127.0.0.1:6379> setex k3 60 value3  #设置一个值(带过期时间)
OK

8 ttl

查看有效期

127.0.0.1:6379> ttl k3
(integer) 55

9 setnx

setnx (set if not exist) #不存在的时候就设置。

127.0.0.1:6379> setnx mykey test_setnx #如果不存在则新建,这里返回值是1,代表成功
(integer) 1
127.0.0.1:6379> get mykey 
"test_setnx"
127.0.0.1:6379> setnx k2 test  #如果不存在则新建,这里返回值是0,代表失败
(integer) 0

10mset&mget

批量设置

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3    #批量设置
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k3  #批量获取
1) "v1"
2) "v3"
# 这里的事务是原子性,一起成功一起失败
127.0.0.1:6379> msetnx k1 v1 k4 v4  #批量设置(判断是否已经存在),这里返回值为0,代表失败,因为k1已经有值
(integer) 0

11 key的技巧

#下面是一个user对象,有两个属性,name和age 这里分别设置他们的值
127.0.0.1:6379> mset user:1:name gsh user:1:age 10  #批量设置
OK
127.0.0.1:6379> get user:1:age #获取用户年龄信息,方便的使用 incr等方法 进行运算
"10"

12 GetSet

先获取,在设置新值

########################GetSet##########################
#先获取再设置,如果存在则先获取 null 设置新的值。
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> getset k1 new1  #先获取k1的值,再重新设置k1的值
"v1"
127.0.0.1:6379> get k1
"new1"

2 List

redis的list是先进后出,后进先出。

所有的list的相关命令都是以 L开头

1. lpush

添加列表创建或新增元素

127.0.0.1:6379> keys *
(empty list or set)
# lpush 将一个值或者多个值,插入到列表的头部
127.0.0.1:6379> lpush list1 one  #向list1里添加 one 元素,
(integer) 1
127.0.0.1:6379> lpush list1 two #向list1里添加 two 元素
(integer) 2
127.0.0.1:6379> lpush list1 three #向list1里添加 three 元素
(integer) 3

2LRANGE

查看列表

127.0.0.1:6379> LRANGE list1 0 -1  #查看list1所有 元素,先进后出。
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[1]> LRANGE list1 0 1  #获取具体区间的值,先进先出
1) "three"
2) "two"

3 rpush

从尾部添加元素

# rpush 将一个值或者多个值,插入到列表的尾部
127.0.0.1:6379> rpush list1 zero 
(integer) 4
127.0.0.1:6379> LRANGE list1 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"

4 LPOP&RPOP

从左边删除、从右边删除

#LPOP 从左边移除
#RPOP 从右边移除
127.0.0.1:6379> LRANGE list1 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
127.0.0.1:6379> lpop list1 #移除列表的第一个元素
"three"
127.0.0.1:6379> LRANGE list1 0 -1  
1) "two"
2) "one"
3) "zero"
127.0.0.1:6379> rpop list1  #移除列表的最后一个元素
"zero"
127.0.0.1:6379> LRANGE list1 0 -1  
1) "two"
2) "one"

5 lindex

获取指定下标的值

127.0.0.1:6379> LINDEX list1 1  #获取指定下标的值,从0开始
"one"
127.0.0.1:6379> LINDEX list1 0 
"two"

6 llen

获取列表长度

127.0.0.1:6379> llen list1  #获取列表的长度
(integer) 2

7 lrem

删除指定元素

# lrem # 移除一个值
127.0.0.1:6379> lpush list1 two
(integer) 3
127.0.0.1:6379> LRANGE list1 0 -1
1) "two"
2) "two"
3) "one"
###新增部分值
127.0.0.1:6379> LRANGE list1 0 -1
1) "two"
2) "3"
3) "two"
4) "two"
5) "one"
127.0.0.1:6379> LREM list1 1 two  #移除 一个'two'元素 ,从上到下
(integer) 1
127.0.0.1:6379> LRANGE list1 0 -1
1) "3"
2) "two"
3) "two"
4) "one"
127.0.0.1:6379> LREM list1 2 two  #移除 两个'two'元素 
(integer) 2
127.0.0.1:6379> LRANGE list1 0 -1
1) "3"
2) "one"

8 ltrim

截取

######trim
127.0.0.1:6379> lpush list1 one two three four  #数据初始化
127.0.0.1:6379> LRANGE list1 0 -1  
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> ltrim list1 1 2  #通过下表获取指定的范围
OK
127.0.0.1:6379> LRANGE list1 0 -1
1) "three"
2) "two"

9 rpoplpush

移除列表的最后一个元素,并将它移动到新的列表中

#rpoplpush #
127.0.0.1:6379> lpush list1 one two three four
(integer) 4
127.0.0.1:6379> LRANGE list1 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> RPOPLPUSH list1 list2 #移动 list1的最后一个元素到list2中
"one"
127.0.0.1:6379> LRANGE list1 0 -1  #老的列表
1) "four"
2) "three"
3) "two"
127.0.0.1:6379> LRANGE list2 0 -1  #新的列表
1) "one"

10 exists

判断列表是否存在

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list1 one two three four
(integer) 4
127.0.0.1:6379> LRANGE list1 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> exists list1
(integer) 1
127.0.0.1:6379> exists list2
(integer) 0

11 lset

重新设置列表中指定下标的值

127.0.0.1:6379> lset list1 0 four4   #
OK
127.0.0.1:6379> LRANGE list1 0 -1
1) "four4"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lset list2 0 four4  #如果 list2 不存在则报错
(error) ERR no such key

12 LINSERT

在列表中,指定元素的前面添加一个元素

127.0.0.1:6379> LINSERT list1 before two 2.5  #在列表中,指定元素的前面添加一个元素
(integer) 5
127.0.0.1:6379> LRANGE list1 0 -1  
1) "four4"
2) "three"
3) "2.5"
4) "two"
5) "one"
127.0.0.1:6379> LINSERT list1 after four4 3.5  #在列表中,指定元素的后面添加一个元素
(integer) 6
127.0.0.1:6379> LRANGE list1 0 -1  
1) "four4"
2) "3.5"
3) "three"
4) "2.5"
5) "two"
6) "one"

小结

  • 它的实质是一个链表,before Node after ,left,right 都可以插入值

  • 如果key不存在,创建新的链表。

  • 如果key存在,新增链表中的元素。

  • 如果一处了所有的值,空链表,也代表不存在。

  • 在两个插入或者改动值,效率是最高的。

可以做消息队列,队列:Lpush Rpop ,栈:Lpush Lpop

3 Set(集合)

特点:

  • 不可重复

  • 乱序

set值不能重复,命令的开头都是 S

sadd

127.0.0.1:6379> sadd set1 v1  #向集合中添加一个值
(integer) 1
127.0.0.1:6379> sadd set1 v1 # 不可重复
(integer) 0
127.0.0.1:6379> sadd set1 v2 v3 v4 #向集合中添加多个值
(integer) 3
#SMEMBERS 查看成员
127.0.0.1:6379> SMEMBERS set1 #查看集合的所有值
1) "v3"
2) "v2"
3) "v1"
4) "v4"
127.0.0.1:6379> SISMEMBER set1 v5 #查看集合中,是否存在某个元素,不存在返回0
(integer) 0
127.0.0.1:6379> SISMEMBER set1 v1 #查看集合中,是否存在某个元素,存在返回1
(integer) 1
127.0.0.1:6379> scard set1  #获取元素个数
(integer) 4
127.0.0.1:6379> srem set1 v1  #移除指定元素
(integer) 1
127.0.0.1:6379> scard set1
(integer) 3
127.0.0.1:6379> SMEMBERS set1
1) "v2"
2) "v4"
3) "v3"
127.0.0.1:6379> SRANDMEMBER set1 #随机获取,一个元素
"v3"
127.0.0.1:6379> SRANDMEMBER set1 #随机获取,一个元素
"v4"
127.0.0.1:6379> SRANDMEMBER set1 2 #随机获取,指定个数元素
1) "v3"
2) "v1"
127.0.0.1:6379> SRANDMEMBER set1 2 #随机获取,指定个数元素
1) "v1"
2) "v4"
127.0.0.1:6379> spop set1  #随机删除,后面可以跟个数  spop set1 2
"v2"
########################元素移动###################
127.0.0.1:6379> SMEMBERS set1
1) "v3"
2) "v2"
3) "v1"
4) "v4"
127.0.0.1:6379> smove set1 set2 v2 #移动set1中的指定元素到set2,如果set2不存在则新建
(integer) 1
127.0.0.1:6379> SMEMBERS set2
1) "v2"
127.0.0.1:6379> SMEMBERS set1
1) "v3"
2) "v1"
3) "v4"
#####################共同关注#############
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set1 v1 v2 v3  #新增一个集合
(integer) 3
127.0.0.1:6379> sadd set2  v3 v4 v5 #新增一个集合
(integer) 3
127.0.0.1:6379> SDIFF set1 set2 #求 set1和set2的不同元素(差集)
1) "v1"
2) "v2"
127.0.0.1:6379> SDIFF set2 set1 #求 set2和set1的不同元素(差集)
1) "v5"
2) "v4"
127.0.0.1:6379> Sinter set2 set1 #求 set1和set2的共通(交集)
1) "v3"
127.0.0.1:6379> Sunion set2 set1 #求 set1和set2的所有元素(并集)
1) "v3"
2) "v5"
3) "v2"
4) "v4"
5) "v1"

4 Hash(Map集合)

key-Map<key,value> 值是map集合

命令以 H开头

127.0.0.1:6379> hset map1 name gsh456  #设置一个值
(integer) 1
127.0.0.1:6379> HGET map1 name  #获取一个值
"gsh456"
#批量设置  hmset key field1 value1 field2 value2 ...
127.0.0.1:6379> hmset map1 age 20 height 185cm wight 75kg 
OK
#批量获取 hmget key field1 field2 ...
127.0.0.1:6379> hmget map1 name age 
1) "gsh456"
2) "20"
127.0.0.1:6379> HGETALL map1  #获取集合的全部属性
1) "name"
2) "gsh456"
3) "age"
4) "20"
5) "height"
6) "185cm"
7) "wight"
8) "75kg"
127.0.0.1:6379> hdel map1 age   #删除集合中的指定 字段
(integer) 1
127.0.0.1:6379> HGETALL map1
1) "name"
2) "gsh456"
3) "height"
4) "185"
5) "wight"
6) "75kg"
127.0.0.1:6379> HLEN map1 #获取集合多少个键值对。
(integer) 3
127.0.0.1:6379> HEXISTS map1 name #判断集合中的字段是否存在,1存在 
(integer) 1
127.0.0.1:6379> HEXISTS map1 age #判断集合中的字段是否存在,1不存在 
(integer) 0
127.0.0.1:6379> hkeys map1 #获取集合的所有key
1) "name"
2) "height"
3) "wight"
127.0.0.1:6379> hvalues map1 
(error) ERR unknown command `hvalues`, with args beginning with: `map1`, 
127.0.0.1:6379> hvals map1 #获取集合的所有值
1) "gsh456"
2) "185"
3) "75kg"

127.0.0.1:6379> HINCRBY map1 height 1  #集合中指定字段 +1 
(integer) 186
127.0.0.1:6379> hvals map1
1) "gsh456"
2) "186"
3) "75kg"
127.0.0.1:6379> hvals map1
1) "gsh456"
2) "186"
3) "75kg"
127.0.0.1:6379> HINCRBY map1 height -2 #集合中指定字段 -2
(integer) 184
127.0.0.1:6379> hvals map1
1) "gsh456"
2) "184"
3) "75kg"
127.0.0.1:6379> HSETNX map1 age 20 #集合不存在则新增,1代表成功
(integer) 1
127.0.0.1:6379> HSETNX map1 age 21 #集合不存在则新增,0代表失败
(integer) 0

本质和string类型没有太大区别,还是一个简单的集合。

hash变更数据,user name age 和string 差不多。用于存储进行变动的数据。更适合对象的存储。

5Zset(有序集合)

在set基础上,增加一个值。

#集合增加一个或者多个元素,注意元素前面加数字
127.0.0.1:6379> zadd salary 3000 xiaoming 2500 xiaohong 4000 xiaoli  #三个人和他们的工资
(integer) 3
127.0.0.1:6379> ZRANGE salary 0 -1  #查看全部
1) "xiaohong"
2) "xiaoming"
3) "xiaoli"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf #排序,-负无穷,和+无穷
1) "xiaohong"
2) "xiaoming"
3) "xiaoli"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores  #排序,-负无穷,和+无穷 带成绩
1) "xiaohong"
2) "2500"
3) "xiaoming"
4) "3000"
5) "xiaoli"
6) "4000"
127.0.0.1:6379> zrem salary xiaohong  #移除元素
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "xiaoming"
2) "xiaoli"
127.0.0.1:6379> zcard salary #集合元素个数
(integer) 2
127.0.0.1:6379> ZREVRANGE salary 0 -1 #倒叙排列
1) "xiaoli"
2) "xiaoming"
127.0.0.1:6379> ZCOUNT salary 2000 4000 #获取指定区间的成员数量
(integer) 2

案例思路

set排序,存储班级层级,工资表

普通消息:1,重要消息:2。带权重的判断

排行榜应用实现。

三大特殊类型

6. geospatial 位置

城市经纬度

# geoadd 添加地理位址

将指定的地理空间项目(纬度,经度,名称)添加到指定的键。数据作为一个排序集存储在密钥中,以便以后可以使用带有 GEORADIUS 或 GEORADIUSBYMEMBER 命令的半径查询来检索项目。

该命令采用标准格式 x,y 的参数,因此经度必须在纬度之前指定。可以索引的坐标是有限的:非常靠近极点的区域不可索引。EPSG:900913 / EPSG:3785 / OSGEO:41001的具体限制如下:

  • 有效经度从-180到180度。

  • 有效纬度从 -85.05112878 到 85.05112878 度。

当用户尝试索引超出指定范围的坐标时,该命令将报告错误。

注意:没有 GEODEL 命令,因为您可以使用 ZREM 来删除元素。地理索引结构只是一个有序集合。

6.1 geoadd-添加位置节点

127.0.0.1:6379> geoadd key longitude latitude member [longitude latitude member ...127.0.0.1:6379> geoadd key longitude latitude member [longitude latitude member ...127.0.0.1:6379> geoadd key longitude latitude member [longitude latitude member ...127.0.0.1:6379> geoadd 
(error) ERR wrong number of arguments for 'geoadd' command
127.0.0.1:6379> geoadd china:city 116.408 39.904 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.445 31.213 shanghai 114.109 22.544 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 113.641 34.758 zhengzhou 108.969 34.285 xian
(integer) 2
127.0.0.1:6379> geoadd china:city 113.265 23.108 guangzhou 120.165 30.319 hangzhou
(integer) 2
127.0.0.1:6379> geoadd china:city 106.549 29.581 chongqing 121.576 38.944 dalian
(integer) 2
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.40800267457962036"
   2) "39.90399988166036138"
127.0.0.1:6379> geodist china:city beijing zhengzhou 
"622398.3332"
127.0.0.1:6379> geodist china:city beijing zhengzhou km
"622.3983"
127.0.0.1:6379> geodist china:city beijing shanghai km
"1068.2320"

6.2 georadius-以指定坐标搜索位置

GEORADIUS:以给定的坐标为中心,寻找指定范围的位置列表,通过半径查找。

附近的人?附近的餐馆?

#获取坐标为 110 30 1000公里范围内的位置信息
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km 
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "guangzhou"
5) "hangzhou"
6) "zhengzhou"
#获取坐标为 110 30 500公里范围内的位置信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
1) "chongqing"
2) "xian"
#获取坐标为 110 30 1000公里范围内的位置信息 并显示位置
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist
1) 1) "chongqing"
   2) "336.3467"
2) 1) "xian"
   2) "486.3850"
   #获取坐标为 110 30 500公里范围内的位置 并显示经纬度
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord
1) 1) "chongqing"
   2) 1) "106.54900163412094116"
      2) "29.58100070345364685"
2) 1) "xian"
   2) 1) "108.96899789571762085"
      2) "34.28499959898385896"
#获取坐标为 110 30 500公里的一个地址信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist count 1
1) 1) "chongqing"
   2) "336.3467"
#获取坐标为 110 30 500公里的2个地址信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist count 2
1) 1) "chongqing"
   2) "336.3467"
2) 1) "xian"
   2) "486.3850"
 #获取坐标为 110 30 500公里的3个地址信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist count 3
1) 1) "chongqing"
   2) "336.3467"
2) 1) "xian"
   2) "486.3850"

6.3 georadiusbymember

GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

以 一个元素的位置为中心,方圆距离内的距离信息

127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "zhengzhou"
2) "beijing"
3) "dalian"
4) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 500 km
1) "beijing"
2) "dalian"

6.4 geohash--获取位置信息的hash值

GEOHASH key member [member ...]

获取元素的hash性质的坐标,从二维编程一维,hash值越像近说明,两个地点离得越近。

127.0.0.1:6379> GEOHASH china:city beijing zhengzhou
1) "wx4g0bm9xh0"
2) "ww0v9zwb500"

6.5 geo的删除和其他操作 zset

geo的底层实现原理是zset,我们可以使用zset命令来操作 geo元素

127.0.0.1:6379> ZRANGE china:city 0 -1  #获取城市列表
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "guangzhou"
5) "hangzhou"
6) "shanghai"
7) "zhengzhou"
8) "beijing"
9) "dalian"
127.0.0.1:6379> zrem china:city dalian #删除一个元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1 
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "guangzhou"
5) "hangzhou"
6) "shanghai"
7) "zhengzhou"
8) "beijing"

2. hyperloglog

什么是基数?不重复元素的个数。

a{1,3,5,7,8}

b{1,3,5,7,8,8}

基数=5,可以接受误差。

redis的hyperloglog的有点

优点。占用的内存是固定的,例如存储 2^64的set,只需要12KB内存,如果从内存角度来比较的话hyperloglog是首选!

网页UV(一个人的访问一个网站,多次但是还是算作一次)

传统的解决方案是,例如set集合,保存用户的id,以set集合中的元素数量为标准判断。

这个方式如果保存大量的用户id,会比较麻烦,我们的目的是计数,而不是保存用户id。

但是官方说有0.81%的错误率。但是有些场景不用。

127.0.0.1:6379> pfadd h1 a b c d e f g h i  # 新增、添加元素
(integer) 1
127.0.0.1:6379> PFCOUNT h1  #查看数量
(integer) 9
127.0.0.1:6379> pfadd h1 a b s  #添加新元素
(integer) 1
127.0.0.1:6379> PFCOUNT h1 # 查看数量
(integer) 10
127.0.0.1:6379> pfadd h2 a b c 1 2 3  # 新增一个h2
(integer) 1
127.0.0.1:6379> pfcount h2  
(integer) 6
127.0.0.1:6379> PFMERGE h3 h1 h2  # h1和h2合并为h3
OK
127.0.0.1:6379> PFCOUNT h3
(integer) 13

3. bitMap

位存储,两种数据状态的,非0即1。省空间

统计疫情感染人数:0 1 0 1 0,没有感染用0,感染用1

统计在线和离线,

使用bitmap来记录一周的打卡

1 setbit 添加

127.0.0.1:6379> SETBIT user:1 1 1  #用户1 周1 打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 2 0 #用户1 周2 未打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 3 0 #用户1 周3 未打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 4 1 #用户1 周4 打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 5 1 #用户1 周5 打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 6 0 #用户1 周6 未打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 7 0 #用户1 周7 未打卡
(integer) 0

2 getbit 数据获取

查看某一天是否打卡

127.0.0.1:6379> getbit user:1 2
(integer) 0
127.0.0.1:6379> getbit user:1 1
(integer) 1

3 bitcount 统计

统计打卡天数

127.0.0.1:6379> BITCOUNT user:1 
(integer) 3

事务

mysql:acid,

原子性:要么全部成功,要么全部失败。

redis的单条命令是保证原子性的,但是redis的事务不是原子性的。

redis事务的本质:一组命令的集合。一个事务中的所有命令都会被序列化,按照顺序一次性执行完毕,排他性。

队列开始--序列化--set set ---执行---

redis 单挑命令,但是事务不保证原子性。

锁:redis可以实现乐观锁

redis的事务:

  • 开启事务(multi)

  • 命令入队(....)

  • 执行事务(exec)

正常执行

127.0.0.1:6379> MULTI  #开启事务
OK
127.0.0.1:6379> set k1 v1 #命令入队
QUEUED
127.0.0.1:6379> set k2 v2 #命令入队
QUEUED
127.0.0.1:6379> get k1 #命令入队
QUEUED
127.0.0.1:6379> set k3 v3 #命令入队
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v1"
4) OK

放弃事务

127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> get k4
QUEUED
127.0.0.1:6379> DISCARD #取消事务
OK
127.0.0.1:6379> get k4
(nil)

编译时异常:代码有问题,编译器发现。事务中的所有命令都不会被执行。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> getset k5  #编译异常
(error) ERR wrong number of arguments for 'getset' command 
127.0.0.1:6379> set k6 v6
QUEUED
127.0.0.1:6379> exec  
(error) EXECABORT Transaction discarded because of previous errors. #事务失败
127.0.0.1:6379> get k6
(nil)

运行时异常(一除以零),逻辑有问题。那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set k1 "v1"  #设置k1的值为v1
127.0.0.1:6379> MULTI  #开启事务
OK
127.0.0.1:6379> incr k1 #对v1进行+1,由于v1是字符串,肯定报错
QUEUED
127.0.0.1:6379> set k2 v2 #设置其他值
QUEUED
127.0.0.1:6379> set k3 v3 #设置其他值
QUEUED
127.0.0.1:6379> get k3 #获取k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k3  #很明显执行成功了
"v3"
127.0.0.1:6379> get k1
"v1"

watch--监控

悲观锁

  • 认为什么时候都可能出问题,无论做什么都加锁!

乐观锁

  • 认为认为什么时候都不一样出问题,所以不会枷锁。在更新数据的时候,判断一下,在此期间是否有人修改过这个数据。mysql表中加一个version进行监控。

  • 获取version

  • 更新的时候比较version

redis的监控测试

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money  #简史money对象
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec  #事务正常结束,事务期间数据未发生任何变化,执行成功!
1) (integer) 80
2) (integer) 20
##############################多线程
127.0.0.1:6379> watch money  #使用watch 进行乐观锁操作
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10   #在未提交事务之前,另外一个线程2执行 
QUEUED
127.0.0.1:6379> exec
(nil)  # 事务提交失败

线程2

127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000.
OK

重新获取锁

127.0.0.1:6379> UNWATCH  #,如果发现事务执行失败,先解除乐观锁
OK
127.0.0.1:6379> watch money #获取最新的值,再次加乐观锁
OK
127.0.0.1:6379> MULTI 
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby money 5
QUEUED
127.0.0.1:6379> exec   #如果加锁的key 没有发生变化,执行成功
1) (integer) 990
2) (integer) 995

乐观锁一般用于秒杀!

Jedis

redis for java,用java操作redis

什么是jedis,是redis官方推荐的java连接redis的工具。使用java操作redis的中间件。

  1. 导入对应的依赖

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>
  1. 编码测试

  • 连接redis

    Jedis j = new Jedis(“host”,"");
    j.ping(); //pong
    //所有的执行命令。

    事务控制

     public static void main(String[] args) {
            //1. new一个 jedis对象
            Jedis jedis = new Jedis("127.0.0.1",6379);
            jedis.flushDB();
            JSONObject jsonObject =new JSONObject();
            jsonObject.put("hello","world");
            jsonObject.put("name","gsh456");
            Transaction multi = jedis.multi();
            try {
                multi.set("user1",jsonObject.toJSONString());
                //代码运行时异常,执行失败
                int i = 1/0;
                multi.set("user2",jsonObject.toJSONString());
                //执行事务
                multi.exec();
            }catch (Exception e){
                //放弃事务
                multi.discard();
            }finally {
                System.out.println(jedis.get("user1")); // null
                System.out.println(jedis.get("user2")); // null
                //两个获取全部为空,代表事务全部执行失败
                jedis.close();
            }
        }

    springBoot整合

    springBoot 操作数据:spring-data-jpa/spring-data-jdbc/spring-data-mongodb

    springData也整合了redis。

    1. 导入

    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-data-redis</artifactId>
    		</dependency>

    spring 2. 0以后把原来的jedis改为了lettuce!

    jedis:采用直连的方式,多线程操作的话是不安全的,如果避免这个问题,需要使用jedis-pool解决。更像BIO

    模式。

    lettuce:底层采用netty,速度快,实例可以在多个线程共享,不存在线程不安全,可以有效减少线程数量,更像NIO模式。

@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")//自己定义 
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
        //默认的RedisTemplate 没有过多的设置,可以自己@bean 进行自定义,redis对象需要序列化
        //两个对象都是object,一般key是string,每次强转太麻烦
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean  //由于string类型最常使用的
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

配置文件

注意如果要配置连接池 spring.redis.lettuce.pool,jedis的配置没有实现,不生效。

spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.lettuce.pool.max-active=8  #需要写其他配置

测试代码

@Test
	void contextLoads() {
		//操作不通用的数据类型,redisTemplate.opsForGeo()
		//opsForValue 操作string
		//操作 opsForList list
		//opsForSet
		//opsForGeo
		//处理基本的操作,常用的方法,都可以直接 redisTemplate进行操作。比例事务和基本的增删改查
//		redisTemplate.multi();
//		RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//		connection.flushDb();
		final String key1 = "key1";
		final String key2 = "key2";
		redisTemplate.opsForValue().set(key1,"test1");
		redisTemplate.opsForValue().set(key2,"这个是中文测试");
		System.out.println(redisTemplate.opsForValue().get(key1));
		System.out.println(redisTemplate.opsForValue().get(key2));
	}

如果通过redis-cli获取key2会出现乱码!

127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04key2"
2) "\xac\xed\x00\x05t\x00\x04key1"

默认的序列化工具

序列化测试

@Test
	public void test() throws JsonProcessingException {
		final String key1 = "user1";
		final String key2 = "user2";
		//先清空数据库
		redisTemplate.getConnectionFactory().getConnection().flushDb();
		//一般的开发,都是用json保存对象
		User gsh = new User("gsh", 3);
		String jsonUser = new ObjectMapper().writeValueAsString(gsh);
		redisTemplate.opsForValue().set(key1,jsonUser);
		redisTemplate.opsForValue().set(key2,gsh);
		System.out.println("redisTemplate.opsForValue().get(key) = " + redisTemplate.opsForValue().get(key1));
		//报错 Failed to serialize object using DefaultSerializer ,user未实现 序列化接口,如果实现了 Serializable则不会报错
		System.out.println("redisTemplate.opsForValue().get(key) = " + redisTemplate.opsForValue().get(key2));
	}

自己配置序列化

redis配置文件详解

启动的时候就是通过配置文件进行启动的

单位,常用单位如下,并且对大小写不敏感

#常用单位
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
# 不区分大小写
# units are case insensitive so 1GB 1Gb 1gB are all the same.

包含其他配置文件,和nginx有点像

# 包含其他配置文件
# include /path/to/local.conf
# include /path/to/other.conf

NETWORK

# Examples: 绑定ip
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1

保护模式

#默认开启,如果注释掉 bind的话,需要开启保护模式的话,需要配置密码
protected-mode yes  

端口

#一般在配置集群的时候用到
port 6379  

通用配置

#开启守护进程
daemonize yes
#配置文件的pid文件,如果daemonize=yes,需要这个文件
pidfile /var/run/redis_6379.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 "" #日志位置,全路径
databases 16 #数据库个数 
always-show-logo yes #是否总是显示LOgo

SNAPSHOTTING

save,持久化,在执行多少次命令之后会进行持久化到文件  .rdb  .aof两种持久化文件
#如果 900秒内,最少有一个key修改过数据,那么就就行持久化操作
save 900 1
#如果 300秒内,最少有10个key修改过数据,那么就就行持久化操作
save 300 10
#如果 60秒内,最少有10000个key修改过数据,那么就就行持久化操作
save 60 10000

#持久化错误之后是否继续工作
stop-writes-on-bgsave-error yes  
#是否压缩 rdb文件,注意该操作消耗cpu的部分资源
rdbcompression yes
#保存rdb文件的时候 是否校验 rdb文件
rdbchecksum yes
#持久化文件(rdb)路径,默认是当前目录
dir ./

REPLICATION:复制,主从复制

SECURITY:安全

# requirepass foobared   #连接密码
127.0.0.1:6379> config get requirepass   #获取配置文件内容
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass gsh456  #命令设置密码,这样的好处是 不用重启redis
OK
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth gsh456
OK
127.0.0.1:6379> ping
PONG

CLIENTS:客户端

# maxclients 10000  #最大连接数量

MEMORY MANAGEMENT :内存管理

# maxmemory <bytes>  #最大内存占用
# maxmemory-policy noeviction  #内存满的时候的处理策略

maxmemory-policy 六种方式
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值) 
2、allkeys-lru : 删除lru算法的key   
3、volatile-random:随机删除即将过期key   
4、allkeys-random:随机删除   
5、volatile-ttl : 删除即将过期的   
6、noeviction : 永不过期,返回错误

APPEND ONLY MODE :AOF设置

#默认不开启 aof,默认的使用的是rdb,大部分情况下 rdb够用
appendonly no

#持久化文件的名字
appendfilename "appendonly.aof"
# 持久化策略,默认每秒同步一次,可能丢失一秒的数据
# appendfsync always #比较消耗性能
appendfsync everysec
# appendfsync no 

redis的持久化

redis的持久化分为两种,rdb和aof,默认采用rdb,aof关闭。

1 rdb

rdb保存的文件一般是 dump.rdb,

触发机制,save到指定条件,会触发rdb 规则,如果flushdb 和 shutdown 主动关闭,都会产生rdb文件,

dump.rdb文件,只要在redis的安装目录,redis将自动加载,rdb的文件位置在配置文件 dir ./ 配置中,

优点:

  • 适合大规模的数据恢复。

缺点:

  • redis意外宕机,可能导致最后的数据丢失

  • fork子进程(保存进程),可能占用更多的系统资源。

在生产环境,注意需要将 dump.rdb进行备份。

2 aof

APPEND ONLY MODE (append to file) 文件追加

本质上是吧所有命令都记录下来(只记录写,不记录读命令),恢复的时候就把所有命令在重新执行一次,如果是大数据恢复起来效率不高。

aop文件名字:appendonly.aof

该配置 默认不开启,改为 appendonly yes即可。其他配置尽量不要动。

两个是同时开启的。

appendonly no
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"  #文件位置
# appendfsync always 
appendfsync everysec  #每秒同步一次
# appendfsync no
no-appendfsync-on-rewrite no   #延迟重写机制

auto-aof-rewrite-percentage 100  #
auto-aof-rewrite-min-size 64mb  #aof文件 达到这个大小后,重写分割一个行文件

aop修复工具

aop有检查aop文件是否有错误并且自动修复策略,如果aof文件不正常,客户端将无法启动。

使用安装目录中/bin的 redis-check-aof进行修复

#修复命令
./bin/redis-check-aof --fix appendonly.aof  

优点

  • 如果每次修改都同步,文件的完整性更好

  • 如果每秒同步一次,可能丢失1s的数据

  • 从不同步,效率最高

缺点

  • 对于数据文件来说,aof文件更大,文件恢复的数据比rdb慢很多。

  • aop的io操作,读写效率比rdb要低。

发布订阅

订阅端

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> SUBSCRIBE gsh456
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "gsh456"
3) (integer) 1
#等待发布者发布信息
1) "message"
2) "gsh456"
3) "test1"
1) "message"
2) "gsh456"
3) "test2"

发布者

127.0.0.1:6379> PUBLISH gsh456 test1  #发布信息
(integer) 1
127.0.0.1:6379> PUBLISH gsh456 test2
(integer) 1

代码使用

主从复制

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

默认情况下,每台redis服务器都是主节点;且一个主节点可以和多个从节点(或者没有从节点),但是一个从节点只有一个主节点。

主从复制的主要特点

  1. 数据冗余:主从复制实现了热备份,是持久化之外的一种数据冗余方式

  2. 数据故障:当主节点出了问题,可以由从节点提供服务,实现快速故障恢复;实际上是一种服务冗余。

  3. 副总均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,从节点提供读服务(即写redis数据的时候连接主节点,读redis数据的时候连接从节点)。分担服务器压力,尤其在写少读多的场景下,可以使用多从节点进行负载,可以大大提高读取效率和redis的并发量

  4. 高可用基石:除了上述作用外,主从复制还是哨兵模式能够实施的基础,也是redis高可用的基础。

一般来说,要将redis运用于项目中,只有一台redis服务器是万万不能的,原因如下:

  1. 从勾结上,单台redis服务发生单点故障,并且一台服务器处理所有redis请求的负载,压力很大。

  2. 从容量上,单台redis服务器的内存有限,一般来说redis最大使用内存在20G左右。

  3. 一般项目都是写少读多,主从模式可以大大提高缓存效率。

最低配: 一主二从,要不然主节点宕机后,根据哨兵模式,无法从从节点选举新的主节点。

环境配置

只用配从库,不用配主库!

127.0.0.1:6379> info replication
# Replication
role:master  #当前服务的角色
connected_slaves:0  #没有从节点
master_replid:0513f36f35e2d8bcf6f7a0b5ef6b78fa30dd0917
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
#从上面的信息,可以看出来,默认自己就是主节点

主从配置

1 先停掉主服务,

2 打开从服务的端口

pidfile  #pid文件
logfile # 日志文件位置
[root@ecs-90b1 redis]# cd ../
[root@ecs-90b1 local]# cp -r redis/ redis80  #复制三个从节点
[root@ecs-90b1 local]# cp -r redis/ redis81 
[root@ecs-90b1 local]# cp -r redis/ redis82
[root@ecs-90b1 local]# ls
agentInstall.sh  games    influxdb-1.7.9.x86_64.rpm  kafka_2.12-2.2.2  libexec  nginx-1.17.10.tar.gz  redis81  share  telescope                     WWW
bin              gsh456   java                       lib               mysql    redis                 redis82  src    telescope_linux_amd64         xyjsoft
etc              include  jdk                        lib64             nginx    redis80               sbin     srs    telescope_linux_amd64.tar.gz  zookeeper-3.4.14
[root@ecs-90b1 local]# vim redis80/redis.conf   # 修改从节点的 端口号和pid文件

客户端都准备好了

配置从节点的配置文件,让他们 认老大

[root@ecs-90b1 local]# ./redis/bin/redis-server ./redis/redis.conf  #启动 6379节点
22780:C 17 Oct 2020 11:10:40.485 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
22780:C 17 Oct 2020 11:10:40.485 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=22780, just started
22780:C 17 Oct 2020 11:10:40.485 # Configuration loaded
[root@ecs-90b1 local]# ./redis80/bin/redis-server ./redis80/redis.conf  #启动 6380节点
23307:C 17 Oct 2020 11:10:53.162 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
23307:C 17 Oct 2020 11:10:53.162 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=23307, just started
23307:C 17 Oct 2020 11:10:53.162 # Configuration loaded
[root@ecs-90b1 local]# ./redis81/bin/redis-server ./redis81/redis.conf   #启动 6381节点
23986:C 17 Oct 2020 11:11:08.928 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
23986:C 17 Oct 2020 11:11:08.928 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=23986, just started
23986:C 17 Oct 2020 11:11:08.928 # Configuration loaded
[root@ecs-90b1 local]# ./redis82/bin/redis-server ./redis82/redis.conf  #启动 6382节点
24400:C 17 Oct 2020 11:11:19.201 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
24400:C 17 Oct 2020 11:11:19.201 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=24400, just started
24400:C 17 Oct 2020 11:11:19.201 # Configuration loaded
[root@ecs-90b1 local]# ps -ef|grep redis
root     22781     1  0 11:10 ?        00:00:00 ./redis/bin/redis-server *:6379
root     23308     1  0 11:10 ?        00:00:00 ./redis80/bin/redis-server *:6380
root     23987     1  0 11:11 ?        00:00:00 ./redis81/bin/redis-server *:6381
root     24402     1  0 11:11 ?        00:00:00 ./redis82/bin/redis-server *:6382
root     24768  7355  0 11:11 pts/1    00:00:00 grep --color=auto redis
#从一主三从全部启动

认老大

[root@ecs-90b1 local]# ./redis80/bin/redis-cli -p 6380  #连接从
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379  #认老大
OK

但是上面的命令指示暂时的,建议修改配置文件的方式。注意只用修改主节点

 replicaof 127.0.0.1 6379  #主节点的地址和端口号
 # masterauth <master-password>  #主节点的密码

重新启动三个从节点

[root@ecs-90b1 local]# ./redis80/bin/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:1
master_sync_in_progress:0
slave_repl_offset:1106
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b2b12f93ead19f85b4d1bf4dc0ce35a36c798e1c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1106
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:239
repl_backlog_histlen:868
[root@ecs-90b1 local]# ./redis/bin/redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication   一主三从,可以看出已经配置好了
role:master  
connected_slaves:3
slave0:ip=127.0.0.1,port=6380,state=online,offset=1162,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=1162,lag=0
slave2:ip=127.0.0.1,port=6382,state=online,offset=1162,lag=0
master_replid:b2b12f93ead19f85b4d1bf4dc0ce35a36c798e1c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1162
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1162

可以从信息中看出,一主三从已经配置好了

主机的配置文件中已经指定,从节点不允许写,所以从会报错。

关于主从的 springboot的配置

如果 主节点挂了,从节点依旧连接到主机的,但是没有写操作了。这个时候,主机重启,一切恢复。

如果从节点挂了一个,主节点一些没问题。从节点恢复,从节点

复制原理:

从节点连接到主节点后,会发送一个sync命令,

主节点接受到名将,启动后台存盘进程,同时手机所有接收到的用于修改的数据集命令,后台执行完成之后,主节点将传送整个数据文件到从节点,并完成一次完整同步。

全量复制:而从节点服务在接受到数据文件后,将其存盘并加载到内存中去。

增量复制:主节点将新的命令集传到到从节点,完成同步

只要重新连接到主节点,全量复制将会执行。

哨兵模式

自动选老大模式

概述:

主从切换技术的方法是:当服务器宕机后,需要手动把一台服务器切换为主服务器,这个需要人工干预,费时费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。redis2.8之后开始提供sentinel(哨兵)架构来解决泽恩问题。

哨兵模式是一种特殊的模式,首先redis提供了哨兵命令,哨兵是一个独立的进程,作为进程,他会独立运行。其原理是哨兵通过发送命令,等待redis服务响应,从而监控多个运行的redis实例。如果redis主服务宕机,自动根据投票数将从节点变成主节点。

哨兵模式的流程图。

运行程序在 redis的安装目录的 bin中redis-sentinel

哨兵作为一个进程,也可能挂掉,一般配置的为双哨兵或者多哨兵,形成多哨兵模式。

1.新建哨兵的配置文件

[root@ecs-90b1 redis]# vim sentinel.conf

# sentinel monitor 任意名字 redis信息,1启用投票模式
sentinel monitor myredis 127.0.0.1 6379 1

启动哨兵

[root@ecs-90b1 redis]# ./bin/redis-sentinel ./sentinel.conf 

启动成功,

手动关闭主节点

[root@ecs-90b1 redis]# ./bin/redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:3
slave0:ip=127.0.0.1,port=6380,state=online,offset=19235,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=19235,lag=1
slave2:ip=127.0.0.1,port=6382,state=online,offset=19235,lag=1
master_replid:b2b12f93ead19f85b4d1bf4dc0ce35a36c798e1c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:19235
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:19235
127.0.0.1:6379> shutdown
not connected> exit

哨兵监视到主节点挂了,就会从从节点选举,投票最多的变成新的主节点。

如果这时再次启动主节点 -6379

如果6379主节点再次启动,那么将会变成新主节点的从节点。

优点:

  1. 拥有主从复制的优点

  2. 主从自动切换,故障转移。

  3. 哨兵模式是主从复制的升级版。

缺点

  1. redis不好在线扩容,如果集群容量一旦到达上限,在线扩容就十分麻烦。

  2. 实现哨兵模式的配置很麻烦,里面有很多选择。

哨兵模式的全部配置文件


0

评论区