当前位置:网站首页 > Haskell函数式编程 > 正文

ceph分布式存储面试题(分布式缓存面试题)



当业务系统查询数据时,首先会查询缓存,如果缓存中数据不存在,然后查询DB再将数据预热到Cache中,并返回。缓存的性能比 DB 高 50~100 倍以上。

很多业务场景,如:秒杀商品、微博热搜排行、或者一些活动数据,都是通过跑任务方式,将DB数据批量、集中预热到缓存中,缓存数据有着近乎相同的过期时间。当过这批数据过期时,会一起过期,此时,对这批数据的所有请求,都会出现缓存失效,从而将压力转嫁到DB,DB的请求量激增,压力变大,响应开始变慢。

解决方案

可以从缓存的过期时间入口,将原来的固定过期时间,调整为过期时间=基础时间+随机时间,让缓存慢慢过期,避免瞬间全部过期,对DB产生过大压力。

不是所有的请求都能查到数据,不论是从缓存中还是DB中。

假如黑客攻击了一个论坛,用了一堆肉鸡访问一个不存的帖子id。按照常规思路,每次都会先查缓存,缓存中没有,接着又查DB,同样也没有,此时不会预热到Cache中,导致每次查询,都会cache miss。由于DB的吞吐性能较差,会严重影响系统的性能,甚至影响正常用户的访问。

解决方案

方案一:查存DB 时,如果数据不存在,预热一个特殊空值到缓存中。这样,后续查询都会命中缓存,但是要对特殊值,解析处理。

方案二:构造一个BloomFilter过滤器,初始化全量数据,当接到请求时,在BloomFilter中判断这个key是否存在,如果不存在,直接返回即可,无需再查询缓存和DB。

指热点 key 在某个时间点过期的时候,而恰好在这个时间点对这个Key 有大量的并发请求过来,从而大量的请求发送到数据库。

解决方案

方案一:使用互斥锁方案。缓存失效时,不是立即去加载 db 数据,而是先使用某些带成功返回的原子操作命令,如(Redis 的 setnx)去操作,成功的时候,再去加载 db数据库数据和设置缓存。否则就去重试获取缓存。

方案一:“永不过期”,是指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间。

缓存雪崩是指部分缓存节点不可用,进而导致整个缓存体系甚至服务系统不可用的情况。

分布式缓存设计一般选择一致性Hash,当有部分节点异常时,采用 rehash策略,即把异常节点请求平均分散到其他缓存节点。但是,当较大的流量洪峰到来时,如果大流量 key 比较集中,正好在某 1~2 个缓存节点,很容易将这些缓存节点的内存、网卡过载,缓存节点异常 Crash,然后这些异常节点下线,这些大流量 key 请求又被 rehash 到其他缓存节点,进而导致其他缓存节点也被过载 Crash,缓存异常持续扩散,最终导致整个缓存体系异常,无法对外提供服务。

解决方案

方案一:增加实时监控,及时预警。通过机器替换、各种故障自动转移策略,快速恢复缓存对外的服务能力

方案二:缓存增加多个副本,当缓存异常时,再读取其他缓存副本。为了保证副本的可用性,尽量将多个缓存副本部署在不同机架上,降低风险。

对于突发事件,大量用户同时去访问热点信息,这个突发热点信息所在的缓存节点就很容易出现过载和卡顿现象,甚至 Crash,我们称之为缓存热点。这个在新浪微博经常遇到,某大V明星出轨、结婚、离婚,瞬间引发数百千万的吃瓜群众围观,访问同一个key,流量集中打在一个缓存节点机器,很容易打爆网卡、带宽、CPU的上限,最终导致缓存不可用。

解决方案

首先能先找到这个热key来,比如通过Spark实时流分析,及时发现新的热点key。将集中化流量打散,避免一个缓存节点过载。由于只有一个key,我们可以在key的后面拼上有序编号,比如key#01、key#02。。。key#10多个副本,这些加工后的key位于多个缓存节点上。每次请求时,客户端随机访问一个即可。

