设计类题目要先从大局上讲出需要设计的东西的重点,然后再等待面试官的继续提问,深挖。
回答:
首先我们需要明确地提出消息中间件的几个重要角色,分别是生产者、消费者、Broker、注册中心。
简述下消息中间件数据流转过程,无非就是生产者生成消息,发送至 Broker,Broker 可以暂缓消息,然后消费者再从 Broker 获取消息,用于消费。
而注册中心用于服务的发现包括:Broker 的发现...
一般会通过定时任务或者消息队列的延迟消息来实现订单超时取消功能。
定时任务定时任务的逻辑就是扫描已创建未支付的订单,判断订单的创建时间与当前时间的差值,如果已经超过了预设的超时时间,比如 10 分钟,那么就将订单状态更新为已取消。
比如可以每 1 分钟扫描一次表,直接根据当前时间和超时时间得到筛选的时间,比如当前是 14:00,那么 13:50 分之前创建的订单未付款其实都超时了,将这个时间...
所谓的单例一般指的是一个进程中一个类对应只有一个实例对象,也就是进程唯一。
而分布式,不过是一个机器部署多个服务,还是多个机器部署,本质上就是多进程,所以所谓的分布式单例指的是这个实例对应需要在多进程中保持唯一。
按照这个思路,我们仅需控制同一时刻,只会有一个进程使用这个单例对象即可,而分布式场景下的分布式锁就很容易实现这个功能。
多个进程竞争分布式锁,谁抢到锁,谁此时就可以使用这个单例对象...
根据问题推断这个排行榜是一个动态的排行榜,店铺内商品一直在售卖,因此排行榜也会一直在变,所以如果利用数据库来排行计算效率会非常低,总不能每卖一单就利用数据库排序计算得到排行榜吧?
所以排除数据库这个方案。
应对数据库效率差的情况,我们常用的替代方案就是缓存,而 Redis 内有个 zset 可以实现排行榜的功能。
每个商家都对应有个 zset,score 存储商品售卖的数量,value 存储...
首先我们要理清朋友圈点赞具体需要涉及哪几个功能点:
1)存储点赞信息
需要存储哪些用户点赞了这条朋友圈,具体需要存储用户ID、点赞时间即可。
2)取消点赞
需要快速找到这名用户,将其移除点赞列表。
3)获取点赞列表
朋友圈需要展示点赞的用户头像列表信息.
核心就是这么三点,其实就是增删查,那用什么来实现比较合适呢?
实现快速存储和删除,Set 就挺合适,而且还能天然去重,但是常见的 Hash...
最简单的可以使用 ConcurrentHashMap + AtomicInteger + 定时任务实现内存中的统计。
ConcurrentHashMap 的 key 为方法的名称、value 为 AtomicInteger 类型,记录调用次数,可以通过 aop 切面实现每个方法调用都记录到 ConcurrentHashMap 中,然后利用定时任务每 60 s 统计一次所有方法的数量,再清空 ...
回答重点HashMap 是 非线程安全 的。因为 HashMap 的内部实现并没有加锁,多个线程同时访问和修改时可能会引发数据竞争,导致数据不一致或陷入死循环等问题。
如何实现一个线程安全的 HashMap要实现一个线程安全的 HashMap,有多种设计方案,下面是几种常见的实现方法:
使用 Collections.synchronizedMapJava 提供了一个简单的方法,可以将非线程安...
回答重点Redis 中的 ZSet(有序集合,Sorted Set) 是一种由 跳表(Skip List) 和 哈希表(Hash Table) 组成的数据结构。ZSet 结合了集合(Set)的特性和排序功能,能够存储具有唯一性的成员,并根据成员的分数(score)进行排序。
ZSet 的实现由两个核心数据结构组成:
跳表(Skip List):用于存储数据的排序和快速查找。
哈希表(Has...
回答重点为什么不用红黑树?1)相比红黑树而言实现简单
跳表基于多层链表实现,通过概率算法动态生成索引层级,没有左旋右旋等操作,逻辑理解上更为简单。而红黑树需要复杂的平衡操作(旋转)来维护结构,代码实现复杂度较高,理解门槛更高。
2)范围查询更高效
范围查询跳表可以通过 O(logn) 的时间复杂度定位起点,然后在原始的链表中往后遍历即可。
红黑树从结构上不支持范围查询。
3)结构更灵活
跳表...
回答重点Redisson 的看门狗(watchdog)主要用来避免 Redis 中的锁在超时后业务逻辑还未执行完毕,锁却被自动释放的情况。它通过定期刷新锁的过期时间来实现自动续期。
主要原理:
定时刷新:如果当前分布式锁未设置过期时间,Redisson 基于 Netty 时间轮启动一个定时任务,定期向 Redis 发送命令更新锁的过期时间,默认每 10s 发送一次请求,每次续期 30s。
...
回答重点Redis 作为一款高性能的内存数据库,其源码中包含了许多巧妙的设计。这些设计不仅体现了高效的数据处理和管理能力,也为后续的系统扩展和性能优化奠定了基础。
巧妙的设计主要包括:线程模型、数据结构、共享对象池、过期设计、数据持久化设计等。
线程模型Redis 使用单线程模型来处理所有的客户端请求。
虽然看似单线程简单,但这种设计减少了上下文切换和锁的开销,避免了多线程编程中的复杂性。
...
回答重点Redisson 是基于 Redis 实现的分布式锁,实际上是使用 Redis 的原子操作来确保多线程、多进程或多节点系统中,只有一个线程能获得锁,避免并发操作导致的数据不一致问题。
1)锁的获取:
Redisson 使用 Lua 脚本,利用 exists + hexists + hincrby 命令来保证只有一个线程能成功设置键(表示获得锁)。
同时,Redisson 会通过 p...
回答重点Redis 的订阅发布功能(Publish/Subscribe,简称 Pub/Sub),是一种消息通信机制,用于在不同客户端之间实现消息的实时传递和广播。使用 Pub/Sub 模型,客户端可以订阅一个或多个频道,当有其他客户端向这些频道发布消息时,所有订阅了该频道的客户端都会立即收到消息。
主要功能包括:
发布(Publish):某个客户端向指定的频道...
回答重点若锁在未完成逻辑前就过期,此时可能会产生数据不一致的问题。因为锁过期了,此时如果再出现一个客户端争抢锁,即可拿到锁然后同时进行业务操作,这等于锁失效了。
此时可以在逻辑执行过程中定期续期锁,确保锁在处理过程中不会过期。
扩展知识看门狗机制业界出了一个看门狗机制来防止这种情况的产生。
理论很简单,在抢到锁之后,后台会有一个任务,定时向 redis 进行锁的续期。比如锁的过期时间是...
回答重点Red Lock,又称为红锁,是一种分布式锁的实现方案,旨在解决在分布式环境中使用 Redis 实现分布式锁时的安全性问题。
一般情况下,我们在生产环境会使用主从+哨兵方式来部署 Redis。
如果我们正在使用 redis 分布式锁,此时发生了主从切换,但从节点上不一定已经同步了主节点的锁信息。
所以新的主节点上可能没有锁的信息。此时另一个业务去加锁,一看锁还没被占,于是抢到了锁开始...
回答重点Redis 中的 String 类型底层实现主要基于 SDS(Simple Dynamic String 简单动态字符串)结构,并结合 int、embstr、raw 等不同的编码方式进行优化存储。
扩展知识C 语言字符串的缺陷Redis 为什么没有使用 C 标准库提供的字符串,而是实现了一种动态字符串?因为 C 语言的字符串本质上就是 char* 的字符数组,存在一定缺陷:
C 语...
回答重点Redis 中 HyperLogLog 结构,可以快速实现网页 UV 、PV 等统计场景。它是一种基数估算算法的概率性数据结构,可以用极少的内存统计海量用户唯一访问量的近似值。
Set 也可以实现,用于精确统计唯一用户访问量,但是但当用户数非常大时,内存开销较高。
扩展知识HyperLogLog 使用介绍HyperLogLog 具有极小的内存占用(每个 HyperLogLog 结...
回答重点Redis 中的 Geo(Geolocation 的简写形式,代表地理坐标) 数据结构主要用于地理位置信息的存储。通过这个结构,可以方便地进行地理位置的存储、检索、以及计算地理距离等操作。Geo 数据结构底层使用了 Sorted Set,并且结合了 Geohash 编码算法来对地理位置进行处理。
它是 Redis 2.2 版本后新增的数据类型
扩展知识Geo 常用命令GEOADD...
回答重点Redis 字符串能存储的最大容量是 512 MB 的数据,可以查看官方文档:
无论是网络传输、内存分配还是字符串操作,大字符串都会增加 Redis 服务器的负载。
且过大的字符串在 GET、SET、APPEND、STRLEN 等操作都会导致性能瓶颈。
所以官方给字符串的大小做了限制,防止单个键值对占用过多的内存,影响整体性能和稳定性。
扩展知识
661. Redis Strin...
为什么 EMBSTR 的阈值大小是 44 个字节?这个问题有几个关键点:
1)Redis 使用的是 jemalloc 作为内存分配器。
2)jemalloc 是以 64 字节作为内存单位进行内存分配的,如果超过了 64 字节,即超过了一个内存单元,使用的就是 raw 编码,反之使用的就是 EMBSTR 编码。
3)核心就是这个 64 字节,围绕 64 字节这个关键点来分析。Redis 的字符...
回答重点原生批处理命令(MSET、MGET) 和 Pipeline 都可以用于一次性处理多个命令,但它们在实现方式和应用场景上有所不同:
1)MSET / MGET(原生批处理命令):
MSET 和 MGET 是 Redis 提供的原生批处理命令,用于批量设置和获取多个键值。
它们是 单个命令,可以一次操作多个键值对,因此只需要进行一次网络往返,适合对多个键值进行原子性的读写操作...
回答重点Redis 主从的几种常见拓扑结构如下(忽略哨兵):
1)一主多从:
这是最基本的拓扑结构,包含一个主节点和多个从节点。所有写操作都在主节点上执行,而读操作可以在从节点上进行,以提高读取速度和负载均衡。
2)树状主从结构(级联):
从节点也可以作为其他从节点的主节点。这样形成了一个层次结构,主节点负责写操作,而从节点负责读操作,并将数据再次复制到更下一级的从节点。
...
回答重点可以通过 List 类型 来实现 队列 和 栈 :
实现队列(FIFO):
队列是一种 先进先出(FIFO) 的数据结构。在 Redis 中,可以使用 LPUSH 和 RPOP 命令组合来实现队列。
LPUSH 向列表的左侧推入元素,而 RPOP 从列表的右侧弹出元素,这样可以保证最先进入的元素最先被弹出。
实现栈(LIFO):
栈是一种 后进先出(LIFO) 的数据结构。在 ...
回答重点Ziplist:
简单、紧凑、连续存储,适用于小数据量场景,但对大量数据或频繁的修改操作不太友好。
适合小数据量场景,例如短列表、小哈希表等,因为它的内存紧凑,可以大幅减少内存使用
Quicklist:
通过将链表和 Ziplist 结合,既实现了链表的灵活操作,又能节省内存,在 Redis 3.2 之后成为 List 的默认实现。
Quicklist 是为了替代纯链表而设计的...
回答重点
Redis 的复制延迟是指从节点同步主节点数据时可能出现时间延迟。在读写分离场景,这个延迟会导致明明写入了数据,但是去从节点查的时候没查到。
可能原因如下:
1)网络原因
可能是带宽不足,或者网络抖动导致同步的延迟。
不过一般内网情况下不会产生这个问题。
2)主节点负载过高
主节点接收到大量的写操作,在处理客户端请求的同时,还需向从节点发送复制数据。如果主节点负载较高时,来不及处...
回答重点Redis 的事务跟严格意义上的关系型数据库事务不一样,先复习下数据库事务 ACID 的定义:
原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败,且可以回滚到事务开始前的状态。
一致性(Consistency):事务执行前后,数据库必须保持一致的状态。
隔离性(Isolation):事务的执行是隔离的,事务之间不会相互干扰。支持不同的隔离级别(如读未提交、读...
回答重点1)Redis Cluster 是 Redis 集群,提供自动分片功能,将数据自动分布在多个节点上,支持自动故障转移。如果一个节点失败,集群会自动重新配置和平衡,不需要外部介入,因为它内置了哨兵逻辑。
2)Sentinel 是哨兵,主要用于管理多个 Redis 服务器实例来提高数据的高可用性。当主节点宕机,哨兵会将从节点提升为主节点,它并不提供数据分片功能。
如果需要处理大量数据并进...
回答重点ListPack 是 Redis 内部的一种数据结构,用于高效存储短小的字符串或整数集合。它是一种紧凑型的序列化数据结构,旨在减少内存占用和提升性能。为了尽可能紧凑地存储数据,因此它没有使用 Redis 常见的对象模型,而是直接以字节序列的形式存储数据。
ListPack 是 Redis 6.0 引入的新数据类型,在 List、Hash 和 ZSet 的内部实现中使用。
ListPa...
回答重点Redis 的内存碎片化是指内存使用中出现小块空间被闲置,无法被有效利用的现象。
Redis 默认使用 jemalloc 作为内存分配器,它是按照固定大小来分配内存的,比如实际需要 8kb 的内存,分配器给了 12kb。
那么多余的 4kb 其实就无法被利用上了,它就叫内存碎片。
且频繁创建和删除大量数据的时候,会导致内存块大小和位置不连续,内存碎片会变多。
可以通过 INFO m...
回答重点Redis 的 VM 机制(Virtual Memory)曾经是 Redis 早期版本(2.0 之前)的一部分,用于将部分数据存储在磁盘上,以扩展内存数据库的容量。当内存不足时,Redis 会将冷数据(不经常访问的数据)移到磁盘,并将热数据(经常访问的数据)保留在内存中。
通过这种方式,Redis 可以处理比实际物理内存更多的数据。
虽然能使用的数据变多了,但是数据存到磁盘在获取会显...