redis

    科技2024-07-18  80

    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 fLpop k1:对某个key弹出某个元素,左边弹(都是按照数据的存储形式弹)Rpop k1:对某个key弹出某个元素,右边弹(从右边弹出,如果是rpush的那么弹出f)lrange k1 0 -1:从左边开始取,取0下标到-1下标Iindex k1 2;取出下标为2的值lset k1 2 xxx:更新下标为2的值为xxxl rem k1 2 a:如果count为正数(2),则从左边开始找,负数从右边开始找linsert k1 after 6 a;在元素6后加个abrpop:阻塞的弹出元素

    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对故障进行迁移

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

    集群模式

    槽指派

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

    故障转移

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

    集群模式和哨兵模式的区别

    哨兵模式监控权交给了哨兵系统,集群模式中是工作节点自己做监控哨兵模式发起选举是选举一个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); } }
    Processed: 0.011, SQL: 9