当访问缓存时,如果key对应的value过大,读写、加载很容易超时,容易引发网络拥堵。另外缓存的字段较多时,每个字段的变更都会引发缓存数据的变更,频繁的读写,导致慢查询。如果大key过期被缓存淘汰失效,预热数据要花费较多的时间,也会导致慢查询。所以我们在设计缓存的时候,要注意缓存的粒度,既不能过大,如果过大很容易导致网络拥堵;也不能过小,如果太小,查询频率会很高,每次请求都要查询多次。

解决方案

方案一:设置一个阈值,当value的长度超过阈值时,对内容启动压缩,降低kv的大小

方案二:评估大key所占的比例,由于很多框架采用池化技术,如:Memcache,可以预先分配大对象空间。真正业务请求时,直接拿来即用。

方案三:颗粒划分,将大key拆分为多个小key,独立维护,成本会降低不少

方案四:大key要设置合理的过期时间,尽量不淘汰那些大key

缓存是用来加速的,一般不会持久化储存。所以,一份数据通常会存在DB和缓存中,由此会带来一个问题,如何保证这两者的数据一致性。另外,缓存热点问题会引入多个副本备份,也可能会发生不一致现象。

方案一:当缓存更新失败后,进行重试,如果重试失败,将失败的 key 写入MQ消息队列,通过异步任务补偿缓存,保证数据的一致性。

方案二:设置一个较短的过期时间,通过自修复的方式,在缓存过期后,缓存重新加载最新的数据。

互联网系统典型的特点就是流量大,一旦缓存中的数据过期、或因某些原因被删除等,导致缓存中的数据为空,大量的并发线程请求(查询同一个key)就会一起并发查询数据库,数据库的压力陡然增加。

如果请求量非常大,全部压在数据库,可能把数据库压垮,进而导致整个系统的服务不可用。

解决方案:

方案一:引入一把全局锁,当缓存未命中时,先尝试获取全局锁,如果拿到锁,才有资格去查询DB,并将数据预热到缓存中。虽然,client端发起的请求非常多,但是由于拿不到锁,只能处于等待状态,当缓存中的数据预热成功后,再从缓存中获取。

方案二:缓存数据创建多个备份,当一个过期失效后,可以访问其他备份。

Redis的英文全称是 Remote Dictionary Server(即远程字典服务),是一个开源的使用C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。

与关系型数据库不同的是,Redis 的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过 10 万次的读写操作。因此Redis 被广泛应用于缓存,Redis 也经常用来做分布式锁。

Redis 支持事务、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。

String(字符串):String 是 Redis 最基础的数据结构类型,它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为 512M。

适用场合:

        String 通常用于保存单个字符串或JSON数据,因为String 是二进制安全的,所以可以把保密要求高的图片文件内容作为字符串来存储。

        计数器:常规key-value 缓存应用,如微博数,粉丝数

Hash(哈希):哈希类型是指 v(值)本身又是一个键值对(k-v)结构。

        适用场合:适合用于存储对象

List(列表):列表(list)类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1 个元素。

        适用场合:

                对数据大的集合数据删减:列表显示、关注列表、粉丝列表、留言评价、分页、热点新闻等

                任务队列:List通常用来实现一个消息队列,而且可以确保先后顺序

Set(集合):Set类型也是用来保存多个的字符串元素,但是不允许重复元素。

        适用场合:对两个集合间的数据进行交集、并集、差集运算

Zset(有序集合):有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合不同的是每个元素都会关联一个double类型的分数Redis 正是通过分数来为集合中的成员进行从小到大的排序。

        适用场合:常用于排行榜。

Geospatial:Redis3.2 推出的地理位置定位,用于存储地理位置信息,并对存储的信息进行操作。

        适用场合:

                朋友的定位,附近的人,打车距离计算

                Redis 的 Geo 在 Redis3.2 版本就推出了!这个功能可以推算地理位置的信息, 两地直接到距离,方圆几里的人!

Hyperloglog:用来做基数统计算法的数据结构,如统计网站的 UV

        适用场合:浏览用户数量,一天同一个用户多次访问只能算一次

Bitmap:用一个比特位来映射某个元素的状态,在 Redis 中,它的底层是基于字符串类型实现的,可以把Bitmap看成是一个以比特位为单位的数组

        适用场合:用户签到次数或者登录次数等

String:最大为 512 MB。

List:理论上可以包含最多 2^32 - 1(约 4.29 亿)个元素。每个元素的大小受到内存限制。

