redis

redis

常识:数据存储在磁盘中,磁盘的寻址是ms级,带宽为g/m级别

内存寻址为ns级别,所以磁盘比内存在寻址上慢10万倍,带宽直接走cpu

io buffer:成本问题

磁盘有磁道、扇区,1扇区为512byte,区域越小,成本变大(索引4k,页)

数据也是以4k为基本单位去存储的

因为操作系统控制无论读取多少 都是以4k为单位所以设置小了没必要

数据存储在磁盘文件中,文件越大,查询越慢.为什么?

查询硬盘时io成为瓶颈.

数据库软件中以1页为单位查询,如果没有索引还是全量io也是非常慢的.索引也是4k的存储

关系型数据库检表时必须给出schema,规定字段长度,如果某字段第一个字与第七个字填了,中间都是空,则存储时会以0填充,好处是增删改时不需要移动数据(行级数据),b+树树干读到内存中

数据库表很大,性能下降?

如果有索引,增删改慢,因为需要维护索引位置

查询速度,如果数据量很大,内存中可以存下索引树干,查询sql命中一页数据页,那么也很快

当并发,或者复杂sql时,会受硬盘带宽影响速度.

全磁盘的性能会低,全内存的太贵.于是折中,将一些数据放入内存中,就有了redis这一软件

介绍

redis是一个开源的内存数据存储系统,可以用作数据库,缓存,消息中间件,支持以下结构(key value,key为String,value有以下类型)

  • 字符串
  • 散列(hash)
  • 列表(list)
  • 集合(set)
  • 有序集合(sort set)
  • 范围查询
  • bitmap

内置了

  • 事物
  • 不同级别的磁盘持久化

并通过哨兵模式和自动分区模式来提高可用性/

redis与类似memcahed的k、v存储软件区别

计算向数据移动

memcahed的value没有类型的概念,可以用json来表示复杂的数据,

redis的value有类型的概念,哟list等.

于是在取值时,memcahed只能取全部值(io是瓶颈),然后解析取出某个值.而redis有server提供的方法比如index(),可以很方便的取到某个值不需要取出全部value值.让redis来计算到底取出哪一个值

redis原理,为什么快

redis是单进程,单线程,单实例的,那么并发请求下为什么会很快呢?

首先许多client在访问redis机器时,先到达操作系统内核(socket),redis与内核之间使用epoll(非阻塞多路复用的系统调用来遍历链接谁要数据就给谁).

每一个client链接在server端就是一个fd,redis使用epoll的机制,将这些fd通过epoll-ctl加入到selector空间内,同时关注它的读写事件.redis在linux机器中的fd有epoll、socket(服务端)

同时有网络客户端传数据给redis时,通过mmap技术传给内核,放到mmap区(存在争议,或者只是一个epfd区域,称为epoll句柄)

由于redis是单线程的,所以数据到达时是一个个处理的.为了数据一致性问题,所以使用单线程,没有锁竞争.

但是有对于一个key操作的顺序性,最好是将一个key的操作打到一条链接上

使用

redis里默认16个库,0 - 16 ,每个库是隔离的.

string类型

字符串操作,数值、bitmap

字符串操作

  • set key1 ooxx nx:这个nx代表库里没有这个key才添加.除了nx还有xx,代表只有存在时才添加
  • append key1 “world”:追加
  • strlen:查看字符串长度,1就是1个字节

正反向索引:hello 有从h开始和从o开始的索引,反向索引从o,-1开始,-1、-2、-3

在key里有登记一个type,描述value类型,key里标记了什么类型的.value就是什么类型.

比如key的value类型标记为string类型,那么set key 99,这个99也是string类型

每种value的类型都有不同的方法(用来规避异常,不需要在参加计算时才出现异常),比如int可以累加,string可以append.int的append则是追加比如9变成999追加了2个9,一个字符一个字节

数值

int型的99999在redis中也是字节,先将字节转换为数值型累加再转为字节,客户端传入数据时,根据客户端编码转为字节数组传入server端.,于是server端就按照这个编码保存

Redis Incr 命令将 key 中储存的数字值增一。可以规避并发下对数据库的事物操作.

还有查看key的encoding的,有enbstr、raw,对象字符串长度小于44字节就用embstr,否则就用raw

二进制安全

