redis通关面试宝典

文章目录

      • 1.redis基本的数据结构有哪些
      • 3.布隆过滤器实现的原理和使用场景(高级数据类型)
        • 4.1 watch乐观锁(对key变化监视)
        • 4.2 分布式锁(悲观锁,对数据value监视)
        • 5.1redis集群的主从复制模型是怎样?
        • 5.2 redis 主从复制的核心原理?
        • 5.3 主从复制会存在哪些问题呢?
      • 6.Redis消息队列的几种模式
        • 6.1 基于List的 LPUSH+BRPOP 的实现
        • 6.2 PUB/SUB,订阅/发布模式
        • 6.3 基于Sorted-Set的实现
        • 6.4基于Stream类型的实现
      • 8.消息队列如何保证可靠消息传输,如何设计消息队列
      • 10.redis集群怎么搭建的
      • 20.缓存预热,缓存雪崩,缓存击穿,缓存穿透
      • 21.为什么要用 redis/为什么要用缓存
      • 22.redis 和 memcached 的区别
      • 24.redis这么快,为什么是单线程的?
      • 25.redis是单线程的,为什么还这么快
        • 2.redis事务的三个阶段和相关命令
        • 4.redis事务支持ACID吗?支持回滚吗
        • 27.1 redis的过期键的删除策略
        • 27.2 redis的内存淘汰策略
      • 28. redis缓存和数据库双写一致性问题
      • 29.多个系统同时操作Redis带来的数据问题?(并发竞争key)
        • 30.2 springboot配置文件 哨兵
      • 32. SpringBoot整合redis

1.redis基本的数据结构有哪些

数据类型的大小

数据类型 最大存储数据量
key(key是字符串) 512M
string 512M
hash 2^32-1
list 2^32-1
set 2^32-1
sorted set
bitmap 512M
hyperloglog 12K
geo

异常快:每秒大概有110000次的设置(set),每秒大概有81000次的读取(get),速度很快。

支持丰富的数据类型:支持字符串、集合、链表、排序等特性的数据结构。一般redis很容易被解决各种问题。

操作具有原子性:所有Redis操作都是原子操作,这确保两个或多个客户端并发访问时,Redis服务器能接收到更新的值。

多实用工具:Redis是一个多实用工具,用于缓存、消息堆栈。

2.五种基本数据类型:

Redis有5种基础数据结构,它们分别是: string(字符串) , list(列表) , hash(字典)set(集合)  zset(有序集合)

1)字符串: Redis中的字符串是一个 动态字符串 ,意味着用户可以修改,它的实现就有点类似 java中的ArrayList 。Redis将同一个结构用泛型定义了很多次。 以key +value存

常用命令(大小写都行): set , get , EXISTS key , DEL key , mget , mset , incr , decr

应用场景: 1.缓存 :把常用信息,字符串,图片或者验证码放到redis中,Redis作为缓存层,mysql做持久化层,降低mysql数据读写压力。 2.计数器 :redis能够保持原子性操作。可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其他存储介质永久存储。 3.共享用户Session :用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存 Cookie ,但是可以利用 Redis 将用户的 Session 集中管理,在这种模式只需要保证 Redis 的高可用,每次用户 Session 的更新和获取都可以快速完成。大大提高效率。

2)list(列表清单): Redis的列表相当于Java语言中的 LinkedList ,注意它是链表而不是数组。这意味着list的插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n))。可以队列规则也可以栈规则。它就是 有序可重复 的,存进去什么样取出来还是什么样。以key+列表

常用命令: lpush , rpush , lpop , rpop , lrange mylist 0 -1 取第一个元素到最后一个元素 ,llen key, lindex key index

应用场景: 1.文章列表或者数据分页展示的应用: 例如微博的时间轴,有人发布微博,用 lpush 加入时间轴,展示新的列表信息。每次都以原先添加的顺序访问数据。2. 消息队列:Redis 的链表结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过 Lpush 命令从左边插入数据,多个数据消费者,可以使用 BRpop 命令阻塞的“抢”列表尾部的数据。3.

3)Hash(字典哈希):这个Redis中的字典Hash相当于 java中的HashMap ,内部实现也差不多,都是通过"数组+链表"的链地址法解决哈希冲突。 无序不可重复 ,以 key+属性field+value 。

常用命令: hget , hset , hdel , hlen , hkeys , hvals , hincrby , hdecrby

应用场景:比如客户id+购买商品id+购买数量

4)set(集合集):Redis的集合相当于Java语言中的 HashSet ,它内部的键值对是 无序不可重复 的。它的内部实现相当于一个特殊的字典,字典中所有的值都是一个值NULL。 以key+属性field(相当于value)