Set:同样可以包含最多 2^32 - 1 个元素,且每个元素的大小也受到内存限制。

Sorted Set:最大容量与 Set 相同,也可以包含最多 2^32 - 1 个元素,元素大小受内存限制。

Hash:最多可以包含 2^32 - 1 个字段(键),每个字段的大小也受到内存限制。

Bitmap:可以支持最大为 2^32 位(约 4.29 亿位),使用内存按位存储。

HyperLogLog:支持最多 2^64(约 1.84 × 10^19)个唯一元素的近似计数,但实际存储使用的内存较小(固定大小)。

Geospatial:理论上可以存储最多 2^32 - 1 个地理位置,具体大小取决于内存限制。

  1. String:使用简单的动态字符串(SDS,Simple Dynamic Strings),支持高效的内存管理和字符串操作。
  2. List:使用双向链表(quicklist)。这种结构结合了链表和压缩列表(ziplist),在小列表时使用压缩列表以节省内存,在大列表时使用链表以提高性能。
  3. Set:使用哈希表(hash table)和整数集合(intset)。当元素数量较少且都是整数时,使用整数集合;当数量增多时,转为哈希表以提高性能。
  4. Sorted Set:主要使用跳表(skip list)和哈希表组合。跳表用于实现有序性,哈希表用于快速查找和更新元素。
  5. Hash:对于小哈希(字段数量少),Redis 使用压缩列表(ziplist);对于大哈希,使用哈希表(hash table)。
  6. Bitmap:直接使用位数组,利用字节数组实现对位的高效操作。
  7. HyperLogLog:使用特定的概率算法(基于寄存器)来估算基数,内存使用固定(12 KB)。
  8. Geospatial:主要使用有序集合(sorted set)来存储地理位置信息,并通过地理哈希算法进行坐标转换和存储。

        读写性能优异:Redis能读的速度是次/s,写的速度是81000次/s。

        高可用性:用于独立和集群部署的自动故障转移复制。支持集群、分布式、主从同步的部署方式。

        可以存储丰富的数据类型:Redis支持String, List, Hash, Set ,Zset等数据类型操作。

        持久化存储:作为一个内存数据库,最担心的就是万一机器死机或宕机,数据就会丢失。Redis使用RDB和AOF做数据的持久化存储。主从数据同时,生成rdb文件,并利用缓冲区添加新的数据更新操作做对应的同步。

        支持事务:Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。通过MULTI和EXE指令包起来。

        支持主从复制:主机会自动将数据同步到从机,可以进行读写分离。

        原子性:Redis的所有操作都是原子性的,意思就是要么成功执行,要么失败完全不执行。

        丰富的特性:Redis还支持 publish/subscribe, 通知key过期等等特性。

        数据库容量受到物理内存的限制:不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。虽然Redis本身有key过期策略,但是还是需要提前预估和节约内存。如果内存增长过快,需要定期删除数据。

        无法利用多核服务器的CPU:Redis是单线程的,单台服务器无法充分利用多核服务器的CPU

        Redis 不具备自动容错和恢复功能:主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。

        主机宕机:宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。

        Redis 较难支持在线扩容:在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

数据结构:Redis 支持 5 种数据结构;Memcached 只支持字符串

性能对比:小数据量存储Redis比Memcached快;大数据存储 Redis 稍逊

持久化:Redis 支持持久化;Memecached 数据都在内存之中

线程模型:Redis 使用单线程模型,基于非阻塞的 IO 多路复用机制,无线程切换;Memecached 使用多线程模型,一个 master 线程,多个 worker 线程

        I/O :网络 I/O,多路 :多个网络连接,复用:复用同一个线程。

        IO 多路复用其实就是一种同步 IO 模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出 CPU。

灾难恢复:Redis数据丢失后可以通过 aof 恢复;Memecached 挂掉后数据不可恢复;

集群模式:Redis 原生支持cluster模式;Memcached 没有原生的集群模式

主要从“高性能”和“高并发”这两点来看待这个问题。

高性能:

        假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

高并发:

        直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

        缓存分为本地缓存和分布式缓存。以 Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着 jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。

        使用 redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。

完全基于内存:绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);

数据结构简单:对数据操作也简单,Redis 中的数据结构是专门进行设计的;