在redis进程与外界交互时,redis只给字节流不给字符流,只要对接双方有统一编码解码,数据就不会被破坏.保证输出是最原始的输入.不会对数据进行再编码.解析

bitmap

mset nx:原子性操作,当某值不存在时才设置.比如msetnx k2 c k3 d,如果k2判定失败了,那k3就不会再设置了,因为是原子性的

setbit:bit位操作相关的指令.value类型为字符串.1个字节是0000 0000,第一个字节的索引是0,第二个字节是1,它是操作二进制01的命令.比如setbit key1 9 1,就是0000 0000, 0100 0000 下标为9的0改成了1.这个位操作可以用来记录某一个用户一年内哪些天登陆了,一共用50个字节的空间就可以,一个字节8位,50x8=400,key是用户,value的400位是天

Bitpos k1 1 0 1:搜索key1 中1的出现位置(下标索引),在0和1这两个字节数组中找

127.0.0.1:6379> bitpos 20190101 0 -1
(integer) 0

bitcount k1 0 1:在k1的0和1字节数组找1出现了几次,比如计算一年最后两周用户是否登陆,bitcount sean -2 -1

127.0.0.1:6379> bitcount 20190101 0 -1
(integer) 2

bittop:做与或运算,为啥做或运算呢?

需求.

  • 统计618活跃用户

    旋转一下,bit位图,setbit 20190101 1 1、setbit 20190102 1 1、setbit 20190102 7 1,此时20190101有1号用户登陆,20190102有1、7两个用户登陆.

    key为年月日,value的位代表用户id

    127.0.0.1:6379> bitop or destkey 20190101 20190202
    (integer) 1
    127.0.0.1:6379> bitcount destkey 0 -1
    (integer) 2
    

    20190101 有一个用户id为1的人在1位处留了1

    20190102 有一个用户id为2的人在2位处留了1

    那么两个key20190101,20190102的位做一个或运算.则1、2都为1,可以统计这两天有两个用户登陆了

list

特征:栈,队列,数组,阻塞

value的类型是list,链表,无环双向链表,key上有head和tail,头指针与尾指针

有序,插入的顺序,不是元素本身的顺序

可以重复出现

  • lpush key1 a b c d e f:l代表left,从左边push进入链表,存储结构应该是fedcba,head指向最后放入的数据,tail指向最先放入的数据
  • Rpush key1 a b c d e f:代表right push,从右边放入,顺序为 a b c d e f
  • Lpop k1:对某个key弹出某个元素,左边弹(都是按照数据的存储形式弹)
  • Rpop k1:对某个key弹出某个元素,右边弹(从右边弹出,如果是rpush的那么弹出f)
  • lrange k1 0 -1:从左边开始取,取0下标到-1下标
  • Iindex k1 2;取出下标为2的值
  • lset k1 2 xxx:更新下标为2的值为xxx
  • l rem k1 2 a:如果count为正数(2),则从左边开始找,负数从右边开始找
  • linsert k1 after 6 a;在元素6后加个a
  • brpop:阻塞的弹出元素

hash

value类型为hash,value是一对对的键值对,hashmap的value又是一个hashmap

对value进行数值计算

需求每个用户3个数据,如何存储

set Sean::name ‘zzl’ key为Sean,value里的key为name

set sean::age ‘22’

hset sean name mcj

hincrbyfloat sean age 0.5 :年龄加0.5

需求

商品详情

set

不维护排序,乱序,不可以重复出现、

man @set

127.0.0.1:6379> sadd k3 ooxx
(integer) 1
127.0.0.1:6379> sinter k2 k3  //算交集、还有差集等
1) "ooxx"
127.0.0.1:6379> sINTErstore dest k2 k3  //将k2 k3的交集存到dest这个key中,所有数据都在服务端完成
(integer) 1
127.0.0.1:6379> smembers dest
1) "ooxx"
127.0.0.1:6379> srandmember k3 2   //随机在k3 中取出2个值.负数则取出带重复的结果集,一定满足要的数量
1) "5"
2) "ooxx"
127.0.0.1:6379> srandmember k3 -10   //随机事件可以解决,抽奖,10个奖品,用户<10 >10.中奖:是否重复
//解决家庭争斗
 1) "2"
 2) "ooxx"
 3) "2"
 4) "ooxx"
 5) "ooxx"
 6) "3"
 7) "5"
 8) "5"
 9) "3"
