关于redis:Redis我这21个灵魂拷问你拿什么挡

2次阅读

共计 9513 个字符,预计需要花费 24 分钟才能阅读完成。

前言

欢送各位进群 973961276 一起聊聊技术吹吹牛,每周都会有几次抽奖送专业书籍的流动,奖品虽不甚值钱,但也可搏个彩头

1. 什么是 redis?

Redis 是一个基于内存的高性能 key-value 数据库。

2.Reids 的特点

Redis 实质上是一个 Key-Value 类型的内存数据库,很像 memcached,整个数据库通通加载在内存当中进行操作,定期通过异步操作把数据库数据 flush 到硬盘上进行保留。因为是纯内存操作,Redis 的性能十分杰出,每秒能够解决超过 10 万次读写操作,是已知性能最快的 Key-Value DB。

Redis 的杰出之处不仅仅是性能,Redis 最大的魅力是反对保留多种数据结构,此外单个 value 的最大限度是 1GB,不像 memcached 只能保留 1MB 的数据,因而 Redis 能够用来实现很多有用的性能,比方说用他的 List 来做 FIFO 双向链表,实现一个轻量级的高性 能音讯队列服务,用他的 Set 能够做高性能的 tag 零碎等等。另外 Redis 也能够对存入的 Key-Value 设置 expire 工夫,因而也能够被当作一 个性能加强版的 memcached 来用。

Redis 的次要毛病是数据库容量受到物理内存的限度,不能用作海量数据的高性能读写,因而 Redis 适宜的场景次要局限在较小数据量的高性能操作和运算上。


零根底和大三大四的敌人看这里 >>c/c++ 企业级我的项目实战

曾经工作了想持续自我晋升跳槽涨薪的工程师看这里 >>c/c++ linux 服务器高级架构师学习

3. 应用 redis 有哪些益处?

1. 速度快,因为数据存在内存中,相似于 HashMap,HashMap 的劣势就是查找和操作的工夫复杂度都是 O(1)

2. 反对丰盛数据类型,反对 string,list,set,sorted set,hash

1)String

常用命令:set/get/decr/incr/mget 等;

利用场景:String 是最罕用的一种数据类型,一般的 key/value 存储都能够归为此类;

实现形式:String 在 redis 外部存储默认就是一个字符串,被 redisObject 所援用,当遇到 incr、decr 等操作时会转成数值型进行计算,此时 redisObject 的 encoding 字段为 int。

2)Hash

常用命令:hget/hset/hgetall 等

利用场景:咱们要存储一个用户信息对象数据,其中包含用户 ID、用户姓名、年龄和生日,通过用户 ID 咱们心愿获取该用户的姓名或者年龄或者生日;

实现形式:Redis 的 Hash 理论是外部存储的 Value 为一个 HashMap,并提供了直接存取这个 Map 成员的接口。Key 是用户 ID, value 是一个 Map。这个 Map 的 key 是成员的属性名,value 是属性值。这样对数据的批改和存取都能够间接通过其外部 Map 的 Key(Redis 里称外部 Map 的 key 为 field), 也就是通过 key(用户 ID) + field(属性标签) 就能够操作对应属性数据。

以后 HashMap 的实现有两种形式:当 HashMap 的成员比拟少时 Redis 为了节俭内存会采纳相似一维数组的形式来紧凑存储,而不会采纳真正的 HashMap 构造,这时对应的 value 的 redisObject 的 encoding 为 zipmap,当成员数量增大时会主动转成真正的 HashMap, 此时 encoding 为 ht。

3)List

常用命令:lpush/rpush/lpop/rpop/lrange 等;

利用场景:Redis list 的利用场景十分多,也是 Redis 最重要的数据结构之一,比方 twitter 的关注列表,粉丝列表等都能够用 Redis 的 list 构造来实现;

实现形式:Redis list 的实现为一个双向链表,即能够反对反向查找和遍历,更不便操作,不过带来了局部额定的内存开销,Redis 外部的很多实现,包含发送缓冲队列等也都是用的这个数据结构。

4)Set

常用命令:sadd/spop/smembers/sunion 等;

利用场景:Redis set 对外提供的性能与 list 相似是一个列表的性能,非凡之处在于 set 是能够主动排重的,当你须要存储一个列表数据,又不心愿呈现反复数据时,set 是一个很好的抉择,并且 set 提供了判断某个成员是否在一个 set 汇合内的重要接口,这个也是 list 所不能提供的;

