Redis学习-FAQ

Redis学习-FAQ

转载声明

本文内容大量转自Java技术栈、 菜鸟架构 ,再加自己的理解有少许改动。
请大家关注公众号【Java技术栈 】【 菜鸟架构 】,尊重作者的辛苦付出。
本文只是为了方便学习。
如给原作者带来不便,请联系我删除。

0x00 系列文章目录

  1. Redis学习-安装和启动
  2. Redis学习-知识梳理
  3. Redis学习-FAQ

0x01 摘要

考虑到绝大部分写业务的程序员,在实际开发中使用 Redis 的时候,只会 Set Value 和 Get Value 两个操作,对 Redis 整体缺乏一个认知。所以我斗胆以 Redis 为题材,对 Redis 常见问题做一个总结,希望能够弥补大家的知识盲点。
主要分为:

  1. Redis基础概念
    • Redis是什么
    • 为什么使用 Redis
    • Redis数据类型
  2. Redis评价
    • Redis相对于Memcached的优势
    • Redis 有什么缺点
  • 单线程的 Redis 为什么这么快
  • Redis 的数据类型,以及每种数据类型的使用场景
  • Redis 的过期策略以及内存淘汰机制
  • Redis 和数据库双写一致性问题
  • 如何应对缓存穿透和缓存雪崩问题
  • 如何解决 Redis 的并发竞争 Key 问题
  • 为什么使用 Redis

0x02 基础概念

2.0 Redis的全称是什么?

Remote Dictionary Server。

2.1 Redis是什么

Redis本质上是一个Key-Value类型的内存型数据库,如果开启了持久化会定期异步把数据flush到硬盘。因为是纯内存操作,Redis的性能非常出色,读写可达10万/S,是已知性能最快的Key-Value DB。

相对于memcached来说,Redis的出色之处不仅是性能,还支持多种数据结构;此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis应用广泛。

比如用List来做FIFO双向链表,实现一个轻量级的高性能MQ;用Set可以做高性能的tag系统;Redis还可以对存入的Key-Value设置expire时间,因此也可以被当作一个功能加强版的memcached来用。

Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

2.2 为什么用Redis

我觉得在项目中使用 Redis,主要是从两个角度去考虑:性能和并发。

当然,Redis 还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件,如 Zookeeper 等代替,并不是非要使用 Redis。

2.2.1 性能

如下图所示,我们在碰到需要执行耗时特别久且结果不频繁变动的 SQL,就特别适合将运行结果放入缓存,使得以后的请求直接从缓存中读取,能够快速响应。
耗时久的SQL调用
也就是说,在适合的情况下使用Redis做缓存,我们可以大大提升请求的响应速度。

2.2.2.1 Redis常见性能问题和解决方案?
  1. Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
  2. 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
  3. 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
  4. 尽量避免在压力很大的主库上增加从库
  5. 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
2.2.2.2 一个Redis实例最多能存放多少的keys?List、Set、Sorted Set他们最多能存放多少元素?

理论上Redis可以处理多达2^32的keys,并且在实际中进行了测试,每个实例至少存放了2亿5千万的keys。我们正在测试一些较大的值。
换句话说,Redis的存储极限是系统中的可用内存值。

2.2.2.3 Redis是单线程的,如何提高多核CPU的利用率?

可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的,
所以,如果你想使用多个CPU,你可以考虑一下分片(shard)。

2.2.2 并发

如下图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。
高并发访问数据库
这个时候,就需要使用 Redis 做为缓存首先来面对高并发的请求压力,而不是直接把压力丢给数据库。

2.3 Redis数据类型

Redis支持丰富的数据类型,主要有:字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets) , bitmaps, hyperloglogs 和 地理空间(geospatial)

2.3.1 String

String是最简单Redis类型,就是一个key对应一个value的pair。如果你只用这种类型,Redis就像一个可以持久化的memcached服务器(注:memcache的数据仅保存在内存中,服务器重启后,数据将丢失)。

可以用String类型做原子类型计数器:

2.3.2 Hash

Redis Hash类型类似Java中的HashMap,他是一个拥有指定key名字的,由field和value组成的键值对映射表,注意field和value都是String。Hash 可以存储2^32 - 1 个键值对(40多亿)。值得注意的是,小的 hash 被用特殊方式编码,非常节约内存。