10) "3"

微博抽3个礼物

数据集中放所有粉丝,随机抽3个 srandmember k1 3 如果为负数则可能重复,不好

需求,人少奖品多,srandmemeber k1 -20

Spop:取出一个

sort-set

分值、元素、索引

并集,交集操作

有序的(元素本身顺序),去重的.

排序是如何实现的?skip list,跳跃表

skip list

它要完成的是去除多余的链表插入时的比较步骤(因为链表插入需要和所有节点比较)

用list做树机构,

1 5 9

1 3 5 7 9

1 2 3 4 5 6 7 8 9

当要插入一个5.5时,可以直接比最上面的索引,确定范围,同时在比第二层索引确定范围,然后再比较,插入

同时通过随机方式50%决定这个新节点是否定义为关键节点,同时一直抛硬币直到出现反,计算抛了多少次,决定跨越多少层

如果要删除的节点为5,那么一路摸到原本值为5的节点删除,同时删除索引,如果一层只有一个索引了,那么删除这一层所有

set中有很多元素,想让他们怎么排序,用哪些属性排序,比如水果的价格,含糖量,大小.

如果没给出条件,则按照字典顺序排序

每个元素都有正反索引.

权重操作

127.0.0.1:6379> zadd k4 8 apple 2 banana 3 orange
(integer) 3
127.0.0.1:6379> zrange k4 0 -1
1) "banana"
2) "orange"
3) "apple"
从小到大排序
127.0.0.1:6379> zrangebyscore k4 3 8
1) "orange"
2) "apple"
127.0.0.1:6379> zrevrange k4 0 -1
1) "apple"
2) "orange"
3) "banana"
127.0.0.1:6379> ZINCRBY k4 2.5 banana //加
"4.5"

进阶使用

缓存的目的:解决数据的读请求

大量插入数据:

冷启动:预加载一些数据

发布订阅模型:只有消费端订阅以后才会收到之后数据,服务端往一个指定名称的channel里发送数据,客户端订阅这个channel,就可以收到数据了.可以解决数据的实时性

历史消息数据放到哪里存取:3天之内的数据访问频率很高,可以使用sorted-set,将消息根据时间进行排序,然后根据时间移除,zremrange

事物:速度快,才会选用redis所以事物不是很完整.同时由于redis是单进程,单线程的,所有的请求都会排队执行,当 client1发给server开启事物,client2先发了一些命令,同时又发送了exec结束事物,那么就是client2先执行

​ client2也发送开启事物,client1后执行.

​ 主要是看谁先发送完整的开启与结束事物.

为什么redis不支持事物回滚,而是接着执行其他命令,因为多半的错误是由于编程问题导致的,不应该在生产环境中,回滚并不难解决这种问题

watich key:当前客户端监控key,如果有事物修改了监控的key,那么后续事物要改动key则不执行

布隆过滤器:解决缓存穿透.缓存穿透,指用户查询了缓存中,数据库中都没有的数据,造成数据库无用操作.用小的空间解决大数据匹配的过程,类似bitmap.但是有几率误标记,就像hash函数会发生碰撞那样,标记也不是百分百正确

  • 首先看库中有什么数据
  • 向bitmap中标记位置
  • 用户请求的数据经过某个映射函数,对应到bitmap中的二进制位,判断库中有这个东西,可以访问数据库,否则就不访问数据库

可以是客户端实现布隆算法并保存bitmap,反正布隆算法与bitmap都要分配在client与server里

如果穿透了并且数据库不存在.客户端要增加redis中的key,value标记不存在

如果数据库增加了元素,必须要完成对布隆过滤器的增加!,出现双写问题,既要写数据库,又要写布隆过滤器

key

key有过期时间可以设置,但是当key修改了值后,会剔除过期时间,

rediskey时间不能延长

定时过期

key的过期有两种方式:被动与主动

  • 惰性删除

    • 当用户访问这个key时,发现过期,于是删除
  • 定时随机抽取删除

    • 主动轮训服务端的key,每10秒随机取几个key看是否过期,如果过期率超过25%,则删除这些过期的,再重复以上步骤
  • 内存淘汰(从设置了过期时间的或者没设置过期时间的key中删除一些)

    • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
      volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
      volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
      allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
      allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
      no-enviction(驱逐):禁止驱逐数据,新写入操作会报错

