Redis基础面试点

2022-12-12 17:48:01

1、Redis事务     (moting)redis事务使用了multi、exec、discard、watch、unwatch命令

    Redis的事务以一个MULTI命令开始,接着将多个命令放入事务中,最后由EXEC命令将这个事务提交

    Redis客户端中存在一个事务队列,一旦MULTI开启事务后,后续的所有命令都会存放在事务队列中,直到'EXEC'命令,服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令所得的结果全部返回给客户端

   若在事务队列中存在命令性错误,则执行EXEC命令时,所有命令都不会执行

  若在事务队列中存在语法性错误,则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常。


   WATCH命令是一个乐观锁,它可以监视redis数据库中的某个键,一旦事务提交时发现监视的键被修改了,事务就会执行失败,确保事务的安全性

   Redis中的事务可以使用DISCARD命令来清空一个命令队列,并放弃对事务的执行


  事务的ACID

   原子性

    redis的事务不支持回滚,原因在于redis作者认为事务回滚这种复杂的功能和Redis追求简单高效的设计主旨不相符,并且他认为,Redis事务的执行时错误通常都是编程错误产生的,这种错误通常只会出现在开发环境中,而很少会在实际生产环境中出现,所以他认为没有必要为Redis开发事务回滚功能


   一致性

   redis能够保证事务的一致性。


   即使在服务器停机时,仍然可以借助RDB和AOF保证事务的一致性


   隔离性

    因为redis总是以单线程的方式执行命令,并且保证事务不会被中断,因此能够保证隔离性


   持久性

   redis能否保证持久性,取决于redis采用何种级别的持久化功能


2、数据类型


    String      dictEntry  dc entry


    因为 Redis 是 KV 的 dic(字典)数据结构,它是通过 hashtable 实现的(我们把这个叫做外层的哈希),所以每个键值对都会有一个 dictEntry(源码位置:dict.h),里面指向了 key 和 value 的指针。next 指向下一个 dictEntry。 key 是字符串,但是 Redis 没有直接使用 C 的字符数组,而是存储在自定义的 SDS 中。


   那到底什么是 SDS? Simple Dynamic String 简单动态字符串


    1.不用担心内存溢出问题,如果需要会对 SDS 进行扩容。

     2.获取字符串长度时间复杂度为 O(1),因为定义了 len 属性。

    3.通过“空间预分配”(sdsMakeRoomFor)和“惰性空间释放”,防止多次重分配内存。

    4.判断是否结束的标志是 len 属性(它同样以'\0'结尾是因为这样就可以使用 C 语言中函数库操作字符串的函数了),可以包含'\0'


    value 既不是直接作为字符串存储,也不是直接存储在 SDS 中,而是存储在redisObject中 。实 际 上 五 种 常 用 的 数 据 类 型 的 任 何 一 种 ,都 是 通 过redisObject 来存储的。 

    redisObjec 定义在 src/server.h 文件中


   字符串类型的内部编码 encoding 有三种:


    1.int,存储 8 个字节的长整型 long,2^63-1)。

    2.embstr, 代表 embstr 格式的 SDS(Simple Dynamic String 简单动态字符串),存储小于 44 个字节的字符串。

    3.raw,SDS 存储大于 44 个字节的字符串


应用场景

1.缓存

2.分布式 ID

3.计数器、限流 ,incrby

4.分布式锁set加 nx 参数

5.位运算


Hash 哈希


   包含键值对的无序散列表。value 只能是字符串,不能嵌套其他类型。同样是存储字符串,

   Hash 与 String 的主要区别?

   1.把所有相关的值聚集到一个 key 中,节省内存空间

   2.只使用一个 key,减少 key 冲突

  3.当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU 的消耗

 Hash 不适合的场景:

 1.Field不能单独设置过期时间

 2.没有 bit 操作


Redis 的 Hash 本身也是一个 KV 的结构,类似于 Java 中的 HashMap。外层的哈希(Redis KV的实现)只用到了 hashtable。当存储 hash 数据

类型时,我们把它叫做内层的哈希。内层的哈希底层可以使用两种数据结构实现:

ziplist:OBJ_ENCODING_ZIPLIST(压缩列表)

hashtable:OBJ_ENCODING_HT(哈希表)


 ziplist 是一个经过特殊编码的双向链表,它不存储指向上一个链表节点和指向下一个链表节点的指针,而是存储上一个节点长度和当前节点长度,通过牺牲部分读写性能,来换取高效的内存空间利用率,是一种时间换空间的思想。只用在字段个数少,字段值小的场景里面。

当 hash 对象同时满足以下两个条件的时候,

使用 ziplist 编码:

1.所有的键值对的健和值的字符串长度都小于等于 64byte(一个英文字母一个字节);

2.哈希对象保存的键值对数量小于 512 个


在 Redis 中,

hashtable 被称为字典(dictionary),它是一个数组+链表的结构。前面我们知道了,Redis 的 KV 结构是通过一个 dictEntry 来实现的。Redis 又对dictEntry 进行了多层的封装。


String 可以做的事情,Hash 都可以做!存储对象类的数据    比如对象或者一张表的数据,比 String 节省了更多 key 的空间,也更加便于集中管理购物车,还可以通过 hincrby hkey f 1加数量!


