reids

相比Redis事务,Lua脚本的优点:

  • 减少网络开销:使用Lua脚本,无需向Redis 发送多次请求,执行一次即可,减少网络传输
  • 原子操作:Redis 将整个Lua脚本作为一个命令执行,原子,无需担心并发
  • 复用:Lua脚本一旦执行,会永久保存 Redis 中,,其他客户端可复用

img

在redis中怎么体现的?
redis中用watch监视key,如果key在提交前被修改,则提交不成功。

基于Redis的令牌桶算法

提到限流就不得不提到令牌桶算法了。具体可以参照度娘的解释 令牌桶算法

令牌桶算法提及到输入速率和输出速率,当输出速率大于输入速率,那么就是超出流量限制了。

也就是说我们每访问一次请求的时候,可以从Redis中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。

依靠上述的思想,我们可以结合Redis的List数据结构很轻易的做到这样的代码,只是简单实现

依靠Java的定时任务,定时往List中rightPush令牌,当然令牌也需要唯一性,所以我这里还是用UUID进行了生成

端口6379

Redis是干嘛的?

C语言编写以key-value形式基于内存存储的高速缓存数据库·配合关系型数据库做高速缓存 ,缓存高频次访问的数据,降低数据库io

还有因为它是单线程+io多路复用,所以可以数据高并发的读写。新版本支持多线程处理网络IO

Redis支持主从模式,可以配置集群,更利于支撑大型的项目。

redis的数据结构

Redis 有 5 种基础数据结构,分别为:string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)

字符串 string 是 Redis 最简单的数据结构。Redis 所有的数据结构都是以唯一的 key

字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数据。不同类型的数据结

构的差异就在于 value 的结构不一样。

string:虽然redis是c语言编写的但是它用的不是c语言的string,它是自己构建了一种名为 简单动态字符串SDS,比如C语言字符串它优化了很多。比如它

可以O(1)获得字符串长度

还可以存储二进制数据

避免缓冲区溢出,

减少修改字符串重新空间分配次数,

列表list:reids也是自己实现的,和集合里面的LinkedList差不多,但是也有一点小优化比如头尾节点还有链表长度提前保存了引用所以获取的时间复杂度是O(1)

但是考虑到在数据量较小的时候LinkedList附加指针占用空间太大

会生成这个结构是 ziplist,也即是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存,虽然它也会用指针但是它的指针比LinkedLIst指针小很多,当数据量比较多的时候才会改成,LinkedList

set:Redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的。它的

内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值 NULL

(Sortset)**zset实现:**它类似于 Java 的 SortedSet 和 Hashtable 的结合体,一方面它是一个 set,保证了内部

value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权

重。它的内部实现用的是一种叫着「跳跃列表」的数据结构。

zset 中最后一个 value 被移除后,数据结构自动删除,内存被回收。 zset 可以用来存

粉丝列表,value 值是粉丝的用户 ID,score 是关注时间。我们可以对粉丝列表按关注时间

进行排序

一个zset同时包含一个字典(dict)和一个跳跃表(zskiplist)

Sorted Set(有序集合)当前有两种编码:ziplist、skiplist使用压缩列表和跳表

当保存的元素长度都小于64字节,同时数量小于128时使用压缩列表

Hash底层用的hashtable和压缩列表,键和值的节点都放在了压缩列表里面。结构类似hashmap

hash结构是数组+链表组成,每个key value保存到压缩列表里面,同一个keyvalue被压缩列表压缩在一起

扩容是采用,渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 结构,查询时会同时查询两个hash 结构,然后在后续的定时任务中以及 hash 的子指令中,循序渐进地将旧 hash 的内容一点点迁移到新的 hash 结构中,旧的hash最后一个元素被移除后就删除。

新增的数据就加入到新的hash表,旧的不加入,保证旧的哈希表会被一步步减少然后删除

为什么?

如果哈希表里保存的键值对数量很大时, 如:四百万、四千万甚至四亿个键值对, 那么一次性将这些键值对全部 rehash 到 ht[1] 的话,庞大的计算量(需要重新计算链表在桶中的位置)可能会导致服务器在一段时间内停止服务(redis是单线程的,如果全部移动会引起客户端长时间阻塞不可用)。

压缩同整数集合一样压缩列表也不是基础数据结构,而是 Redis 自己设计的一种数据存储结构。它有点儿类似数组,通过一片连续的内存空间,来存储数据。不过,它跟数组不同的一点是,它允许存储的数据大小不同。

既然Redis键值存储使用哈希表,时间复杂度为O(1),那为什么写入大量数据后,操作会变慢呢?

数组+链表 链地址法

redis是如何做rehash操作的?

答:Redisrehash是一种渐进式rehash。为了使rehash操作更高效,Redis默认使用了两个全局哈希表:哈希表1和哈希表2。一开始,当你刚插入数据时,默认使用哈希表1,此时的哈希表2并没有被分配空间。随着数据逐步增多,Redis开始执行rehash,这个过程分为三步:

一边处理请求一边拷贝,处理请求的时候顺带把数据拷贝过去

  • 给哈希表2分配更大的空间,例如是当前哈希表1大小的两倍;
  • 把哈希表1中的数据重新映射并拷贝到哈希表2中;
  • 释放哈希表1的空间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CfZu9EjX-1649594637316)(image-20211213161915706.png)]

上面是普通数组,下面是压缩列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRP97uT9-1649594637317)(image-20211213161920126.png)]

Sorted Set 为什么同时使用字典和跳跃表?

主要是为了提升性能。

单独使用字典:单独使用字典的话范围查询不方便字典需要进行排序,至少需要 O(NlogN) 的

时间复杂度及额外 O(N) 的内存空间。字典就类似于Map,实际开发现在都有用Map字典已经过时

单独使用跳跃表:单值查询时间复杂度Olog(n),同时范围查询更方便

,Redis 的跳跃表共有 64 层,意味着最

最多可以容纳 2^64 次方个元素