采用单线程:避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

使用多路 I/O 复用模型:非阻塞 IO;

        I/O :网络 I/O

        多路 :多个网络连接

        复用:复用同一个线程。 IO 多路复用其实就是一种同步 IO 模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出 CPU。

使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

答案:Redis的多线程主要是处理数据的读写、协议解析。执行命令还是采用单线程顺序执行。

主要是因为redis的性能瓶颈在于网络IO而非CPU,使用多线程进行一些周边预处理,提升了IO的读写效率,从而提高了整体的吞吐量。antirez 在 RedisConf 2019 分享时提到,Redis 6 引入的多线程 IO 对性能提升至少一倍以上。

从系统可用性角度思考,Redis Cluster引入主备机制,当主节点挂了后,自动切换到备用节点,继续提供服务。

Client 端引入本地缓存,通过开关切换,避免Redis突然挂掉,高并发流量把数据库打挂。

缓存、分布式数据共享、分布式锁、全局ID、计数器、限流、位统计、购物车、用户消息时间线timeline、消息队列、抽奖、点赞,签到,打卡、商品标签、商品筛选、用户关注、推荐模型、排行榜等

什么是Redis持久化? 持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:

RDB:是Redis DataBase缩写快照

RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。

优点

1、只有一个文件 dump.rdb,方便持久化。

2、容灾性好,一个文件可以保存到安全的磁盘。

3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能。

4.相对于数据集大时,比 AOF 的启动效率更高。

缺点

1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)

2、AOF(Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储)保存为 aof 文件

AOF持久化

AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。

当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复

优点

1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。

2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。

3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))

缺点

1、AOF 文件比 RDB 文件大,且恢复速度慢。

2、数据集大的时候,比 rdb 启动效率低。

AOF文件比RDB更新频率高,优先使用AOF还原数据。

AOF比RDB更安全也更大

RDB性能比AOF好

如果两个都配了优先加载AOF

一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

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

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

我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是 指当Redis中缓存的key过期了,Redis如何处理。

定时过期

        每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

惰性过期

        只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

定期过期

        每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 (expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。) Redis中同时使用了惰性过期和定期过期两种过期策略。

1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中,淘汰最近最少使用的数据

2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中,淘汰最早会过期的数据

3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中,随机淘汰数据

4. allkeys-lru:从数据集(server.db[i].dict)中,淘汰最近最少使用的数据

5. allkeys-random:从数据集(server.db[i].dict)中,随机淘汰数据

6.noenviction:Redis 的默认策略,不回收数据,当达到最大内存时,新增数据返回 error

expire 和 persist 命令。

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

1、定时去清理过期的缓存;

2、当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

1. 热点数据识别

首先,需定义什么是“热点数据”。通常,热点数据是指被频繁访问的数据。可以通过以下方法识别这些数据:

  • 访问日志分析:分析访问日志,找出访问频率最高的数据。
  • 使用计数器:在 MySQL 中为每条数据维护一个访问计数器,每次访问时更新该计数器。

2. 数据淘汰策略

确保 Redis 中保持最新的热点数据,可以使用以下数据淘汰策略:

  • LRU(Least Recently Used)算法:利用 Redis 的 LRU 特性,自动移除最少使用的数据。
  • 定期更新:定期将 MySQL 中新的热点数据更新到 Redis。可以设置定时任务(如每小时、每天)来扫描 MySQL 中的访问数据。

3. 使用消息队列

结合消息队列(如 Kafka、RabbitMQ)来实时更新 Redis 中的数据。每次访问数据时,可以将数据 ID 推送到消息队列,后台服务消费队列并更新 Redis。

4. Cache Aside Pattern

在应用中实现 Cache Aside 模式,当应用请求某个数据时:

  • 首先检查 Redis。
  • 如果数据不在 Redis 中,查询 MySQL,并将结果存入 Redis,同时增加访问计数。
  • 在 Redis 中设置适当的过期时间,确保数据不会一直保留。

5. 定期清理

定期清理 Redis 中不再是热点的数据,可以根据访问频率或使用时间进行清理,保持 Redis 中数据的新鲜度。

6. 监控与优化

监控 Redis 的命中率和性能,评估热点数据的变化,并根据需要调整 Redis 中的数据集。