实现形式:set 的外部实现是一个 value 永远为 null 的 HashMap,理论就是通过计算 hash 的形式来疾速排重的,这也是 set 能提供判断一个成员是否在汇合内的起因。

5)Sorted Set

常用命令:zadd/zrange/zrem/zcard 等;

利用场景:Redis sorted set 的应用场景与 set 相似,区别是 set 不是主动有序的,而 sorted set 能够通过用户额定提供一个优先级 (score) 的参数来为成员排序,并且是插入有序的,即主动排序。当你须要一个有序的并且不反复的汇合列表,那么能够抉择 sorted set 数据结构,比方 twitter 的 public timeline 能够以发表工夫作为 score 来存储,这样获取时就是主动按工夫排好序的。

实现形式:Redis sorted set 的外部应用 HashMap 和跳跃表 (SkipList) 来保证数据的存储和有序,HashMap 里放的是成员到 score 的映射,而跳跃表里寄存的是所有的成员,排序根据是 HashMap 里存的 score, 应用跳跃表的构造能够取得比拟高的查找效率,并且在实现上比较简单。

3. 反对事务,操作都是原子性,所谓的原子性就是对数据的更改要么全副执行,要么全副不执行

4. 丰盛的个性:可用于缓存,音讯,按 key 设置过期工夫,过期后将会主动删除

4.redis 相比 memcached 有哪些劣势?

  • memcached 所有的值均是简略的字符串,redis 作为其替代者,反对更为丰盛的数据类型
  • redis 的速度比 memcached 快很多 (3) redis 能够长久化其数据

5.Memcache 与 Redis 的区别都有哪些?

  • 存储形式 Memecache 把数据全副存在内存之中,断电后会挂掉,数据不能超过内存大小。Redis 有部份存在硬盘上,这样能保证数据的持久性。
  • 数据反对类型 Memcache 对数据类型反对绝对简略。Redis 有简单的数据类型。
  • 应用底层模型不同 它们之间底层实现形式 以及与客户端之间通信的利用协定不一样。Redis 间接本人构建了 VM 机制,因为个别的零碎调用零碎函数的话,会节约肯定的工夫去挪动和申请。

6.redis 实用于的场景?

Redis 最适宜所有数据 in-momory 的场景,如:

1. 会话缓存(Session Cache)

最罕用的一种应用 Redis 的情景是会话缓存(session cache)。用 Redis 缓存会话比其余存储(如 Memcached)的劣势在于:Redis 提供长久化。

2. 全页缓存(FPC)

除根本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题,即便重启了 Redis 实例,因为有磁盘的长久化,用户也不会看到页面加载速度的降落,这是一个极大改良,相似 PHP 本地 FPC。

3. 队列

Reids 在内存存储引擎畛域的一大长处是提供 list 和 set 操作,这使得 Redis 能作为一个很好的音讯队列平台来应用。Redis 作为队列应用的操作,就相似于本地程序语言(如 Python)对 list 的 push/pop 操作。

如果你疾速的在 Google 中搜寻“Redis queues”,你马上就能找到大量的开源我的项目,这些我的项目的目标就是利用 Redis 创立十分好的后端工具,以满足各种队列需要。例如,Celery 有一个后盾就是应用 Redis 作为 broker,你能够从这里去查看。

4. 排行榜 / 计数器

Redis 在内存中对数字进行递增或递加的操作实现的十分好。汇合(Set)和有序汇合(Sorted Set)也使得咱们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。所以,咱们要从排序汇合中获取到排名最靠前的 10 个用户–咱们称之为“user_scores”,咱们只须要像上面一样执行即可:

当然,这是假设你是依据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你须要这样执行:

ZRANGE user_scores 0 10 WITHSCORES 

Agora Games 就是一个很好的例子,用 Ruby 实现的,它的排行榜就是应用 Redis 来存储数据的,你能够在这里看到。

5. 公布 / 订阅

最初(但必定不是最不重要的)是 Redis 的公布 / 订阅性能。公布 / 订阅的应用场景的确十分多。举荐浏览:Redis 的 8 大利用场景。

7、redis 的缓存生效策略和主键生效机制

作为缓存零碎都要定期清理有效数据,就须要一个主键生效和淘汰策略.

在 Redis 当中,有生存期的 key 被称为 volatile。在创立缓存时,要为给定的 key 设置生存期,当 key 过期的时候(生存期为 0),它可能会被删除。

1、影响生存工夫的一些操作

生存工夫能够通过应用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令笼罩原来的数据,也就是说,批改 key 对应的 value 和应用另外雷同的 key 和 value 来笼罩当前,以后数据的生存工夫不同。