AVL树和红黑树有几点比较和区别:
(1)AVL树是更加严格的平衡,因此可以提供更快的查找速度,一般读取查找密集型任务,适用AVL树。
(2)红黑树更适合于插入修改密集型任务。

因为,AVL树的旋转比红黑树的旋转更加难以平衡和调试,AVL插入删除后平衡起来更麻烦,但是平衡更加严格让他查询速度更快

查询和增删都有的平均性能红黑树更强

bitmap:位图,如果只需要统计数据的二值状态,例如商品有没有、用户在不在等,就可以使用 Bitmap,因为它只用一个 bit 位就能表示 0 或 1。

Hyperloglog:专门用来做基数统计,但是它是基于概率完成的所以有一定误差

Geospatial :主要用于存储地理位置信息

redis 的访问压力也很大怎么解决

从机,集群,限流,二级缓存

开启多线程后,是否会存在线程并发安全问题?

不会,Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行,只是网络数据会存在并发问题,但是redis本身并不存在

多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dhVT6UvR-1649594637317)(image-20211216195923138.png)]

Redis 是单线程还是多线程?

redis 4.0 之前,redis 是完全单线程的,它也有后台线程在处理一些较为缓慢的操作,例如清理脏数据、无用连接的释放、大 key 的删除等等。

redis 6.0 中,多线程主要用于网络 I/O 阶段,也就是接收命令和写回结果阶段,而在执行命令阶段,还是由单线程串行执行。由于执行时还是串行,因此无需考虑并发安全问题。

当有读事件到来时,主线程将该客户端连接放到全局等待读队列

2、读取数据:主线程将等待读队列的客户端连接通过轮询调度算法分配给 I/O 线程处理;2)同时

主线程也会自己负责处理一个客户端连接的读事件;3)当主线程处理完该连接的读事件后,会自旋等待

所有 I/O 线程处理完毕

3、命令执行:主线程按照事件被加入全局等待读队列的顺序(这边保证了执行顺序是正确的),串行执

行客户端命令,然后将客户端连接放到全局等待写队列

4、写回结果:跟等待读队列处理类似,主线程将等待写队列的客户端连接使用轮询调度算法分配给 I/O

线程处理,同时自己也会处理一个,当主线程处理完毕后,会自旋等待所有 I/O 线程处理完毕,最后清

空队列

为什么redis性能瓶颈不是CPU?

Redis的瓶颈并不在CPU,它的主要瓶颈在于内存和网络。所谓内存瓶颈很好理解,Redis做为缓存使用时很多场景需要缓存大量数据,所以需要大量内存空间,这可以通过集群分片去解决

对于网络瓶颈,Redis在网络I/O模型上采用了多路复用技术,虽然性能也很高,但是调用epoll的过程是阻塞的只是epoll的高速处理看起来非阻塞了,随着并发量越来越高这里会成为一个瓶颈,所以采用多线程来处理网络读写

单线程是指的是在核心网络模型中,网络请求模块使用一个线程来处理,即一个线程处理所有网络请求,

3.Redis绝大部分操作是基于内存的,而且是纯kv(key-value)操作,所以命令执行速度非常快。我们可以大概理解成,redis中的数据存储在一张大HashMap中,HashMap的优势就是查找和写入的时间复杂度都是O(1)。Redis内部采用这种结构存储数据,就奠定了Redis高性能的基础。

1 redis是单线程的,因此避免了多线程切换和恢复上下文的时间,还有并发问题,还有多核CPU的影响

Redis 是单线程还是多线程?

redis 4.0 之前,redis 是完全单线程的,它也有后台线程在处理一些较为缓慢的操作,例如清理脏数据、无用连接的释放、大 key 的删除等等。

redis 6.0 中,多线程主要用于网络 I/O 阶段,也就是接收命令和写回结果阶段,而在执行命令阶段,还是由单线程串行执行。由于执行时还是串行,因此无需考虑并发安全问题。

Redis 6.0 之前的版本真的是单线程吗

不是,Redis基于Reactor模式分成了四个部分,套接字,IO多路复用程序,文件事件分配器,事件处理器。IO多路符用监听到消息了发给文件事件分派器,然后分配器交给事件处理器来处理,,事件分派器是一个队列结构,它的消费是单线程的,所以才说redis是单线程的。后台fork子进程进行aof文件重写,和生成rdb文件快照,惰性删除(也叫异步删除),这是由额外的线程执行的,意思就是我们可以使用异步的方式对 Redis 中的数据执行删除操作了

Redis 6.0 之前为什么一直不使用多线程?

因为redis单线程处理已经非常快,大部分情况都用不到多线程。

多线程实现起来复杂,而且它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。

Redis 删除过期键的策略(缓存失效策略、数据过期策略)

定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行

对键的删除操作。对内存最友好,对 CPU 时间最不友好。

惰性删除:放任键过期不管,但是每次获取键时,都检査键是否过期,如果过期的话,就删除该键;如

果没有过期,就返回该键。对 CPU 时间最优化,对内存最不友好。

定期删除:每隔一段时间,默认100ms,程序就对数据库进行一次检査,删除里面的过期键。至 于要删

除多少过期键,以及要检査多少个数据库,则由算法决定。前两种策略的折中,对 CPU 时间和内存的友

好程度较平衡。

Redis 使用惰性删除和定期删除。

那么定期+惰性都没有删除过期的key怎么办?

假设redis每次定期随机查询key的时候没有删掉,这些key也没有做查询的话,就会导致这些key一直保存在redis里面无法被删除,这时候就会走到redis的内存淘汰机制。

内存满了才开启这个策略

Redis的内存淘汰(驱逐)策略

redis是如何让让数据一直在缓存中的?

当 redis 的内存空间(maxmemory 参数配置)已经用满时,redis 将根据配置的驱逐策略

(maxmemory-policy 参数配置),进行相应的动作。

分为设置了过期事件的key淘汰策略和没有设置过期时间的key淘汰策略