Redis Hash一般用于存储对象。

2.3.3 List

按插入顺序排序的字符串元素的集合,数据结构是链表(linked list)。这意味着在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是常数级别的。但是,如果要用索引进行随机访问,那么效率会比较低。Redis list 使用链表的主要考虑就是能快速插入数据。

List 可以做简单的消息队列MQ(利用LPUSH和RPOP或是阻塞的BRPOPLPUSH和BRPOP,详情点击这里 )。也可以利用 lrange 顺序范围访问命令做基于 Redis 的高性能分页功能。

2.3.4 Set

不重复且无序的字符串元素的集合,类似java中的hashset,可以求交集、并集、差集等。

2.3.5 Sorted Set

类似Set,但Sorted Set中的每个字符串元素关联到一个叫score浮动数值,集合中的元素按 Score 进行排序。所以它是可以进行有序搜索的元素集合(例如取出前面10个或者后面10个元素)。

Sorted Set可以做排行榜应用如求 TOP N 、范围查找等。

2.4 主从架构

2.4.1 Redis集群的主从复制模型是怎样的?

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.

2.4.2 Redis集群之间是如何复制的?

异步复制

2.4.3 怎么测试Redis的连通性?

ping

0x03 Redis评价

3.1 Redis相对于Memcached的优势

  • Memcached所有的值均是简单的字符串,redis支持更为丰富的数据类型,主要有:字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets) , bitmaps, hyperloglogs 和 地理空间(geospatial)
  • Redis的性能比memcached高很多
  • Redis可以持久化数据,可以做主从HA,支持failover

3.2 Redis有什么缺点

3.2.1 容量受限
  • 容量受内存大小限制
3.2.2 Redis分区带来的问题
  • 涉及多个key的操作通常不会被支持。例如你不能对两个集合求交集,因为他们可能被存储到不同的Redis实例(实际上这种情况也有办法,但是不能直接使用交集指令)。
  • 同时操作多个key时不能使用Redis事务.
  • 分区使用的粒度是key,所以如果一个如set这样的数据结构中有数量非常巨大的数据的时候,因为都属于同一个key的set,所以无法被切分(shard)到多个Redis节点中。
  • 当使用分区的时候,数据处理会非常复杂,例如为了备份你必须从不同的Redis实例和主机同时收集RDB / AOF文件。
  • 分区时动态扩容或缩容可能非常复杂。Redis集群在运行时增加或者删除Redis节点,能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。然而,有一种预分片的技术也可以较好的解决这个问题。
3.2.3 Redis做缓存带来的问题
  • 缓存和数据库双写一致性问题
  • 缓存雪崩问题
  • 缓存击穿问题
  • 缓存的并发竞争问题

0x04 Redis应用

4.1 Redis 和数据库双写一致性问题

一致性是分布式重要问题,可分为强一致性和最终一致性。

数据库和缓存双写,就必然会存在不一致的问题。需明白一个前提:即如果对数据有强一致性要求,就不能放缓存。我们为缓存所做的一切,只能保证最终一致性。

另外,我们所做的方案从根本上来说,只能说降低不一致发生的概率,无法完全避免。

因此,有强一致性要求的数据,不能放缓存。

回答:首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

4.2 如何应对缓存穿透和缓存雪崩问题

如果有大并发、流量达到几百万时,这两个问题一定要深入研究。

4.2.1 缓存穿透

4.2.1.1 概念

即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而导致数据库连接异常。

假如客户端每秒发送5000个请求,其中4000个为黑客的恶意攻击,即在数据库中也查不到。举个例子,用户id为正数,黑客构造的用户id为负数,

如果黑客每秒一直发送这4000个请求,缓存就不起作用,数据库也很快被打死。
缓存穿透