redis与数据库的区别

缓存的目的:解决数据的读请求,快,减少访问压力,数据库讲究安全

数据可以丢

缓存的数据不重要,不是全量数据,但是被频繁读取

内存大小是瓶颈

  • key的有效期

  • 配置文件中有密码链接,库数量,端口,ip地址,最大内存配置

  • 当内存到达最大值后如何处理?官网有

    • 报错
    • 回收使用次数最少的key,lru是时间上多少时间没用,lfu是次数上最少次数没用
    • 回收快过期的key

面试问题

缓存击穿:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

缓存穿透:缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。进行数据是否有的判断.对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤

缓存一致性:双写一致性,写数据库,写redis,

缓存雪崩:缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机,推荐将常用数据设置永不过期.在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量

高并发下:

持久化

redis提供两种方式进行持久化,一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化),另外一种是AOF持久化(原理是将Reids的操作日志以追加的方式写入文件)

存储层技术有两个特点,1.快照、副本 2.日志(用户发送增删改查时,都会记录操作)

rdb

rdb持久化:快照,副本,时点性,一批数据8点钟要落盘,落落几十秒钟,此时快照里是8点的还是8点10分的.

a=3 b=4

有几种方式可以实现

  • 阻塞:要落盘时,redis不对外提供服务,数据就还是落盘时的样子

  • 非阻塞:redis继续对外提供服务,同时写磁盘,如果既要满足修改,又要满足落盘,那么时点性就会混乱,无法确定版本

    • 此时需要操作系统辅助,管道衔接前一个命令的输出作为后一个命令的输入,管道会触发创建子进程,左边一个进程,右边一个进程,原本bash窗口一个进程,3个.$$优先级高于管道,引出父子进程.

    • 如果子进程修改了值,那么父进程能否看见?不会看见,子进程无法影响父进程,就像子类与父类.

    • 那么父进程通过管道开启子进程,子进程是8点钟的数据,落盘,但是速度与内存空间是否够用,于是有系统调用fork(),

      fork的速度快,空间消耗不大,redis进程使用的是虚拟地址,指向物理地址的某个位置,redis的子进程也是虚拟地址,指向同一份物理地址,比如redis中有key=a的数据,fork到子进程中,也有个a,但是物理内存中只有一份,父进程fork子进程只是指针复制了一份

      子进程与父进程修改数据对方都看不见,是因为copy on write(写时复制),创建子进程并不复制,只有向修改数据时才复制数据,创建进程变快了并且子父进程不会把所有数据都改一遍.

      增删改都在父进程,父进程修改后,父进程复制值并修改指向物理内存新的值,子进程还是指向原来值并落盘

通过命令触发这种落盘:save 、 background save(fork子进程)

rdb的优点

  • 快速,可以不阻塞redis服务,一边对外提供服务一边持久化
  • rdb只有一个文件,可以快速迁移

fork的方式弊端:

  • 只有一个dump.rdb文件
  • 如果系统在定时持久化之前宕机了,那么就会丢失数据
  • 类似于java序列化,恢复数据快

rdb何时触发

执行命令save或者bgsave

save 900 1  #900s检查一次,增量的数据变更命令超过1,就触发;
save 300 10 #300s检查一次 更改10次
sava 60 10000 #60s检查一次  更改命令1w条,就触发;
aof(append only file)

redis的写操作记录到文件中,每执行一次操作就记录一次.

优点

  • 丢失数据少
  • 记录步骤
  • 不会破坏原本数据文件
  • 如果某人使用了flushall,只要后台重写还没有发生,那么就可以删除这条指令并且恢复数据

缺点

  • aof要比rdb文件大的多,因为可能有很多无用操作

rdb和aof可以同时开启,但是重启服务器只会用aof恢复(因为完整一些),aof中包含fdb全量,增加记录新的写操作

问题:redis运行了10年,开启aof,10年时redis挂了,1.aof多大,2,恢复要多久3.恢复时会不会溢出

1.aof,15t(就是很大,因为记录都在)

2.恢复不会溢出,因为都是在这个redis里操作,这些操作只要是线性执行的,以前都成功,现在重新来一遍也会成功

3.恢复用5年(就很久)

弊端:

  • 体量无限变大,恢复慢