网上很多资料都是写 6 种,但是其实当前 redis 的淘汰策略已经有 8 种了,多余的两种是 Redis 4.0 新

增的,基于 LFU(Least Frequently Used)算法实现的。

noeviction:默认策略,不淘汰任何 key,直接返回错误

allkeys-lru:在所有的 key 中,使用 LRU 算法淘汰部分 key

allkeys-lfu:在所有的 key 中,使用 LFU 算法淘汰部分 key,该算法于 Redis 4.0 新增

allkeys-random:在所有的 key 中,随机淘汰部分 key

过期的里面多了个最短时间,全局里面可以报错

volatile-lru:在设置了过期时间的 key 中,使用 LRU 算法淘汰部分 key

volatile-lfu:在设置了过期时间的 key 中,使用 LFU 算法淘汰部分 key,该算法于 Redis 4.0 新增

volatile-random:在设置了过期时间的 key 中,随机淘汰部分 key

volatile-ttl:在设置了过期时间的 key 中,挑选 TTL(time to live,剩余时间)短的 key 淘汰

  • 也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru
  • 如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

如果数据访问频率差不多可以按照lru时间淘汰

如果追求追求淘汰的效率避免容量不够可以随机淘汰

重要的数据就开启不淘汰

Redis 的持久化机制有哪几种,各自的实现原理和优缺点?

RDB:就是把内存中的数据键值信息生成二进制文件保存到磁盘中生成RDB文件,类似于生成一张快照。

命令:有两个 Redis 命令可以用于生成 RDB 文件,一个是 SAVE,另一个是 BGSAVE。

RDB可以用save指令来手动保存生成 RDB 快照文件,但是因为redis是单线程执行指令,如果某个指令执行时间过长就会长时间阻塞服务器将无法处理客户端发来的命令请求,所以一般不使用。

第二种是besave,也就是后台执行,调用bgsave它会在后台用linux调用fork函数生成子进程去处理指令然后生成rdb的存储文件,执行完了就返回一个消息给redis,这也是常用的一种方式,但是比较浪费内存,因为子进程会拷贝父进程的内存,如果内存过多拷贝时还会出现停顿现象

BGSAVE:fork 子进程来生成 RDB 快照文件,阻塞只会发生在 fork 子进程的时候,之后主进程可以正常处理请求,详细过程如下图:

fork:在 Linux 系统中,调用 fork() 时,会创建出一个新进程,称为子进程,子进程会拷贝父进程的

page table。如果进程占用的内存越大,进程的 page table 也会越大,那么 fork 也会占用更多的时

间。极端情况下,如果所有的页面都被修改,则此时的内存占用是原先的2倍。

RDB 的优点

简答: 1.二进制文件占用空间小内容紧凑,存储效率高,通常存取某个时间点的数据 因此适合做灾容恢复,大数据集恢复数据比AOF更快

​ 2.提高了redis性能使用fork子进程,

缺点:存数据频率不高可能会导致数据丢失,fork子进程会使用额外的CPU资源,数据量大的时候存储效率低,

1)RDB 文件是是经过压缩的二进制文件,占用空间很小,它保存了 Redis 某个时间点的数据集,很适

合用于做备份。 比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一

天,也备份一个 RDB 文件。这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。

2)RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以

(在加密后)将它传送到别的数据中心。

3)RDB 可以最大化 redis 的性能。父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后

这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。

RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

RDB 的缺点

1)RDB 在服务器故障时容易造成数据的丢失。RDB 允许我们通过修改 save point 配置来控制持久化的

频率。但是,因为 RDB 文件需要保存整个数据集的状态, 所以它是一个比较重的操作,如果频率太频

繁,可能会对 Redis 性能产生影响。所以通常可能设置至少5分钟才保存一次快照,这时如果 Redis 出

现宕机等情况,则意味着最多可能丢失5分钟数据。

2)RDB 保存时使用 fork 子进程进行数据的持久化,如果数据比较大的话,fork 可能会非常耗时,造成

Redis 停止处理服务N毫秒。如果数据集很大且 CPU 比较繁忙的时候,停止服务的时间甚至会到一秒。

3)Linux fork 子进程采用的是 copy-on-write 的方式。在 Redis 执行 RDB 持久化期间,如果 client 写

入数据很频繁,那么将增加 Redis 占用的内存,最坏情况下,内存的占用将达到原先的2倍。刚 fork

时,主进程和子进程共享内存,但是随着主进程需要处理写操作,主进程需要将修改的页面拷贝一份出

来,然后进行修改。极端情况下,如果所有的页面都被修改,则此时的内存占用是原先的2倍。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6pjXzu04-1649594637318)(image-20211213174932328.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wcyJykQj-1649594637318)(image-20211213174958340.png)]

AOF

描述:保存 Redis 服务器所执行的所有写操作命令来记录数据库状态,并在服务器启动时,通过重新执

行这些命令来还原数据集

AOF 持久化功能的实现可以分为三个步骤:命令追加、文件写入、文件同步。

命令追加:当 AOF 持久化功能打开时,服务器在执行完一个写命令之后,会将被执行的写命令追加到服

务器状态的 aof 缓冲区(aof_buf)的末尾。

文件写入是指写入到页缓存中。

为什么需要 AOF 重写

AOF 持久化是通过保存被执行的写命令来记录数据库状态的,随着写入命令的不断增加,AOF 文件中的

内容会越来越多,文件的体积也会越来越大。如果你开启的是每次命令都存文件更大

而且AOF还有一个重写机制,就是因为它专注细节很多记录都会存,所以会导致文件越来越大,它内部就会把那些对结果没有影响的命令去掉或者是合并,去掉那些无效的操作,这样做可以节约空间,然后去掉了多余的命令执行的更快。重写的工作原理和RDBbgsave类似,也是后台fork函数生成子进程然后重写aof文件,重写完了返回指令通知redis。

一般使用redis时候AOF优先级更高,先使用他,他的存储速度比起RDB更快,而且不容易丢数据。但是恢复速度慢一些所以数据发生灾难性丢失一般采用RDB先恢复。