常用命令: sadd , spop , scard , smembers , srem移除 , sismember

场景:点赞,或点踩,收藏等,可以放到set中实现

5)有序列表zset:相当于java中的 SortedSet ,保证了 无序不可重复 ,但是 取出来的元素是排序好的 ,以 key+value+排序score

常用命令: zadd , zrange , zrevrange , zcard计数 , zcore指定 value 的 score , zrank排名

场景: 1.排行榜:有序集合经典使用场景 。例如小说视频等网站需要对用户上传的小说视频做排行榜 ,榜单可以按照用户关注数,更新时间,字数等打分,做排行。 2.用Sorted Sets来做带权重的队列 ,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

2. Redis高级类型

1.Bitmap(位图数据结构): 数据以0和1存储 ,通常设1是有,0是无,这样就可以过滤掉0的数据,和 布隆过滤器 (BloomFilter)差不多。

常用命令:

  • 设置指定key对应 偏移量offset (相当于hash(key)与数组-1位运算得到的索引值)上的bit值, value只能是1或0 setbit key offset value
  • 获取指定key对应偏移量上的bit值 getbit key offset
  • 对指定key按位进行交、并、非、异或操作,并将结果保存到destKey中 bitop or destKey key1 [key2...]
    • or处还可以填 and交 not非 xor异或
  • 统计指定key中1的数量 bitcount key [start end]

2.HyperLogLog: 就是 去除重复的数据 ,比较适合用来做大规模数据的去重统计。

常用命令:

pfadd key element [element ...]
pfcount key [key ...]
pfmerge destkey sourcekey [sourcekey...]

3.Geo:就是获取坐标点的位置和计算两点之间的距离。增加了地理位置 GEO 模块

常用命令:

  • 添加坐标点: geoadd key longitude latitude member [longitude latitude member ...]

    georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]

  • 获取经纬度坐标点: geopos key member [member ...] georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]

  • 计算坐标点距离: geodist key member1 member2 [unit] geohash key member [member ...]

3.布隆过滤器实现的原理和使用场景(高级数据类型)

布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数(哈希函数)。布隆过滤器可以用与检索一个元素是否在一个集合中。它的优点就是 空间效率和查询时间都超过一般算法 ,缺点是 有一定的误判率和删除困难 。

3.1. 原理

当一个元素被加入集合时,通过K个哈希散列函数将这个元素映射成一个位数组中的K个点(就是通过K次哈希函数和数组运算得出K个索引值(偏移量) ),并把数组中K个索引处置为1。检索时,我们只要看这些点的数组是不是1,如果是1,(大概率范围)默认集合中有该元素了(因为通过Hash算法的哈希值与数组长度相关运算,得出数组的索引值,和哈希表一样,只能说索引值相同,但是数组存储的元素不一定相同,所以说 存在误判率 )。如果K个点中只要任何一个为0,则检测元素一定不在数组中;如果都是1,则检测元素很可能在。

布隆过滤器跟单哈希函数BitMap不同之处在于:Bloom Filter使用了k个哈希函数,就是需要k次哈希运算,每个字符串跟k个bit对应,降低了冲突的概率;但是BitMap是一个单一的哈希函数,只用一次哈希运算,即只能得出一个索引值(偏移量),放一个value(0或1)。

3.2. 案例

布隆过滤器是一个 bit 向量或者说 bit 数组,长这样:

如果添加一个字符串"baidu",那么这个字符串会和K个bit对应,图中"baidu"和3个不同的索引值对应,通过3次哈希函数和数组相关运算得出索引值,即偏移量为1,4,7,把这些位置置为1,则添加成功。(说明:检索“baidu”,需要索引值1、4、7均为1才行,这还只能是大概率说明存在该字符串,只要其中任何一个为0说明该元素不存在)

再存一个"tencent",得出3、4、8, 发现它在某次哈希算法的哈希值与数组位运算时得出的索引值也为4。

3.3 Bloom Filter的缺点

  • 存在误判率:之前的哈希算法已经解释。如果bloom filter中存储的是黑名单,那么可以通过建立一个 白名单来存储可能会误判的元素。
  • 删除元素困难。因为一个元素对应K个bit位的数组值1,删除的时候不能把这K个索引处都置为0,因为可能存在其他元素的索引值也含有这些索引位(如上述案例’tencent’和’baidu’都含有索引值4),贸然删除,可能导致存储的其他元素也失效了。

3.4. Bloom Filter 实现

k 为哈希函数个数,m 为布隆过滤器长度,n 为插入的元素个数,p 为误报率。