4.2.1.2 解决方案
  • 限流
    计数、滑动窗口、令牌桶、漏桶
  • 互斥锁
    缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。
  • 异步更新
    无论 Key 是否取到值,都直接返回。Value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。此时需要做缓存预热(项目启动前,先加载缓存)操作。此策略需慎重采用,因为可能影响业务时效性和正确性。
  • 布隆过滤器(提供一个能迅速判断请求是否有效的拦截机制)
    比如,利用布隆过滤器,内部维护一系列合法有效的 Key。迅速判断出,请求所携带的 Key 是否合法有效。如果不合法,则直接返回,不访问数据库。
  • Null作为value缓存
    将此key简历一个pair<key, null>放入缓存。但要注意必须设定较短的过期时间,因为如果很多这样的key会导致内存空间一直扩大。还需要有机制在真正插入这类key值时更新缓存,否则会导致缓存和真实数据不一致。

4.2.2 缓存雪崩

4.2.2.1 概念

缓存雪崩,即缓存同一时间大面积的失效。这个时候又来了一波请求,结果请求都打到数据库上,从而导致数据库连接异常甚至崩溃。

假设有如下一个系统:高峰期请求为5000次/秒,4000次走了缓存,只有1000次落到了数据库上,数据库每秒1000的并发是一个正常的指标,完全可以正常工作。但如果缓存宕机了,每秒5000次的请求会全部落到数据库上,数据库立马就死掉了,因为数据库一秒最多抗2000个请求,如果DBA重启数据库,立马又会被新的请求打死了,这就是缓存雪崩。
缓存雪崩

4.2.2.2 解决方案
  • 事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃

  • 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL被打死

  • 事后:redis持久化,快速恢复缓存数据

  • 随机过期时间
    给缓存的失效时间再加上一个随机值,避免缓存同一时间集体失效

  • 使用互斥锁
    缓存失效的时候,先去获得锁SETNXSET if Not eXists),得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试获取。但是该方案吞吐量明显下降了。

  • 双缓存
    有两个缓存,缓存 A 和缓存 B。缓存 A 的失效时间为 20 分钟,缓存 B 不设失效时间。自己做缓存预热操作:
    请求先读缓存 A ,有则返回,无则从B读 。并且异步启动一个更新线程,更新线程同时更新缓存 A 和缓存 B。
    此方案问题是缓存B导致Redis占用内存越来越大。

  • 异步更新

4.2.3 缓存击穿

4.2.3.1 概念

缓存击穿是指,某些key被大量用户并发访问,在过期时会导致这些大并发访问全部转到数据库,可能瞬间把数据库打挂。

缓存击穿和缓存雪崩的区别在于缓存击穿是针对某一特定key缓存的大并发访问。

4.2.3.2 解决方案
  • 使用互斥锁
    缓存失效的时候,先去获得锁SETNXSET if Not eXists),得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试获取。但是该方案吞吐量明显下降了。
  • 双缓存
    有两个缓存,缓存 A 和缓存 B。缓存 A 的失效时间为 20 分钟,缓存 B 不设失效时间。自己做缓存预热操作:
    请求先读缓存 A ,有则返回,无则从B读 。并且异步启动一个更新线程,更新线程同时更新缓存 A 和缓存 B。
    此方案问题是缓存B导致Redis占用内存越来越大。
  • 异步更新

4.3 分布式Redis是前期做还是后期规模上来了再做好?为什么?

既然Redis是如此的轻量(单实例只使用1M内存),为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便你只有一台服务器,你也可以一开始就让Redis以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。

一开始就多设置几个Redis实例,例如32或者64个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。

这样的话,当你的数据不断增长,需要更多的Redis服务器时,你需要做的就是仅仅将Redis实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的Redis实例从第一台机器迁移到第二台机器。

4.4 Redis如何做大量数据插入?

Redis2.6开始redis-cli支持一种新的被称之为pipe mode的新模式用于执行大量数据插入工作。

4.5 Redis key的过期时间和永久有效分别怎么设置?

EXPIRE和PERSIST命令。

4.6 Redis事务相关的命令有哪几个?

MULTI、EXEC、DISCARD、WATCH

4.7 Redis集群会有写操作丢失吗?为什么?

Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。

4.8 Jedis与Redisson对比有什么优缺点?

Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

Redisson是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。

4.9 Redis支持的Java客户端都有哪些?官方推荐用哪个?

Redisson、Jedis、lettuce等等,官方推荐使用Redisson。

4.10 Redis有哪些适合的场景?

  1. 会话缓存(Session Cache)
    最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?

幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。

  1. 全页缓存(FPC)
    除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
    再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
    此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

  2. 队列
    Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
    如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

  3. 排行榜/计数器
    Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
    当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
    ZRANGE user_scores 0 10 WITHSCORES
    Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

  4. 发布/订阅
    最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。

还可以用bitmap搞签到系统

0x05 Redis并发

5.1 如何解决 Redis 的并发竞争 Key 问题

同时有多个子系统去 Set 一个 Key。

不推荐使用 Redis 的事务机制。因为我们的生产环境,基本都是 Redis 集群环境,做了数据分片操作。但Redis事务不支持数据分片。

那么我们必须考虑别的解决方案。

  • 如果对这个 Key 操作的不要求顺序,可以准备一个分布式互斥锁,抢到锁就做 set 操作即可,比较简单。

  • 如果对这个 Key 操作,要求顺序
    假设有一个 key1,系统 A 需要将 key1 设置为 valueA,系统 B 需要将 key1 设置为 valueB,系统 C 需要将 key1 设置为 valueC。
    期望按照 key1 的 value 值按照 valueA -> valueB -> valueC 的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。
    假设时间戳如下:
    系统A key 1 {valueA 3:00}
    系统B key 1 {valueB 3:05}
    系统C key 1 {valueC 3:10}
    那么,假设这会系统 B 先抢到锁,将 key1 设置为{valueB 3:05}。接下来系统 A 抢到锁,发现自己的 valueA 的时间戳早于缓存中的时间戳,那就不做 set 操作了,以此类推。

  • 其他方法,比如利用队列,将 set 方法变成串行访问也可以。

0x06 Redis集群

6.1 修改配置不重启Redis会实时生效吗?

针对运行实例,有许多配置选项可以通过 CONFIG SET 命令进行修改,而无需执行任何形式的重启。 从 Redis 2.2 开始,可以从 AOF 切换到 RDB 的快照持久性或其他方式而不需要重启 Redis。检索 ‘CONFIG GET *’ 命令获取更多信息。

6.2 Redis集群方案应该怎么做?都有哪些方案?

  1. twemproxy,大概概念是,它类似于一个代理方式,使用方法和普通redis无任何区别,设置好它下属的多个redis实例后,使用时在本需要连接redis的地方改为连接twemproxy,它会以一个代理的身份接收请求并使用一致性hash算法,将请求转接到具体redis,将结果再返回twemproxy。使用方式简便(相对redis只需修改连接端口),对旧项目扩展的首选。 问题:twemproxy自身单端口实例的压力,使用一致性hash后,对redis节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。
  2. codis,目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新hash节点。
  3. redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。
  4. 在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key 进行hash计算,然后去对应的redis实例操作数据。 这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。

6.3 Redis集群方案什么情况下会导致整个集群不可用?

有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。

0x07 Redis持久化

7.1 Redis提供了哪几种持久化方式?

  • RDB
    RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.

  • AOF
    AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.

如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.

你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

7.2 如何选择合适的持久化方式?

一般来说, 如果想达到足以媲美PostgreSQL的数据安全性, 你应该同时使用两种持久化功能。如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。

有很多用户都只使用AOF持久化,但并不推荐这种方式:因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外, 使用RDB还可以避免之前提到的AOF程序的bug。

7.3 Redis持久化数据和缓存怎么做扩容?

如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。

如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。

0x08 Redis内存机制

8.1 Redis的内存占用情况怎么样?

给你举个例子: 100万个键值对(键是0到999999值是字符串“hello world”)在我的32位的Mac笔记本上 用了100MB。同样的数据放到一个key里只需要16MB, 这是因为键值有一个很大的开销。 在Memcached上执行也是类似的结果,但是相对Redis的开销要小一点点,因为Redis会记录类型信息引用计数等等。

当然,大键值对时两者的比例要好很多。

64位的系统比32位的需要更多的内存开销,尤其是键值对都较小时,这是因为64位的系统里指针占用了8个字节。 但是,当然,64位系统支持更大的内存,所以为了运行大型的Redis服务器或多或少的需要使用64位的系统。

8.2 都有哪些办法可以降低Redis的内存使用情况呢?

如果你使用的是32位的Redis实例,可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。