重写也有两种方式一种是直接重写,是因为redis是单线程执行指令,如果某个指令执行时间过长就会长时间阻塞服务器将无法处理客户端发来的命令请求,所以一般不使用。

另一种就是后台fork子进程去重写,子进程会拷贝父进程的pagetable去操作,如果进程占用的内存越大,进程的 page table 也会越大,那么 fork 也会占用更多的时

间。如果 Redis 占用的内存很大,那么在 fork 子进程时,则会出现明显的停顿现象。

AOF 后台重写存在的问题

AOF 后台重写使用子进程进行从写,解决了主进程阻塞的问题,但是仍然存在另一个问题:子进程在进

行 AOF 重写期间,服务器主进程还需要继续处理命令请求,新的命令可能会对现有的数据库状态进行修

改,从而使得当前的数据库状态和重写后的 AOF 文件保存的数据库状态不一致。

解决方案就是不要直接对现有AOF文件重写,而是在重写缓冲区里面重写,缓冲区里面重写完了直接对现有AOF

文件进行覆盖就行了。现有 AOF 文件的处理工作会如常进行。这样即使在重写的中途发生停机,现有的 AOF 文件也还是安

全的,停机命令没了,但是缓冲区里面还存着

如何解决 AOF 后台重写存在的数据不一致问题

为了解决上述问题,Redis 引入了 AOF 重写缓冲区(aof_rewrite_buf_blocks),这个缓冲区在服务器

创建子进程之后开始使用,当 Redis 服务器执行完一个写命令之后,它会同时将这个写命令追加到 AOF

缓冲区和 AOF 重写缓冲区。

这样一来可以保证:

1、现有 AOF 文件的处理工作会如常进行。这样即使在重写的中途发生停机,现有的 AOF 文件也还是安

全的,停机命令没了,但是缓冲区里面还存着

2、从创建子进程开始,也就是 AOF 重写开始,服务器执行的所有写命令会被记录到 AOF 重写缓冲区里

面。

这样,当子进程完成 AOF 重写工作后,父进程会在 serverCron 中检测到子进程已经重写结束,则会执

行以下工作:

1、将 AOF 重写缓冲区中的所有内容写入到新 AOF 文件中,这时新 AOF 文件所保存的数据库状态将和

服务器当前的数据库状态一致。主要是先在缓冲区里面重写,再用旧的吧新的覆盖掉

2、对新的 AOF 文件进行改名,原子的覆盖现有的 AOF 文件,完成新旧两个 AOF 文件的替换。

之后,父进程就可以继续像往常一样接受命令请求了。

AOF 的缺点

\1. 对于相同的数据集,AOF 文件的大小一般会比 RDB 文件大。

\2. 根据所使用的 fsync 策略,AOF 的速度可能会比 RDB 慢。通常 fsync 设置为每秒一次就能获得比

较高的性能,而关闭 fsync 可以让 AOF 的速度和 RDB 一样快。

AOF优点:

1.对数据的存储更可靠,可以选择每次命令都存储

2.存数据快,因为是命令存储但是会导致恢复起来慢。相比之下RDB存的慢但是全量恢复很快。

3.后台会生成fork子进程去重写AOF文件,,它内部就会把那些对结果没有影响的命令去掉或者是合并,去掉那些无效的操作,

最终策略混合持久化:

先用RDB进行全量数据恢复,再用AOF恢复部分数据,因为RDB存储特点就是频率比较低,但是恢复数据快,然后AOF存储频率高,存储的数据更加完整,但是恢复起来慢,就综合了他们的优点来混合回收

Linux 操作系统中为了提升性能,使用了页缓存(page cache)。当我们将 aof_buf 的内容写到磁盘上

时,此时数据并没有真正的落盘,而是在 page cache 中,为了将 page cache 中的数据真正落盘,需要

执行 fsync / fdatasync 命令来强制

\1. always:每处理一个命令都将 aof_buf 缓冲区中的所有内容写入并同步到AOF 文件,即每个命令

都刷盘。

\2. everysec:将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,如果上次同步 AOF 文件的时间距离

现在超过一秒钟, 那么再次对 AOF 文件进行同步, 并且这个同步操作是异步的,由一个后台线程

专门负责执行,即每秒刷盘1次。

\3. no:将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 但并不对 AOF 文件进行同步, 何时同步

由操作系统来决定。即不执行刷盘,让操作系统自己执行刷盘。

Redis怎么保证高可用、有哪些集群模式

主从复制、哨兵模式、集群模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzSAz3sf-1649594637319)(image-20211213184228527.png)]

主从复制分为两个阶段,首先是全量复制,主机开开启bgsave用fork子进程生成RDB文件,利用RDB全量数据恢复快的这个特性把RDB发送给slave让slave做数据恢复,但是在恢复的这个阶段仍然会有指令到来,这时候到来的指令放到复制缓冲区,等从机RDB文件恢复完了再把复制缓冲区命令发送过来,此时从机接收到缓冲区的命令再执行,这个过程叫部分复制,用的是AOF.

主从复制的过程中如果因为网络原因停止复制了会怎么样?

会接着复制缓冲区上次的位置接着复制,复制缓冲区是一个队列的结构,内部维护了字节值和偏移量

字节值:就是指令的一个一个字节

偏移量:就是每个字节都有一个编号就叫偏移量

主机和从机每次传递就通过偏移量直到自己传到了哪个位置,下次就从那开始

这个缓冲区要AOF开启才会有

如果没找到就重新发生RDB实行全量复制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DQK17Yjn-1649594637320)(image-20211213190842219.png)]

主从复制优点:读写分离提高了读的能力,可以大量增加从机应对并发读的操作。

2.出现故障以后可以有从机帮忙实现故障转移

3.开启合理的数据备份策略可以有效防止数据丢失

缺点:故障恢复很复杂,没有发现故障机制,需要手动干预

2.主机写能力受到单机的限制,存储能力也是