在实现布隆过滤器的时候,需要考虑 1)数据量n和设定误判率; 2)Hash函数的选取和bit数组的大小

(1)Bit数组大小选择:根据数据量n,误判率fpp计算bit数组大小。

(2)哈希函数选择K多少个:预估数据量n以及bit数组长度m,可以得到一个hash函数的个数k。

一般过小的Bloom Filter,放入很多字符串时,很快数组的位都置为1了,那么查询任何值都会返回“可能存在”,起不到过滤的目的了。 一般过滤器越长,误判率就越小 。另外哈希函数的个数K,个数越多,则置为1的速度就越快,但是效率就变低;如果太少,误判率就越高。

3.5 使用场景

一般在缓存穿透下,使用较多,可以过滤掉大部分无效请求.

4.redis锁

4.1 watch乐观锁(对key变化监视)

业务场景:天猫双11热卖过程中,对已经售罄的货物追加补货, 4个业务员都有权限进行补货。补货的

操作可能是一系列的操作(即使事务有隔离性,最后补货还是最开始的那个人,其他业务员补货会被隔离,但是他们都执行补货这个动作了,虽然结果不影响,但是多做了3次无用功的补货动作,耗费性能),牵扯到多个连续操作,如何保障不会重复操作? 比如A补货了,B、C、D就都不再补货。

业务分析:多个客户端有可能同时操作同一组数据,并且该数据一旦被操作修改后,将不适用于继续操

作 在操作之前锁定要操作的数据,一旦发生变化,终止当前操作 。

解决方法: watch 对 key 添加监视锁,在执行exec前如果key发生了变化,终止事务执行 。

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

用无参数的 unwatch 命令可以手动取消对所

有键的监视。

watch , unwatch 指令都是放在multi指令前,即事务之前加锁和释放锁 ,它假设数据访问时没有其他客户端的写操作,等到提交执行事务时才会检测是否有写操作,如果有写操作则终止事务,体现一种乐观的思想。

加不加锁和并发有关系,它是指多个线程在同一时间对同一个数据的并发访问,key变化则终止事务:

127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> set age 13
OK
127.0.0.1:6379> watch name -- watch要在multi之前监控,不能在事务中watch
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set aa bb
QUEUED
127.0.0.1:6379> exec -- 执行前让其他事务修改 set name lisi,那么该事务就不会再
执行
(nil)
127.0.0.1:6379> unwatch -- 释放对所有锁的监控
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set aa bb
QUEUED
127.0.0.1:6379> exec
1) OK
127.0.0.1:6379>

说明:redis的锁和隔离性没有任何关系!!!!

redis的隔离性是开启了事务 multi 之后,在事务之内的指令的执行,它会先把这些指令放入事务队列,在执行事务 exec 时,会按照添加队列的顺序依次执行这些指令,中途不会被中断、干扰。与其他客户端请求隔离,即使其他客户端执行写操作,也会无视操作。

那就提出一个问题: 既然事务有了隔离性,最终结果不会变化,为什么还要加监视器锁watch监视key的变化呢?

[就拿之前的业务员补货问题,即使事务有隔离性,最后补货还是最开始的那个人,其他业务员补货会被隔离,但是他们都执行补货这个动作了,虽然结果不影响,但是多做了3次无用功的补货动作,耗费性能。你说重复劳动有必要么。]

4.2 分布式锁(悲观锁,对数据value监视)

业务场景:天猫双11热卖过程中,对已经售罄的货物追加补货,且补货完成。客户购买热情高涨, 3秒

内将所有商品购买完毕。本次补货已经将库存全部清空,如何避免最后一件商品不被多人同时购买?

【超卖问题】就是说如果只剩一件商品,但是有5个人要买,如何保证不被超卖???

业务分析: 使用watch监控一个key有没有改变已经不能解决问题 ,此处要 监控的是具体数据 ,我们要

监控的是商品的数量什么时候到 1 ,这个商品的数量是一直变化的,不可能别每次变化,都放弃执行。

虽然redis是单线程的,但是多个客户端对同一数据同时进行操作时,如何避免不被同时修改?

解决方案:

使用 setnx 设置一个公共锁: setnx key value ,这个value设置任意值都行,无关紧要;操作完毕通过 del key 操作释放锁 。

利用setnx命令的返回值特征,有值则返回设置失败,无值则返回设置成功

  • 对于返回设置成功的,拥有控制权,进行下一步的具体业务操作
  • 对于返回设置失败的,不具有控制权,排队或等待

操作完毕通过del操作释放锁