示例流程

  1. 初始化:从 MySQL 中加载初始的 20 万热点数据到 Redis。
  2. 实时更新:每次请求时:
    • 如果 Redis 命中,则更新访问计数。
    • 如果未命中,则从 MySQL 获取并存入 Redis,并更新计数。
  3. 周期性维护:定期从 MySQL 识别新的热点数据,并替换掉 Redis 中的冷数据。

通过这些策略,可以确保 Redis 中只存储热点数据,提高系统的性能和响应速度。

Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。

全局的键空间选择性移除

noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。

allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)

allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。设置过期时间的键空间选择性移除

volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。

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

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

Redis的内存淘汰策略的选取并不会影响过期的key的处理。

内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。

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

1、内存回收

2、3种过期策略

3、8种淘汰策略:如果服务器内存满了,但是依然继续执行redis的set操作,就会触发淘汰策略,可以通过参数 maxmemory-policy 进行配置;

可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。

Redis集群可以分为主从集群和分片集群两类。

主从集群: 一般一主多从,主库用来写数据,从库用来读数据。结合哨兵,可以再主库宕机时从新选主库,目的是保证Redis的高可用。

分片集群: 是数据分片,我们会让多个Redis节点组成集群,并将16383个插槽分到不同的节点上。存储数据时利用对key做hash运算,得到插槽值后存储到对应的节点即可。因为存储数据面向的是插槽而非节点本身,因此可以做到集群动态伸缩。目的是让Redis能存储更多数据。

主从集群,也是读写分离集群。一般都是一主多从方式。

Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。

只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步给从服务器,从而一直保证主从服务器的数据相同。

写数据时只能通过主节点完成

读数据可以从任何节点完成

如果配置了哨兵节点,当master宕机时,哨兵会从salve节点选出一个新的主。

主从集群分两种

一主多从

带有哨兵的集群

主从集群中,每个节点都要保存所有信息,容易形成木桶效应。并且当数据量较大时,单个机器无法满足需求。此时我们就要使用分片集群了。

每个节点都保存不同数据

所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。

节点的fail是通过集群中超过半数的节点检测失效时才生效。

客户端与redis节点直连,不需要中间proxy层连接集群中任何一个可用节点都可以访问到数据

redis-cluster把所有的物理节点映射到[0-16383]slot(插槽)上,实现动态伸缩

为了保证Redis中每个节点的高可用,我们还可以给每个节点创建replication(slave节点),如图:

出现故障时,主从可以及时切换:

Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,

Redisson:实现了分布式和可扩展的Java数据结构。

Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

Jedis:比较全面的提供了Redis的操作特性

Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列

Lettuce:主要在一些分布式缓存框架上使用比较多

Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。

Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作

Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作

  1. 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  2. 没有隔离级别,事务提交前结果不可见,事务提交执行后可见。
  3. 不保证原子性,Redis 同一个事务中有命令执行失败,其后的命令仍然会被执行,不会回滚
  1. 信息开启:MULTI 指令开启一个事务
  2. 入队:将多个命令入队到事务中,这些命令不会立即执行,而是放到等待执行的事务队列
  3. 执行:由 EXEC 指令触发事务执行
  1. multi,标记一个事务块的开始,返回 ok
  2. exec,执行所有事务块内,事务块内所有命令执行的先后顺序的返回值,操作被,返回空值 nil
  3. discard,取消事务,放弃执行事务块内的所有命令,返回 ok
  4. watch,监视 key 在事务执行之前是否被其他指令改动,若已修改则事务内的指令取消执行,返回 ok
  5. unwatch,取消 watch 命令对 key 的监视,返回 ok

1. 分布式锁,是控制分布式系统不同进程共同访问共享资源的一种锁的实现。秒杀下单、抢红包等等业务场景,都需要用到分布式锁,我们项目中经常使用Redis 作为分布式锁。

2. 首先利用Redis缓存的性质在Redis中设置一个key-value形式的键值对,key就是锁的名称,然后客户端的多个线程去竞争锁,竞争成功的话将value设为客户端的唯一标识。

3. 竞争到锁的客户端要做两件事

(1) 设置锁的有效时间,目的是防死锁 (非常关键)