Redis主从架构数据会丢失吗,为什么?

  1. 异步复制导致的数据丢失:因为master -> slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了。

    解决策略:丢失无法避免,但是可以配置数据复制和同步延迟时间,可以设置不超过10秒,超过了就不接受任何请求

  2. master还运行着,但是突然脱离了正常网络,其他从机执行故障恢复为主机,然后客户端依然向旧主机发送数据,然后旧主机恢复的时候会作为从机把数据清空重新从新主机同步数据,导致客户端发送数据丢失。

    解决策略:可以对客户端采用降级策略,写入磁盘保证数据不丢失,或者写入消息队列,过一段时间再去消费。

解决故障恢复机制方案:哨兵

哨兵通常需要三个及以上的奇数个

哨兵可以同时监视多个主从服务器,并且在被监视的master下线时,自动将某个slave提升为master,然后由新的master继续接收命令。整个过程如下:sentinel会每隔1秒向所有实例(包括主从服务器和其他sentinel)发送ping命令,并且根据回复时间是否超过设定的值判断是否已经下线,这种方式叫做主观下线。当判断为主观下线时,就会向其他监视的sentinel询问,如果超过半数的投票认为已经是下线状态,则会标记为客观下线状态,此时会投票选举出一个哨兵去触发故障转移,在从节点中重新选举一个主结点。这里故障转移只针对主节点,从节点并不会。如果没有超过半数投票同意就会等到主机进行回应移除主观下线。

故障转移时会从剩下的slave选举一个新的master,被选举为master的标准是什么?

符合条件的从节点竞选顺序:

  1. 优先级最高的从节点将会作为新主节点;
  2. 优先级相等则判断复制偏移量,偏移量最大的从节点获胜;
  3. 如果以上两个条件都相同,选择 Redis 运行时随机生成 ID 最小那个为新的主服务器。
旧主节点恢复上线

如果之前的旧主节点恢复上线,会作为从节点运行在主从服务器模式中。

服务器运行ID(runid)

  • 概念:服务器运行ID是每一台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行id
  • 组成:运行id由40位字符组成,是一个随机的十六进制字符,例如:7cfbf53e4901277d7247db9fac7945af44dcc666
  • 作用:运行id被用于在服务器间进行传输,识别身份,如果向两次操作均对同一台服务器进行,必须每次操作携带对应的运行id,用于对方识别
  • 实现方式:运行id在每台服务器启动时自动生成的,master在首次连接slave时,会将自己的运行ID发送给slave,slave保存此ID,通过info Server命令,可以查看节点的runid

同步配置的时候其他哨兵根据什么更新自己的配置呢?

其他哨兵会从新主机那里拿到一个版本号,每次切换版本号都是唯一的,如果切换失败就等一段时间重新获得这个版本号,哨兵都靠这个版本号来调整自己的配置

为什么Redis哨兵集群只有2个节点无法正常工作?

因为两个节点没法执行选举,两个节点的选举票数是2,3个节点也是2,但是如果这里宕机是主机和所在哨兵一起宕机就没法满足这个2的选举票数,无法实行故障转移

但是哨兵依旧没有解决单机容量有限的问题。

reids集群

哨兵和主从复制都是一主多从吗,哨兵至少三个,集群redis主机至少6个节点,三主三从,主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

Redis Cluster采用虚拟槽分区,就是把存储空间分成16384个slot,然后对key进行一个CRC运算从而确定这个key属于哪一份,并且不同服务器之间维持了内部通讯机制,只需要两次寻址,也就是说第一台机器没有找到你的key可以直接通过服务器内部通讯定位到这个key在哪个机器。

Sentinel 模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或 Sentinel 模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个 Redis 实例中。Cluster 模式的出现就是为了解决单机 Redis 容量有限的问题,将 Redis 的数据根据一定的规则分配到多台机器。

优势:

  • 自动分割数据到不同的节点上。

  • 整个集群的部分节点失败或者不可达的情况下能够继续处理命令

  • Cluster 模式的特点如下:

    多个redis节点网络互联,数据共享
    所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用
    不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上,并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为
    支持在线增加、删除节点
    客户端可以连接任何一个主节点进行读写
    

一个节点发现某个节点疑似下线,它会将这条信息向整个集群广播,其它节点就会收到这个消息,并且通过 PING 的方式监测某节点是否真的下线了。如果一个节点收到某个节点疑似下线的数量超过集群数量的一半以上,就可以标记该节点为确定下线状态,然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。

这就是疑似下线和确认下线的概念,这个概念和哨兵模式里面的主观下线和客观下线的概念比较类似。

发现故障节点
  1. 集群内的节点会向其他节点发送PING命令,检查是否在线
  2. 如果未能在规定时间内做出PONG响应,则会把对应的节点标记为疑似下线
  3. 集群中一半以上负责处理槽的主节点都将主节点X标记为疑似下线的话,那么这个主节点X就会被认为是已下线
  4. 向集群广播主节点X已下线,大家收到消息后都会把自己维护的结构体里的主节点X标记为已下线
从节点选举

(从下线的主节点的所有从节点中,选择一个从节点)作为主节点

  1. 当从节点发现自己复制的主节点已下线了(这里是多个从节点),会向集群里面广播一条消息,要求所有有投票权的节点给自己投票(所有负责处理槽的主节点都有投票权)
  2. 主节点会向第一个给他发选举消息的从节点回复支持
  3. 当支持数量超过N/2+1的情况下,该从节点当选新的主节点
  4. 新主机会拿走旧主机的全部槽位
故障的迁移
  1. 新当选的从节点执行 SLAVEOF no one,修改成主节点
  2. 新的主节点会撤销所有已下线的老的主节点的槽指派,指派给自己
  3. 新的主节点向集群发送命令,通知其他节点自己已经变成主节点了,负责哪些槽指派
  4. 新的主节点开始处理自己负责的槽的命令