127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> setnx num 1 -- 加锁
(integer) 1
127.0.0.1:6379> incrby num -1 ---当前加锁客户端内可以继续操作
(integer) 9
127.0.0.1:6379> del num -- 释放锁
(integer) 1
--------------------------------
127.0.0.1:6379> setnx num 1 -- 当前客户端加锁,在加锁客户端内可以继续操作
(integer) 1
127.0.0.1:6379> setnx num 1 -- 其他客户端获取不到锁
(integer) 0

**死锁:**如果加了锁,但是没有释放,就会导致死锁,其他下客户端一直获取不到锁。

使用 expire 为锁 key 添加时间限定 ,到时不释放,放弃锁。 由于操作通常都是微秒或毫秒级,因此该锁

定时间不宜设置过大。具体时间需要业务测试后确认。

指令:加锁时间 expire key seconds ; pexpire key milliseconds

127.0.0.1:6379> set name 123
OK
127.0.0.1:6379> setnx name 1
(integer) 1
127.0.0.1:6379> expire name 20 -- 使用expire为锁key添加时间限定
(integer) 1
127.0.0.1:6379> get name
"123

5.redis主从复制

5.1redis集群的主从复制模型是怎样?

master : 写数据,执行写操作时,将出现变化的数据自动同步到slave 。

slave :读数据 。

主从复制:是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后

者称为从节点(slave), 数据的复制是单向的,只能由主节点到从节点 。默认情况下,每台Redis服务器

都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点 。

高可用集群:

主从复制的作用:

① 数据冗余,实现数据的热备份

② 故障恢复,避免单点故障带来的服务不可用读写分离,

③ 负载均衡,主节点负载读写,从节点负责读,提高服务器并发量

④ 高可用基础,是哨兵机制和集群实现的基础

5.2 redis 主从复制的核心原理?

主从复制是将主机的数据同步到从机上,即主从复制仅是数据同步,但是不能解决故障迁移(主机宕机,从机无法提供服务)

  • 主从复制过程大体可以分为3个阶段

    • 建立连接阶段(即准备阶段)
    • 数据同步阶段
    • 命令传播阶段

5.2.1 建立连接阶段

建立slave到master的连接,使master能够识别slave, 并保存slave端口号 。

5.2.2 数据同步阶段

(1) 在 slave 初次连接 master 后,复制 master 中的所有数据到 slave。

(2) 将 slave 的数据库状态更新成master当前的数据库状态 。

5.2.3 命令传播阶段

当上一步执行完数据同步后,之后每一次主服务器master每执行一次写操作,就需要向从服务器slaver同步该命令,保证主从一致。这就是 命令传播阶段 。

(1) 当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一

致的 状态, 同步数据一致的动作 称为 命令传播 .

(2) master将接收到的数据变更命令发送给slave, slave接收命令后执行命令 。

命令传播阶段出现了断网现象: 网络闪断闪连、 短时间网络中断、 长时间网络中断

部分复制的三个核心要素: 服务器的运行 id、 主服务器的复制积压缓冲区 、 主从服务器的复制偏移量

服务器的运行id:运行id被用于在服务器间进行传输,识别身份,如果想两次操作均对同一台服务器进

行,必须每次操作携带对应的运行id,用于对方识别。

复制缓冲区:当master接收到主客户端的指令时,除了将指令执行,会将该指令存储到缓冲区中 ,是

一个先进先出( FIFO)的队列, 用于存储服务器执行过的命令, 每次传播命令, master都会将传播

的命令记录下来, 并存储在复制缓冲区 。

工作原理:

  • 通过offset区分不同的slave当前数据传播的差异.

  • master记录已发送的信息对应的offset

  • slave记录已接收的信息对应的offset

主从服务器的复制偏移量:

master复制偏移量:记录发送给所有slave的指令字节对应的位置(多个)

slave复制偏移量:记录slave接收master发送过来的指令字节对应的位置(一个)

master端:发送一次记录一次

slave端:接收一次记录一次

作用:同步信息,比对master与slave的差异,当slave断线后,再次启动可以恢复数据使用 。

5.2.4 redis主从复制的工作流程

主要包括: 全量复制、部分复制、命令传播

​ 1.如果设置了一个 slave ,无论是第一次连接还是重连到 master ,他都会发出一个 psync2 命令;

  1. master 收到 psync2 命令之后,会做两件事: a) master 执行 bgsave ,即在后台保存数据到磁盘(rdb快照文件,因为恢复速度比较快); b) master` 同时将新收到的写入和修改数据集的命令存入缓冲区(非查询类);
  2. master 在后台把数据保存到快照文件完成之后, master 会把这个快照文件传送给 slave,而
    slave 则把内存清空后,加载该文件到内存中恢复数据; 到这一步是全量复制(RD
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值