(2) 需要根据业务需要,不断的压力测试来决定有效期的长短。

(3) 分配客户端的唯一标识,目的是保证持锁人解锁(非常重要)

4. 访问共享资源

5. 释放锁,释放锁有两种方式,第一种是有效期结束后自动释放锁,第二种是先根据唯一标识判断自己是否有释放锁的权限,如果标识正确则释放锁。

  1. slave启动后,向master发送sync命令
  2. master收到sync之后,执行bgsave保存快照,生成RDB全量文件
  3. master把slave的写命令记录到缓存
  4. bgsave执行完毕之后,发送RDB文件到slave,slave执行
  5. master发送缓冲区的写命令给slave,slave接收命令并执行,完成复制初始化。
  6. 此后,master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性
  1. master能自动将数据同步到slave,可以进行读写分离,分担master的读压力
  2. master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求
  1. 不具备自动容错与恢复功能,master 节点宕机后,需要手动指定新的 master
  2. master宕机,如果宕机前数据没有同步完,则切换IP后会存在数据不一致的问题
  3. 难以支持在线扩容,Redis的容量受限于单机配置

哨兵模式基于主从复制模式,增加了哨兵来监控与自动处理故障。

  1. 哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有
  2. master 挂掉可以自动进行切换,系统可用性更高
  1. Redis的容量受限于单机配置
  2. 需要额外的资源来启动sentinel进程

实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。

  • 无中心架构,数据按照slot分布在多个节点
  • 集群中的每个节点都是平等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
  • 可线性扩展到1000多个节点,节点可动态添加或删除
  • 能够实现自动故障转移,节点之间通过
  • gossip协议交换状态信息,用投票机制完成slave到master的角色转换
  • 数据通过异步复制,不保证数据的强一致性
  • slave充当 “冷备”,不对外提供读、写服务,只作为故障转移使用。
  • 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好
  • key事务操作支持有限,只支持多key在同一节点的事务操作,多key分布在不同节点时无法使用事务功能
  • 不支持多数据库空间,一台redis可以支持16个db,集群模式下只能使用一个,即db 0。Redis Cluster模式不建议使用pipeline和multi-keys操作,减少max redirect产生的场景。

一个redis集群由多个节点node组成,而多个node之间通过cluster meet命令来进行连接,组成一个集群。

数据存储通过分片的形式,整个集群分成了16384个slot,每个节点负责一部分槽位。整个槽位的信息会同步到所有节点中。

key与slot的映射关系:健值对 key,进行 CRC16 计算,计算出一个 16 bit 的值将 16 bit 的值对 16384 取模,得到 0 ~ 16383 的数表示 key 对应的哈希槽。

  1. 先删除缓存
  2. 再更新数据库
  3. 休眠一会(比如 1 秒),再次删除缓存
  1. 因为延时双删可能会存在第二步的删除缓存失败,导致的数据不一致问题。可以使用这个方案优化:删除失败就多删除几次呀,保证删除缓存成功就可以了呀, 所以可以引入删除缓存重试机制
  2. 写请求更新数据库
  3. 缓存因为某些原因,删除失败
  4. 把删除失败的 key 放到消息队列
  5. 消费消息队列的消息,获取要删除的 key
  6. 重试删除缓存操作
到此这篇ceph分布式存储面试题(分布式缓存面试题)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • sigmrnd函数(sigmoid 函数)2025-10-26 12:36:07
  • 连接redis哨兵模式(redis哨兵连接数设置)2025-10-26 12:36:07
  • 合并数组的函数(合并数组的函数是什么)2025-10-26 12:36:07
  • lead函数是聚合函数不是窗口函数(聚合函数 null)2025-10-26 12:36:07
  • 条件变量例子(条件变量函数)2025-10-26 12:36:07
  • 支付方式怎么更换快手(快手支付方式怎么改为微信)2025-10-26 12:36:07
  • 三方协议支付方式怎么写(三方付款协议怎么有效)2025-10-26 12:36:07
  • 鸿蒙编程技术(鸿蒙系统 编程)2025-10-26 12:36:07
  • cmake project(cmake project函数)2025-10-26 12:36:07
  • 生成范围内的随机数(随机生成一个范围内的数的函数)2025-10-26 12:36:07
  • 全屏图片