集群模式和哨兵模式的区别#
  1. 哨兵模式监控权交给了哨兵系统,集群模式中是工作节点自己做监控
  2. 哨兵模式发起选举是选举一个leader哨兵节点来处理故障转移,集群模式是在从节点中选举一个新的主节点,来处理故障的转移

Redis高可用方案具体怎么实施?

使用官方推荐的哨兵(sentinel)机制就能实现,当主节点出现故障时,由Sentinel自动完成故障发现和转移,并通知应用方,实现高可用性。它有四个主要功能:

  • 集群监控,负责监控Redis master和slave进程是否正常工作。

  • 消息通知,如果某个Redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员。

  • 故障转移,如果master node挂掉了,会自动转移到slave node上。

  • 配置中心,如果故障转移发生了,通知client客户端新的master地址。

  • Redis宕机怎么办?哨兵机制?

  • 在这里插入图片描述

  • 宕机则需要分为在主从模式下区分来看:
    1、从的redis宕机怎么解决?
      配置主从复制的时候才配置从的redis, 从的会从主的redis中读取主的redis的操作日志,来达到主从复制。
        1.只要把从的redis重新启动,再和主的进行连接就可以
        2.如果从redis上面做数据的持久化,可以直接连接到主的上面,只要实现增量备份

    2、主的redis宕机怎么解决?
      要先确认是否做持久化,若没有做持久化,重新启动主的redis就会造成数据丢失。
        1.先把从的redis升级为主的redis. 执行slave of one命令
        2.原来的主的可以重新启动,作为从的redis, 连接到主的redis上面做主从复 制

分布式缓存寻址算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E0X3whDh-1649594637320)(image-20211213150716395.png)]

圆环长度是int最大值2的32次方-1

这样有机器宕机也只有这台机器之前的那一部分节点受到影响

一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点

一致性资源倾斜问题,通过生成更多hash节点(用其他值进行hash运算),这样每次hash计算出来的节点就不会大概率命中真实节点较多的服务器了,保证了负载均衡,虽然那个服务器还是要处理那么多节点,但是不至于让他每次hash运算都很大概率去处理。取模运算来计算出位置,也就是说只能在已有hash圆环上几个节点进行选择

hashslot:就是把存储空间分成16384个slot,然后对key进行一个CRC运算从而确定这个key属于哪一份,并且不同服务器之间维持了内部通讯机制,只需要两次寻址,也就是说第一台机器没有找到你的key可以直接通过服务器内部通讯定位到这个key在哪个机器。

Redis cluster中每个master都会持有部分slot(槽),比如有3个master,那么可能每个master持有5000多个hash slot。

寻址的主要问题是增加或者减少服务器时候会造成其他机器的key受到影响,hash slot让node的增加和移除很简单,增加一个master,就将其他master的hash slot移动部分过去,减少一个master,就将它的hash slot移动到其他master上去。每次增加或减少master节点都是对16384取模,而不是根据master数量,这样原本在老的master上的数据不会因master的新增或减少而找不到。并且增加或减少master时Redis cluster移动hash slot的成本是非常低的。

slot:一个位数组结构

虚拟桶是取模和一致性哈希二者的折中办法。

  • 采用固定节点数量,来避免取模的不灵活性。
  • 采用可配置映射节点,来避免一致性哈希的部分影响。

槽位定位算法

Redis 集群总共的槽位数是 16384 个,每一个主节点负责维护一部分槽以及槽所映射的键值数据,Redis 集群默认会对要存储的 key 值使用 CRC16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位,公式为:

slot = CRC16(key) % 16383

缓存雪崩

描述:大量的热点 key 设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量

大、压力骤增,引起雪崩,甚至导致数据库被打挂。

缓存雪崩其实有点像“升级版的缓存击穿”,缓存击穿是一个热点 key,缓存雪崩是一组热点 key。

解决方案:

1**)过期时间打散。**既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期

时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效。

2**)热点数据不过期。**该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情

况。

3**)加互斥锁。**该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key,只允许一个线程去计算,其

他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。

*:如果缓存失效后,大量的请求直接打在数据库上会发生什么?*

大量请求直接到数据库,数据库无法抗住这么大的并发量,导致数据库宕机,从而导致整个程序宕机。之后可能就无法启动数据库,起一次,死一次。

事前:尽量保证整个 Redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。

事中:本地 Ehcache 缓存 + Hystrix 限流&降级,避免 MySQL 崩掉。

事后:利用 Redis 持久化机制保存的数据尽快恢复缓存。

缓存击穿

描述:某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以

请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

热点数据不过期。直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。

缓存穿透

描述:访问一个缓存和数据库都不存在的 key,此时会直接打到数据库上,并且查不到数据,没法写缓

存,所以下一次同样会打到数据库上。

此时,缓存起不到作用,请求每次都会走到数据库,流量大时数据库可能会被打挂。此时缓存就好像被

“穿透”了一样,起不到任何作用。

1**)接口校验。**在正常业务流程中可能会存在少量访问不存在 key 的情况,但是一般不会出现大量的情

况,所以这种场景最大的可能性是遭受了非法攻击。可以在最外层先做一层校验:用户鉴权、数据合法

性校验等,例如商品查询中,商品的ID是正整数,则可以直接对非正整数直接过滤等等。

2**)缓存空值。**当访问缓存和DB都没有查询到值时,可以将空值写进缓存,但是设置较短的过期时间,

该时间需要根据产品业务特性来设置。

3**)布隆过滤器。**使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接被过滤,存在的 key 则

再进一步查询缓存和数据库。

知道什么是热key吗?热key问题怎么解决?

所谓热key问题就是,突然有几十万的请求去访问redis上的某个特定key,那么这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机引发雪崩。

针对热key的解决方案:

  1. 提前把热key打散到不同的服务器,降低压力
  2. 加入二级缓存,提前加载热key数据到内存中,如果redis宕机,走内存查询

Redis事务

multi和exec中间查询没有效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EDcPAM6w-1649594637321)(image-20220121131327629.png)]

Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

为什么Redis不支持回滚?