List 列表  

  存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色。

  统一用 quicklist 来存储。quicklist存储了一个双向链表,每个节点都是一个ziplist。

  quicklist(快速列表)是 ziplist 和linkedlist的结合体。 head 和 tail 指向双向列表的表头和表尾

  quicklistNode中的*zl 指向一个 ziplist,一个 ziplist 可以存放多个元素。  pre 上一个地址  tail 下一个地址


   1.用户消息时间线  因为 list 是有序的 

    2.消息队列  List 提供了两个阻塞的弹出操作:BLPOP/BRPOP,可以设置超时时间。


    BLPOP:BLPOP key1 timeout 移出并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

   BRPOP:BRPOP key1 timeout 移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

   队列:先进先出:rpush blpop,左头右尾,右边进入队列,左边出队列。

   栈:先进后出:rpush brpop


Set 集合

   String类型的无序集合,最大存储数量 2^32-1(40亿左右)。

  Redis 用 intset(整数集合)或 hashtable 存储 set。如果元素都是整数类型,就用 intset 存储。如果不是整数类型,就用 hashtable(数组+链表的存来储结构)。

   问题:KV 怎么存储 set 的元素?key 就是元素的值,value 为 null。如果元素个数超过 512 个,也会用 hashtable 存储。

   使用场景

   1.抽奖

   2.点赞、签到等

   3.交集并集关注等场景


ZSet 有序集合

 同时满足以下条件时使用 ziplist 编码:

   元素数量小于 128 个

   所有 member 的长度都小于 64 字节


  在 ziplist 的内部,按照 score 排序递增来存储。插入的时候要移动之后的数据。 在 ziplist 的内部,按照 score 排序递增来存储。插入的时候要移动之后的数据。

 使用场景

  排行榜




内存淘汰


  过期策略

   定时过期(主动淘汰)

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好,但是会占用大量的

CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。


 惰性过期(被动淘汰)

该策略就可以最大化地节省CPU资源,但是却对内存非常不友好。因为你不实时过期了,该过期删除的就可能一直堆积在内存里面!极端情况可能现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。


 定期过期

   每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。

   Redis默认会每秒进行10次(redis.conf中通过hz配置)过期扫描,扫描并不是遍历过期字典中的所有键,

   1.从过期字典中随机取出20个键

   2.删除这20个键中过期的键

   3. 如果过期键的比例超过25%,重复步骤1和2


淘汰策略

    no le v ction

   第一种淘汰策略是 noeviction,它是 Redis 的默认策略。在内存超过阀值后,Redis 不做任何清理工作,然后对所有写操作返回错误,但对读请求正常处理。noeviction 适合数据量不大的业务场景,将关键数据存入 Redis 中,将 Redis 当作 DB 来使用。


第二种淘汰策略是 volatile-lru,它对带过期时间的 key 采用最近最少访问算法来淘汰。


第三种策略是 volatile-lfu,它对带过期时间的 key 采用最近最不经常使用的算法来淘汰。

 [ˈvɑːlətl]  va letile

第四种策略是 volatile-ttl,它是对带过期时间的 key 中选择最早要过期的 key 进行淘汰。


第五种策略是 volatile-random,它是对带过期时间的 key 中随机选择 key 进行淘汰


第六种策略是 allkey-lru,它是对所有 key,而非仅仅带过期时间的 key,采用最近最久没有使用的算法来淘汰。


第七种策略是 allkeys-lfu,它也是针对所有 key 采用最近最不经常使用的算法来淘汰。


第八种策略是 allkeys-random,它是针对所有 key 进行随机算法进行淘汰。




LRU(最久未使用)   LFU(最不常用)




持久化机制


  RDB持久化   

   RDB是Redis默认的持久化方案。RDB快照(Redis DataBase),当满足一定条件的时候,会把当前内存中的数据写入磁盘,生成一个快照文件

dump.rdb。Redis重启会通过dump.rdb文件恢复数据。


1.自动触发 (1)配置规则触发

(2)shutdown触发保证服务器正常关闭,关闭的时候不会造成数据丢失。

(3)flushall,RDB文件是空的,没什么意义。


2.手动触发 

  (1) save save在生成快照的时候会阻塞当前Redis服务器,Redis不能处理其他命令。

  (2)执行bgsave时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求

一、优势

1.RDB是一个非常紧凑(compact类型)的文件,它保存了redis在某个时间点上的数据集

。这种文件非常适合用于进行备份和灾难恢复。

2.生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作

,主进程不需要进行任何磁盘IO操作

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

。二、劣势

1.RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,频繁执行成本过高

2.在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失

最后一次快照之后的所有修改(数据有丢失)。


AOF持久化  

Append Only File的简称,只加载文件,这个方案在redis默认是不开启的。

AOF采用日志的形式来记录每个写操作,并追加到文件中。开启后

,执行更改Redis数据的命令时,就会把命令写入到AOF文件中。Redis重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复工作。



、优点能最大限度的保证数据安全,就算用默认的配置everysec

,也最多只会造成1s的数据丢失

、缺点数据量比RDB要大很多,所以性能没有RDB好,没有一个性能保证!