比如说,对一个 key 执行 INCR 命令,对一个列表进行 LPUSH 命令,或者对一个哈希表执行 HSET 命令,这类操作都不会批改 key 自身的生存工夫。另一方面,如果应用 RENAME 对一个 key 进行改名,那么改名后的 key 的生存工夫和改名前一样。

RENAME 命令的另一种可能是,尝试将一个带生存工夫的 key 改名成另一个带生存工夫的 another_key,这时旧的 another_key (以及它的生存工夫)会被删除,而后旧的 key 会改名为 another_key,因而,新的 another_key 的生存工夫也和本来的 key 一样。应用 PERSIST 命令能够在不删除 key 的状况下,移除 key 的生存工夫,让 key 从新成为一个 persistent key。

2、如何更新生存工夫

能够对一个曾经带有生存工夫的 key 执行 EXPIRE 命令,新指定的生存工夫会取代旧的生存工夫。过期工夫的精度曾经被管制在 1ms 之内,主键生效的工夫复杂度是 O(1),EXPIRE 和 TTL 命令搭配应用,TTL 能够查看 key 的以后生存工夫。设置胜利返回 1;当 key 不存在或者不能为 key 设置生存工夫时,返回 0。

最大缓存配置,在 redis 中,容许用户设置最大应用内存大小

server.maxmemory默认为 0,没有指定最大缓存,如果有新的数据增加,超过最大内存,则会使 redis 解体,所以肯定要设置。redis 内存数据集大小回升到肯定大小的时候,就会履行数据淘汰策略。

redis 提供 6 种数据淘汰策略:

  • 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(驱赶):禁止驱赶数据

留神这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期工夫的数据集淘汰数据还是从全副数据集淘汰数据,前面的 lru、ttl 以及 random 是三种不同的淘汰策略,再加上一种 no-enviction 永不回收的策略。

应用策略规定:

  • 如果数据出现幂律散布,也就是一部分数据拜访频率高,一部分数据拜访频率低,则应用 allkeys-lru
  • 如果数据出现平等散布,也就是所有的数据拜访频率都雷同,则应用 allkeys-random

三种数据淘汰策略:

ttl 和 random 比拟容易了解,实现也会比较简单。次要是 Lru 最近起码应用淘汰策略,设计上会对 key 按生效工夫排序,而后取最先生效的 key 进行淘汰

8. 为什么 redis 须要把所有数据放到内存中?

Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的形式将数据写入磁盘。所以 redis 具备疾速和数据长久化的特色。如果不将数据放在内存中,磁盘 I / O 速度为重大影响 redis 的性能。在内存越来越便宜的明天,redis 将会越来越受欢迎。

如果设置了最大应用的内存,则数据已有记录数达到内存限值后不能持续插入新值。

9.Redis 是单过程单线程的

redis 利用队列技术将并发拜访变为串行拜访,打消了传统数据库串行管制的开销

10.redis 的并发竞争问题如何解决?

Redis 为单过程单线程模式,采纳队列模式将并发拜访变为串行拜访。Redis 自身没有锁的概念,Redis 对于多个客户端连贯并不存在竞争,然而在 Jedis 客户端对 Redis 进行并发拜访时会产生连贯超时、数据转换谬误、阻塞、客户端敞开连贯等问题,这些问题均是因为客户端连贯凌乱造成。对此有 2 种解决办法:

  1. 客户端角度,为保障每个客户端间失常有序与 Redis 进行通信,对连贯进行池化,同时对客户端读写 Redis 操作采纳外部锁 synchronized。
  2. 服务器角度,利用 setnx 实现锁。

注:对于第一种,须要应用程序本人解决资源的同步,能够应用的办法比拟艰深,能够应用 synchronized 也能够应用 lock;第二种须要用到 Redis 的 setnx 命令,然而须要留神一些问题。

11、redis 常见性能问题和解决方案:

1.Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比拟大时对性能影响是十分大的,会间断性暂停服务,所以 Master 最好不要写内存快照。

2.Master AOF 长久化,如果不重写 AOF 文件,这个长久化形式对性能的影响是最小的,然而 AOF 文件会一直增大,AOF 文件过大会影响 Master 重启的复原速度。Master 最好不要做任何长久化工作,包含内存快照和 AOF 日志文件,特地是不要启用内存快照做长久化, 如果数据比拟要害,某个 Slave 开启 AOF 备份数据,策略为每秒同步一次。