原子性:redis的原子性除了不支持回滚执行命令失败了还会继续执行,所以并不是严格的原子性

  • Redis 命令只在两种情况失败:

    • 语法错误的时候才失败(在命令输入的时候不检查语法)。
    • 要执行的key数据类型不匹配:这种错误实际上是编程错误,这应该在开发阶段被测试出来,而不是生产上。
  • 这两种错误回滚了也没用

  • MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列。multi开启事务,类似关系型数据库中的begin,表示开启事务

  • EXEC:执行事务中的所有操作命令。exec执行事务,类似关系型数据库中的commit。操作会提交事务队列中所有入队命令,并取消客户端事务状态。即使事务中有某条/某些命令执行失败了, 事务队列中的其他命令仍然会继续执行 —— Redis 不会停止执行事务中的命令。

  • **DISCARD:**取消事务,放弃执行事务块中的所有命令。discard

    discard取消事务,类似关系型数据库中的rollback。操作会清空事务队列中所有入队命令,并取消客户端事务状态。

  • **WATCH:基于CAS乐观锁机制来监听的。**监视一个或多个key,如果事务在执行前(也就是exec开始前),这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令。exec 指令就会返回 null 回复告知客户端事务执行失败,这个时候客户端一般会选择重试。

    watch命令提供了数据库键Key的监听功能,它相当于给不同的多个事务会话提供了一种监听通信的能力来察觉数据变更,但是它仅能监测已提交数据变更来保护事务完整性,却不能向MySQL那样提供不同隔离级别来察觉其他事务会话未提交或已提交数据变化。

  • watch监听某个Key状态,对其进行监听标记以确保在事务操作过程中不会被其他操作修改破坏事务完整性。

  • 严格的说Redis的命令是原子性的,而事务是非原子性的,我们要让Redis事务完全具有事务回滚的能力

  • WATCH 使得 EXEC 命令需要有条件地执行: 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。

  • EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消

  • 另外, 当客户端断开连接时, 该客户端对键的监视也会被取消

注意:Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。

Redis阻塞?

key*查询redis所有key

写入大key,导致写入删除都非常耗时

aof开启每次写入命令都把AOF缓冲区数据写入磁盘

redis大key集中过期,redis的key内部可以设置定时器,此时用的定时删除,就算此时cpu很高也会强制执行删除

分布式锁

为什么需要分布式锁?

同一个进程,多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来「互斥」,以保证共享变量的正确性

现在的业务应用通常都是微服务架构,这也意味着一个应用会部署多个进程,那这多个进程如果需要修改 MySQL 中的同一行记录时,为了避免操作乱序导致数据错误,此时,我们就需要引入「分布式锁」

分布式锁怎么实现?

想要实现分布式锁,必须借助一个外部系统,这个外部系统,必须要实现「互斥」的能力,即两个请求同时进来,只会给一个进程返回成功,另一个返回失败,这个外部系统,可以是 MySQL,也可以是 Redis 或 Zookeeper。但为了追求更好的性能,我们通常会选择使用 Redis 或 Zookeeper 来做。

SETNX key value

将 key 的值设为 value,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作。 SETNX 是SET if Not eXists的简写。如果 SETNX 返回1,说明该进程获得锁,

127.0.0.1:6379> SETNX lock 1
(integer) 1     // 客户端1,加锁成功

操作完成后,还要及时释放锁,给后来者让出操作共享资源的机会。如何释放锁呢?

127.0.0.1:6379> DEL lock // 释放锁
(integer) 1

第一个问题

1.程序处理业务逻辑异常,没及时释放锁

2.进程挂了,没机会释放锁。

    127.0.0.1:6379> SETNX lock 1    // 加锁
    (integer) 1
    127.0.0.1:6379> EXPIRE lock 10  // 10s后自动过期
    (integer) 1

解决方案给key设置过期时间

第二个问题,加锁设置过期时间两条命令,有没有可能只执行了第一条,第二条却「来不及」执行的情况发生呢

  1. SETNX 执行成功,执行 EXPIRE 时由于网络问题,异常宕机,客户端异常崩溃,,执行失败
// 采用一条命令保证原子性执行,解决问题
127.0.0.1:6379> SET lock 1 EX 10 NX
OK

第三个问题

我们评估操作共享资源的时间不准确导致的。

  1. 客户端 1 加锁成功,开始操作共享资源,但是操作的事件超过了锁的过期时间,锁被「自动释放」

  2. 客户端 2 加锁成功,开始操作共享资源

  3. 客户端 1 操作共享资源完成,释放锁(但释放的是客户端 2 的锁)

  4. 锁过期:客户端 1 操作共享资源耗时太久,导致锁被自动释放,之后被客户端 2 持有

加锁时,先设置一个过期时间,然后我们开启一个「守护线程」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就自动对锁进行「续期」,重新设置过期时间Redisson实现了这个机制,这个守护线程我们一般也把它叫做「看门狗」线程。

Redisson 是一个 Java 语言实现的 Redis SDK 客户端除此之外,这个 SDK 还封装了很多易用的功能:

  • 可重入锁 乐观锁 公平锁 读写锁 Redlock

为什么要设置uuid?

因为有可能机器宕机了,这时候即使看门狗线程续费也没用了,防止宕机以后又恢复然后又把机器2的锁释放了。

如果主机挂了从机升级为主机没有锁对象怎么办?

readlock可以用多个实例来存储共享变量,首先客户端记录当前时间,然后开始对多个实例进行加锁,然后如果加锁的时间比过期时间短就说明可以加锁成功

​ 2.释放别人的锁:客户端 1 操作共享资源完成后,却又释放了客户端 2 的锁

解决办法是:客户端在加锁时,设置一个只有自己知道的「唯一标识」进去。

例如,可以是自己的线程 ID,也可以是一个 UUID(随机且唯一),之后,在释放锁时,要先判断这把锁是否还归自己持有,