8.3 Redis的内存用完了会发生什么?

如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将Redis当缓存来使用配置LRU淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。

8.4 Redis回收使用的是什么算法?

默认为LRU算法

8.5 Redis如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面.

8.6 Redis回收进程如何工作的?

  1. 一个客户端运行了新的命令,添加了新的数据。
  2. Redi检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。
  3. 一个新的命令被执行。
  4. 所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。

如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

8.7 Redis内存不足时有哪几种数据淘汰策略?

  • noeviction
    默认值。当内存不足以容纳新写入数据时,新写入操作会报错。
    不推荐使用。

  • allkeys-lru
    当内存不足以容纳新写入数据时,在key中移除最近最少使用的 Key(LRU)。
    推荐使用。

  • allkeys-random
    当内存不足以容纳新写入数据时,在key中随机移除某个 Key。
    不推荐使用。

  • volatile-lru
    当内存不足以容纳新写入数据时,在设置了过期时间的key中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。
    不推荐。

  • volatile-random
    当内存不足以容纳新写入数据时,在设置了过期时间的key中,随机移除某个 Key。
    不推荐。

  • volatile-ttl
    当内存不足以容纳新写入数据时,在设置了过期时间的key中,有更早过期时间的 Key 优先移除。
    不推荐。

如果没有设置 expire 的 Key,那么 volatile-lru,volatile-random 和 volatile-ttl 策略的行为,和 noeviction(不删除) 基本上一致。

0x09 Redis和其他内存数据库比较

9.1 Redis与其他key-value存储有什么不同?

  • Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
  • Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是, 相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。 同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。

0x10 Redis分区

10.1 Redis分区有什么缺点?

  • 涉及多个key的操作通常不会被支持。例如你不能对两个集合求交集,因为他们可能被存储到不同的Redis实例(实际上这种情况也有办法,但是不能直接使用交集指令)。
  • 同时操作多个key,则不能使用Redis事务.
  • 分区使用的粒度是key,不能使用一个非常长的排序key存储一个数据集(The partitioning granularity is the key, so it is not possible to shard a dataset with a single huge key like a very big sorted set).
  • 当使用分区的时候,数据处理会非常复杂,例如为了备份你必须从不同的Redis实例和主机同时收集RDB / AOF文件。
  • 分区时动态扩容或缩容可能非常复杂。Redis集群在运行时增加或者删除Redis节点,能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。然而,有一种预分片的技术也可以较好的解决这个问题。

10.2 为什么要做Redis分区?

分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。

10.3 你知道有哪些Redis分区实现方案?

  • 客户端分区就是在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。大多数客户端已经实现了客户端分区。

  • 代理分区 意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis实例,然后根据Redis的响应结果返回给客户端。redis和memcached的一种代理实现就是Twemproxy

  • 查询路由(Query routing) 的意思是客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。

0x11 Redis高级功能

11.1 Redis自动过期

Redis有自动过期功能,可以对key设置超时时间,当时间到达后该key数据会被自动删除。精度可为毫秒或秒。下面是一个示例:

> set key some-value
OK
> expire key 5
(integer) 1
> get key (immediately)
"some-value"
> get key (after some time)
(nil)

上面的例子使用了EXPIRE来设置超时时间(也可以再次调用这个命令来改变超时时间,还可以使用PERSIST命令去除超时时间 )。

我们也可以直接在创建值的时候设置超时时间:

> set key 100 ex 10
OK
> ttl key
(integer) 9

上文中TTL命令用来查看key对应的值剩余存活时间。

11.2 事务

11.2.1 怎么理解Redis事务?

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

但是Redis事务有一点不同,当EXEC命令执行后,就算有命令不成功,后面的命令还是会执行。而且Redis事务不能回滚。

11.3 管道

11.3.1 Redis中的管道有什么用?

一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应。这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复。

这就是管道(pipelining),是一种几十年来广泛使用的技术。例如许多POP3协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程。

11.4 说说Redis哈希槽的概念?

Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。

0xFE 总结

这是一个总结

0xFF 参考文档

Redis中文网站

RedisCommands

扫盲,为什么分布式一定要有Redis

Redis面试题汇总

Redis的n种妙用,不仅仅是缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值