3.Master 调用 BGREWRITEAOF 重写 AOF 文件,AOF 在重写的时候会占大量的 CPU 和内存资源,导致服务 load 过高,呈现短暂服务暂停景象。

4.Redis 主从复制的性能问题,为了主从复制的速度和连贯的稳定性,Slave 和 Master 最好在同一个局域网内。

12.redis 事物的理解 CAS(check-and-set 操作实现乐观锁)?

和泛滥其它数据库一样,Redis 作为 NoSQL 数据库也同样提供了事务机制。在 Redis 中,MULTI/EXEC/DISCARD/WATCH 这四个命令是咱们实现事务的基石。置信对有关系型数据库开发教训的开发者而言这一概念并不生疏,即便如此,咱们还是会简要的列出 Redis 中事务的实现特色:

1). 在事务中的所有命令都将会被串行化的程序执行,事务执行期间,Redis 不会再为其它客户端的申请提供任何服务,从而保障了事物中的所有命令被原子的执行。

2). 和关系型数据库中的事务相比,在 Redis 事务中如果有某一条命令执行失败,其后的命令依然会被继续执行。

3). 咱们能够通过 MULTI 命令开启一个事务,有关系型数据库开发教训的人能够将其了解为 ”BEGIN TRANSACTION” 语句。在该语句之后执行的命令都将被视为事务之内的操作,最初咱们能够通过执行 EXEC/DISCARD 命令来提交 / 回滚该事务内的所有操作。这两个 Redis 命令可被视为等同于关系型数据库中的 COMMIT/ROLLBACK 语句。

4). 在事务开启之前,如果客户端与服务器之间呈现通信故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是产生在客户端执行 EXEC 命令之后,那么该事务中的所有命令都会被服务器执行。

5). 当应用 Append-Only 模式时,Redis 会通过调用零碎函数 write 将该事务内的所有写操作在本次调用中全副写入磁盘。然而如果在写入的过程中呈现零碎解体,如电源故障导致的宕机,那么此时兴许只有局部数据被写入到磁盘,而另外一部分数据却曾经失落。Redis 服务器会在重新启动时执行一系列必要的一致性检测,一旦发现相似问题,就会立刻退出并给出相应的谬误提醒。此时,咱们就要充分利用 Redis 工具包中提供的 redis-check-aof 工具,该工具能够帮忙咱们定位到数据不统一的谬误,并将曾经写入的局部数据进行回滚。修复之后咱们就能够再次重新启动 Redis 服务器了。

13.WATCH 命令和基于 CAS 的乐观锁?

在 Redis 的事务中,WATCH 命令可用于提供 CAS(check-and-set)性能。假如咱们通过 WATCH 命令在事务执行之前监控了多个 Keys,假使在 WATCH 之后有任何 Key 的值产生了变动,EXEC 命令执行的事务都将被放弃,同时返回 Null multi-bulk 应答以告诉调用者事务执行失败。例如,咱们再次假如 Redis 中并未提供 incr 命令来实现键值的原子性递增,如果要实现该性能,咱们只能自行编写相应的代码。

其伪码如下:

val = GET mykey  
val = val + 1  
SET mykey $val 

以上代码只有在单连贯的状况下才能够保障执行后果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会呈现多线程程序中经常出现的一种谬误场景 – 竞态争用(race condition)。

比方,客户端 A 和 B 都在同一时刻读取了 mykey 的原有值,假如该值为 10,尔后两个客户端又均将该值加一后 set 回 Redis 服务器,这样就会导致 mykey 的后果为 11,而不是咱们认为的 12。为了解决相似的问题,咱们须要借助 WATCH 命令的帮忙,见如下代码:

WATCH mykey  
val = GET mykey  
val = val + 1  
MULTI  
SET mykey $val  
EXEC 

和此前代码不同的是,新代码在获取 mykey 的值之前先通过 WATCH 命令监控了该键,尔后又将 set 命令突围在事务中,这样就能够无效的保障每个连贯在执行 EXEC 之前,如果以后连贯获取的 mykey 的值被其它连贯的客户端批改,那么以后连贯的 EXEC 命令将执行失败。这样调用者在判断返回值后就能够获悉 val 是否被从新设置胜利。

14. 应用过 Redis 分布式锁么,它是什么回事?

先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期工夫避免锁遗记了开释。

这时候对方会通知你说你答复得不错,而后接着问如果在 setnx 之后执行 expire 之前过程意外 crash 或者要重启保护了,那会怎么样?