// 锁的VALUE设置为UUID
127.0.0.1:6379> SET lock $uuid EX 20 NX
OK
// 锁是自己的,才释放
if redis.get("lock") == $uuid:
    redis.del("lock")

这里释放锁使用的是 GET + DEL 两条命令,这时,又会遇到我们前面讲的原子性问题了。

客户端1get判断锁是自己的,然后客户端2在客户端1删除锁之前拿到了锁,然后客户端1释放了别人的锁

由此可见,这两个命令还是必须要原子执行才行。

怎样原子执行呢?Lua 脚本。

我们可以把这个逻辑,写成 Lua 脚本,让 Redis 来执行。因为 Redis 处理每一个请求是「单线程」执行的,在执行一个 Lua 脚本时,其它请求必须等待,直到这个 Lua 脚本处理完成,这样一来,GET + DEL 之间就不会插入其它命令了。

到这里我们再小结一下,基于 Redis 的实现分布式锁,前面遇到的问题,以及对应的解决方案:

  • 死锁:设置过期时间

  • 过期时间评估不好,锁提前过期:守护线程,自动续期

  • 锁被别人释放:锁写入唯一标识,释放锁先检查标识,再释放

  • 用lua脚本保证加锁过程的原子操作

  • redisson可以用lock方法加锁,unlock释放锁,来实现可重入锁,内部有一个计数器,唯一标识作为key,加锁次数作为value,形成一个类似map结构

    redisson内部就封装了这些我们需要注意的地方,最好使用它方便一些

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QfCikBji-1649594637322)(image-20220329101353667.png)]

如果现在有个读超高并发的系统,用Redis来抗住大部分读请求,你会怎么设计?

如果是读高并发的话,先看读并发的数量级是多少,因为Redis单机的读QPS在万级,每秒几万没问题,使用一主多从+哨兵集群的缓存架构来承载每秒10W+的读并发,主从复制,读写分离。

使用哨兵集群主要是提高缓存架构的可用性,解决单点故障问题。主库负责写,多个从库负责读,支持水平扩容,根据读请求的QPS来决定加多少个Redis从实例。如果读并发继续增加的话,只需要增加Redis从实例就行了。

如果需要缓存1T+的数据,选择Redis cluster模式,每个主节点存一部分数据,假设一个master存32G,那只需要n*32G>=1T,n个这样的master节点就可以支持1T+的海量数据的存储了。

Redis单主的瓶颈不在于读写的并发,而在于内存容量,即使是一主多从也是不能解决该问题,因为一主多从架构下,多个slave的数据和master的完全一样。假如master是10G那slave也只能存10G数据。所以数据量受单主的影响。 而这个时候又需要缓存海量数据,那就必须得有多主了,并且多个主保存的数据还不能一样。Redis官方给出的 Redis cluster 模式完美的解决了这个问题。

为什么要用 Redis?(Redis 是用来做什么的,Redis 使用场景)

1、缓存

缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力。Redis提供了键过期功能,也提供了灵活的键淘汰策略,所以,现在Redis用在缓存的场合非常多。

2、独特的数据结构可以

,基于SDS的字符串显示数据string

基于list的微信点赞,增加删除性能比较高

hash 基于key作为用户id field作为商品 value作为商品数量 形式实现的购物车商品存储比起传统的 string + json存储它对那种频繁修改的商品存储效率更高,因为是基于缓存

用set求交集

排行榜sortset

很多网站都有排行榜应用的,如京东的月度销量榜单、商品按时间的上新排行榜等。Redis提供的有序集合数据类构能实现各种复杂的排行榜应用。

5、分布式锁

在很多互联网公司中都使用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发访问,如全局ID、减库存、秒杀等场景,并发量不大的场景可以使用数据库的悲观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来控制资源的并发访问是不太理想的,大大影响了数据库的性能。可以利用Redis的setnx功能来编写分布式的锁,如果设置返回1说明获取锁成功,否则获取锁失败,实际应用中要考虑的细节要更多。

redis 在分布式中会有什么问题怎么解决(某个主节点挂掉,后面的写入读取会怎么样)

master停止复制,master脱离了网络,master宕机

哨兵选举

集群分片,投票选举

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9JAm2DNF-1649594637322)(image-20220122213247544.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XnXErulP-1649594637322)(image-20220122213337499.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZHGhMAY-1649594637323)(image-20220122213356453.png)]

redis 的访问压力也很大怎么解决

加二级缓存

熔断 限流 降级

缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。

在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

  • 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
  • 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
  • 错误:比如可用率低于90%,或者数据库连接池被打
  • 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

在你发现热key以后,把热key加载到系统的JVM中。
针对这种热key请求,会直接从jvm中取,而不会走到redis层。
假设此时有十万个针对同一个key的请求过来,如果没有本地缓存,这十万个请求就直接怼到同一台redis上了。
现在假设,你的应用层有50台机器,OK,你也有jvm缓存了。这十万个请求平均分散开来,每个机器有2000个请求,会从JVM中取到value值,然后返回数据。避免了十万个请求怼到同一台redis上的情形。

限流

通过对并发访问进行限速。最简单的方式,把多余的请求直接拒绝掉,做的高大上一些,可以根据一定的用户规则进行拒绝策略爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;

  • 1、方案一具体流程

    (1)更新数据库数据;

    (2)缓存因为种种问题删除失败;

    (3)将需要删除的key发送至消息队列;

    (4)自己消费消息,获得需要删除的key;

    (5)继续重试删除操作,直到成功。

    然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,在方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。

    2、方案二具体流程

    (1)更新数据库数据;

    (2)数据库会将操作信息写入binlog日志当中;

    (3)订阅程序提取出所需要的数据以及key;

    (4)另起一段非业务代码,获得该信息;

    (5)尝试删除缓存操作,发现删除失败;

    (6)将这些信息发送至消息队列;

    (7)重新从消息队列中获得该数据,重试操作。

    以上方案都是在业务中经常会碰到的场景,可以依据业务场景的复杂和对数据一致性的要求来选择具体的方案

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值