重写rewrite是根据当前内存数据库中的数据进行遍历写到一个临时的AOF文件,待写完后替换掉原来的AOF文件即可,为了优化aof文件中的垃圾操作,进行指令合并.

在Redis配置文件redis.conf中,用户设置了auto-aof-rewrite-percentage和auto-aof-rewrite-min-size参数,并且当前AOF文件大小server.aof_current_size大于auto-aof-rewrite-min-size(server.aof_rewrite_min_size),同时AOF文件大小的增长率大于auto-aof-rewrite-percentage(server.aof_rewrite_perc)时,会自动触发AOF rewrite

Redis Server接收到客户端发送的BGREWRITEAOF指令请求,如果当前AOF/RDB数据持久化没有在执行,那么执行,反之,等当前AOF/RDB数据持久化结束后执行AOF rewrite

redis> BGREWRITEAOF

优化

RDB-AOF混合持久化方式即bgsave

如果能让aof足够小就不错.redis4.0以前用重写机制,抵消与整合命令,比如新增1,删除1,新增1,删除1…合并重复的命令.最终是一个纯指令的配置文件…4.0以后先将老数据rdb到aof文件中,再将增量数据以指令方式append到aof中,于是利用rdb的快,日志的全量.

老方式:重写机制,抵消与整合命令(消耗时间很多):set k1 1,set k1 2,调用命令后,文件中只会有k1 2的指令,老的数据没了

新版:调用将当前数据通过rdb到aof文件里,然后用户再普通写数据时,aof里又追加了明文数据set k1 这样子,增量数据+全量时点数据.将rdb与aof混合使用.

服务重新启动时,可以先加载rdb的内容,然后再执行aof指令部分达到Redis数据重放的目的,重启效率因此大幅得到提升。

redis是内存数据库,写操作会触发io行为,io有三个级别:no、aways、everysec(每秒),

当java调用write后,还要调用flush,因为write只是将数据写进了fd的recv-q,内核只有在这个队列满了才会刷磁盘,调用flush则会主动刷盘

为no时,redis不会调用flush,可能丢buffer

为aways,写一次调用一次flush,可能丢一条,一次

为everysec,每秒钟调用一次flush

集群

cap定理:一致性,可用性,分区容错性

为什么要多个监控节点判断主机是否可用?因为如果1个监控来判断那就会产生非常多的结果,如果半数以上的监控节点判断那就只会产生一个结果(势力范围),所以半数以上的节点就会解决这样的问题

一半集群使用基数台

单机单节点有什么问题?

  • 单点故障
  • 数据太多存不住,容量有限
  • 压力

akf:x y z 三轴来拆分服务

x:全量、镜像

y:业务、功能

数据一致性问题:

  • 所有节点阻塞直到数据一致(同步阻塞):强一致性,但是强一致性极其容易破坏可用性,因为如果其中一个节点没成功卡住了,那么整个集群就卡住了,客户端可能超时认为写失败了.本来就是要解决单实例的可用性,与强一致性冲突.要强一致就不能有可用性

  • 强一致性降级(异步):容忍数据丢失,客户端发送数据给redis,redis给节点,只要其中一个节点返回成功那就返回客户端成功,不阻塞在节点这(异步)

  • 先写到一个中间件(同步阻塞,例如kafka,足够快),返回成功,由中间件写到集群中(最终数据一致),但是有可能取到不一致的数据

主备

主备:客户端只会访问主redis,不会访问备redis

当主redis宕机后,备redis顶上,主的数据会同步到备上

改进:在主与备中加一个可靠的软件(kafka,可靠集群,响应速度快).主redis同步写到kafka然后返回,备机从kafka读数据可以达成最终一致性.

主从

主从:其他节点也参与业务计算,当客户端取一个黑盒集群时,由于最终一致性的问题可能会取到错误的数据

Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。

问题:

无法保证高可用

没有解决 master 写的压力

哨兵模式

哨兵模式是redis高可用的实现方式之一
使用一个或者多个哨兵(Sentinel)实例组成的系统,对redis节点进行监控,在主节点出现故障的情况下,能将从节点中的一个升级为主节点,进行故障转义,保证系统的可用性。

首先主节点的信息是配置在哨兵(Sentinel)的配置文件中

哨兵节点会和配置的主节点建立起两条连接命令连接订阅连接