这时候你要给予诧异的反馈:唉,是喔,这个锁就永远得不到开释了。紧接着你须要抓一抓本人得脑袋,故作思考片刻,如同接下来的后果是你被动思考进去的,而后答复:我记得 set 指令有非常复杂的参数,这个应该是能够同时把 setnx 和 expire 合成一条指令来用的!对方这时会露出笑容,心里开始默念:摁,这小子还不错。

15. 如果 Redis 外面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀结尾的,如果将它们全副找进去?

应用 keys 指令能够扫出指定模式的 key 列表。

对方接着诘问:如果这个 redis 正在给线上的业务提供服务,那应用 keys 指令会有什么问题?

这个时候你要答复 redis 要害的一个个性:redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会进展,直到指令执行结束,服务能力复原。这个时候能够应用 scan 指令,scan 指令能够无阻塞的提取出指定模式的 key 列表,然而会有肯定的反复概率,在客户端做一次去重就能够了,然而整体所破费的工夫会比间接用 keys 指令长。

16. 应用过 Redis 做异步队列么,你是怎么用的?

个别应用 list 构造作为队列,rpush 生产音讯,lpop 生产音讯。当 lpop 没有音讯的时候,要适当 sleep 一会再重试。

如果对方诘问可不可以不必 sleep 呢?list 还有个指令叫 blpop,在没有音讯的时候,它会阻塞住直到音讯到来。

如果对方诘问能不能生产一次生产屡次呢?应用 pub/sub 主题订阅者模式,能够实现 1:N 的音讯队列。

如果对方诘问 pub/sub 有什么毛病?在消费者下线的状况下,生产的音讯会失落,得应用业余的音讯队列如 rabbitmq 等。

如果对方诘问 redis 如何实现延时队列?我预计当初你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么具体。然而你很克服,而后神态自若的答复道:应用 sortedset,拿工夫戳作为 score,音讯内容作为 key 调用 zadd 来生产音讯,消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行解决。

到这里,面试官暗地里曾经对你竖起了大拇指。然而他不晓得的是此刻你却竖起了中指,在椅子背地。

17. 如果有大量的 key 须要设置同一时间过期,个别须要留神什么?

如果大量的 key 过期工夫设置的过于集中,到过期的那个工夫点,redis 可能会呈现短暂的卡顿景象。个别须要在工夫上加一个随机值,使得过期工夫扩散一些。

18.Redis 如何做长久化的?

bgsave 做镜像全量长久化,aof 做增量长久化。因为 bgsave 会消耗较长时间,不够实时,在停机的时候会导致大量失落数据,所以须要 aof 来配合应用。在 redis 实例重启时,会应用 bgsave 长久化文件从新构建内存,再应用 aof 重放近期的操作指令来实现残缺复原重启之前的状态。

对方诘问那如果忽然机器掉电会怎么?取决于 aof 日志 sync 属性的配置,如果不要求性能,在每条写指令时都 sync 一下磁盘,就不会失落数据。然而在高性能的要求下每次都 sync 是不事实的,个别都应用定时 sync,比方 1s1 次,这个时候最多就会失落 1s 的数据。

对方诘问 bgsave 的原理是什么?你给出两个词汇就能够了,fork 和 cow。fork 是指 redis 通过创立子过程来进行 bgsave 操作,cow 指的是 copy on write,子过程创立后,父子过程共享数据段,父过程持续提供读写服务,写脏的页面数据会逐步和子过程拆散开来。

19.Pipeline 有什么益处,为什么要用 pipeline?

能够将屡次 IO 往返的工夫缩减为一次,前提是 pipeline 执行的指令之间没有因果相关性。应用 redis-benchmark 进行压测的时候能够发现影响 redis 的 QPS 峰值的一个重要因素是 pipeline 批次指令的数目。

20.Redis 的同步机制理解么?

Redis 能够应用主从同步,从从同步。第一次同步时,主节点做一次 bgsave,并同时将后续批改操作记录到内存 buffer,待实现后将 rdb 文件全量同步到复制节点,复制节点承受实现后将 rdb 镜像加载到内存。加载实现后,再告诉主节点将期间批改的操作记录同步到复制节点进行重放就实现了同步过程。

21. 是否应用过 Redis 集群,集群的原理是什么?

Redis Sentinal 着眼于高可用,在 master 宕机时会主动将 slave 晋升为 master,持续提供服务。

Redis Cluster 着眼于扩展性,在单个 redis 内存不足时,应用 Cluster 进行分片存储。

正文完
 0