哨兵会通过命令连接每10s发送一次INFO命令,通过INFO命令,主节点会返回自己的run_id和自己的从节点信息

故障转移(代替人设置主机)

主观下线

哨兵(Sentinel)节点会每秒一次的频率向建立了命令连接的实例发送PING命令,如果在down-after-milliseconds毫秒内没有做出有效响应包括(PONG/LOADING/MASTERDOWN)以外的响应,哨兵就会将该实例在本结构体中的状态标记为SRI_S_DOWN主观下线

客观下线

当一个哨兵节点发现主节点处于主观下线状态是,会向其他的哨兵节点发出询问,该节点是不是已经主观下线了。如果超过配置参数quorum个节点认为是主观下线时,该哨兵节点就会将自己维护的结构体中该主节点标记为SRI_O_DOWN客观下线
询问命令SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <run_id>

参数意义
ip/port当前认为下线的主节点的ip和端口
current_epoch配置纪元
run_id*标识仅用于询问是否下线 有值标识该哨兵节点希望对方将自己设置为leader 询问时用*,选举时用run_id
leader选举

在认为主节点客观下线的情况下,哨兵节点节点间会发起一次选举,命令还是上面的命令SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <run_id>,只是run_id这次会将自己监控的节点的run_id带进去,希望接受者将自己设置为主节点。如果超过半数以上的节点返回将该节点标记为leader的情况下,会有该leader对故障进行迁移

故障迁移
  1. 在从节点中挑选出新的主节点
    a. 通讯正常
    b. 优先级排序
    c. 优先级相同是选择offset最大的
  2. 将该节点设置成新的主节点 SLAVEOF no one,并确保在后续的INGO命令时,该节点返回状态为master
  3. 将其他的从节点设置成从新的主节点复制, SLAVEOF命令
  4. 将旧的主节点变成新的主节点的从节点
集群模式
槽指派

redis集群可以被分为16384个槽,只有这些槽全被指派了处理的节点的情况下,集群的状态才能是上线状态(ok)
操作redis集群的时候,将key作为参数,就可以计算出对应的处理槽上,所以存储等操作都应该在该槽对应的节点上。通过这种方式,可以完美的实现集群存储的水平拓展。类似于数取模

故障转移
发现故障节点
  1. 集群内的节点会向其他节点发送PING命令,检查是否在线
  2. 如果未能在规定时间内做出PONG响应,则会把对应的节点标记为疑似下线
  3. 集群中一半以上负责处理槽的主节点都将主节点X标记为疑似下线的话,那么这个主节点X就会被认为是已下线
  4. 向集群广播主节点X已下线,大家收到消息后都会把自己维护的结构体里的主节点X标记为已下线
从节点选举
  1. 当从节点发现自己复制的主节点已下线了,会向集群里面广播一条消息,要求所有有投票权的节点给自己投票(所有负责处理槽的主节点都有投票权)
  2. 主节点会向第一个给他发选举消息的从节点回复支持
  3. 当支持数量超过N/2+1的情况下,该从节点当选新的主节点
故障的迁移
  1. 新当选的从节点执行 SLAVEOF no one,修改成主节点
  2. 新的主节点会撤销所有已下线的老的主节点的槽指派,指派给自己
  3. 新的主节点向集群发送命令,通知其他节点自己已经变成主节点了,负责哪些槽指派
  4. 新的主节点开始处理自己负责的槽的命令
集群模式和哨兵模式的区别
  1. 哨兵模式监控权交给了哨兵系统,集群模式中是工作节点自己做监控
  2. 哨兵模式发起选举是选举一个leader哨兵节点来处理故障转移,集群模式是在从节点中选举一个新的主节点,来处理故障的转移

redis做分布式锁

  • 创建同一进程需要用到的某个key与value,做锁,有进程要用锁了就将value设置为1,释放锁设置为0
  • 设置锁的过期时间,保证不会死锁

进程进入首先获取锁状态,因为redis是单线程的,所以没有并发问题

//错误实例
//正确
public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
  //正确,多参数的set,设置过期时间
     String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    Long result = jedis.setnx(lockKey, requestId);
    if (result == 1) {
        // 若在这里程序突然崩溃,则无法设置过期时间,将发生死锁
        jedis.expire(lockKey, expireTime);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值