分布式系统架构面试题汇总(下)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 这篇文章主要是说在进化的过程中可能会遇到的问题以及如何去解救这些问题。

三、缓存数据库Redis


1、主从复制:实现高可用

一个缓存数据库压力太大,读写分离,通过哨兵机制监控各个节点和相互监督;

v2-ef91679ea6c576dbf470776a45de68fd_1440w.jpg

(1)哨兵模式原理


①哨兵如何实现相互监督的功能


第一:哨兵通过发布订阅__sentinel__:hello channel来实现这个功能。每个哨兵每隔2s会向自己监控的所有主从Redis节点发送hello message,包括自己的IP、端口、运行ID、自己监控的Master节点IP、Master节点端口。


第二:所有主从Redis节点也会反馈这样的信息


②哨兵如何故障检测


第一:某个哨兵节点判定master节点故障,他会投出一票S_DOWN,


第二:当有足够多的sentinel节点判定master节点故障都投出S_DOWN票时,master节点会被认为是真正的下线了。


也就是基于多数投票原则


③哨兵模式如何实现故障恢复


故障恢复需要完成如下几步操作:


第一:通过选主机制选择新的Master节点替换掉原来的故障节点


第二:其他的节点成为Slave节点用于主从复制,也就是不变


第三:告知客户端新的master节点地址信息,同时执行必要的脚本来通知系统管理员。


(2)选主机制


过程:


sentinel的选举过程基本上是Raft协议的实现,即所有节点会随机休眠一段时间,然后发起拉票,当某个节点获得的票数超过max(sentinel|/2 + 1), qurom时,该节点就被推选为leader节点。注意是哨兵去从节点里面选。


到底选谁呢?


①根据指定的优先级选择


管理员在启动redis从节点的时候,指定了其优先级,哨兵会先从优先级高的从节点去选择。


②根据数据更新程度选择


优先级相同,所有slave节点复制数据的时候都会记录复制偏移量,值越大说明与master节点的数据更一致。所以哨兵会选择复制偏移量最大的节点。


③根据runid选择:


到了这一步节点的孰优孰劣就没什么区别了,每个节点启动的时候都会有一个唯一的runId, 那么我们就选择runid最小的节点好了。


2、集群策略


(1)基本实现


Redis Cluster中,Sharding采用slot(槽)的概念,一共分成16384个槽,分布在不同的Redis数据库中,对于每个进入Redis的键值对,根据CRC16后16384取模hash映射,分配到这16384个slot中的某一个中。


缺点


要保证16384个槽对应的node都正常工作,如果某个node发生故障,那它负责的slots也就失效,整个集群将不能工作。


(2)更好的方案一致性哈希算法,解决扩容问题jedis:


Jedis里面有一致性哈希算法,首先构建一个一致性哈希环的结构。一致性哈希环的大小是我们计算机中无符号整型值的取值范围,

v2-1edea337093a6f663bfb011f4e0b5631_1440w.jpg

将一个数据库服务器虚拟成若干个虚拟节点,把这些虚拟节点的 hash 值放到环上去。在实践中通常是把一个服务器节点虚拟成 200 个虚拟节点,然后把 200 个虚拟节点放到环上。用户的key过来时,顺时针的查找距离它最近的虚拟节点,找到虚拟节点以后,根据映射关系找到真正的物理节点。


3、Redis可能出现的问题


(1)缓存雪崩


原理:


缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。


解决方案:


1)主从复制

2)根据一些关键数据进行自动降级

3)提前数据预热


(2)缓存穿透


原理:


缓存穿透是指查询一个一不存在的数据。例如:从缓存redis没有命中,需要从mysql数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。


解决办法:


1)布隆过滤器


当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。


2)缓存空对象


缓存中没有就回去存储层获取,此时即使数据库返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取


(3)缓存击穿


缓存击穿是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,瞬间对数据库的访问压力增大。


解决方案:在查询缓存的时候和查询数据库的过程加锁,只能第一个进来的请求进行执行,当第一个请求把该数据放进缓存中,接下来的访问就会直接集中缓存,防止了缓存击穿。


(4)Redis缓存与数据库数据一致性


不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。举一个例子:


1.如果删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。


2.如果先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况。


怎么保证缓存一致性?:读直接去缓存读,没有的话就读数据库,写直接写数据库,然后失效缓存中对应的数据


第一种方案:延时双删策略+缓存超时设置


在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。


具体的步骤就是:


1)先删除缓存;

2)再写数据库;

3)休眠一段时间;

4)再次删除缓存。


设置缓存过期时间


所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。也就是看到写请求就执行上面的策略。


第二种方案:异步更新缓存(基于订阅binlog的同步机制)


MySQL binlog增量订阅消费+消息队列+增量数据更新到redis

一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息通过消息队列推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。


(5)redis的热key问题如何解决


第一:热Key的概念


所谓热key问题就是,突然有几十万的请求去访问redis上的某个特定key。那么,这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机。那接下来这个key的请求,就会直接怼到你的数据库上,导致你的服务不可用。


第二:怎么发现热key


方法一:凭借业务经验,进行预估哪些是热key 其实这个方法还是挺有可行性的。比如某商品在做秒杀,那这个商品的key就可以判断出是热key。缺点很明显,并非所有业务都能预估出哪些key是热key。


方法二:在客户端进行收集 这个方式就是在操作redis之前,加入一行代码进行数据统计。那么这个数据统计的方式有很多种,也可以是给外部的通讯系统发送一个通知信息。缺点就是对客户端代码造成入侵。


方法三:在Proxy层做收集 有些集群架构是下面这样的,Proxy可以是Twemproxy,是统一的入口。可以在Proxy层做收集上报,但是缺点很明显,并非所有的redis集群架构都有proxy。

v2-8662c5e799d0b774b7d6f21b54fd9283_1440w.jpg

方法四:用redis自带命令


(1)monitor命令,该命令可以实时抓取出redis服务器接收到的命令,然后写代码统计出热key是啥。当然,也有现成的分析工具可以给你使用,比如redis-faina。但是该命令在高并发的条件下,有内存增暴增的隐患,还会降低redis的性能。


(2)hotkeys参数,redis 4.0.3提供了redis-cli的热点key发现功能,执行redis-cli时加上–hotkeys选项即可。但是该参数在执行的时候,如果key比较多,执行起来比较慢。

方法五:自己抓包评估


Redis客户端使用TCP协议与服务端进行交互,通信协议采用的是RESP。自己写程序监听端口,按照RESP协议规则解析数据,进行分析。缺点就是开发成本高,维护困难,有丢包可能性。


以上五种方案,各有优缺点。根据自己业务场景进行选择即可。那么发现热key后,如何解决呢?


三:如何解决


目前业内的方案有两种


(1)利用二级缓存 比如利用ehcache,或者一个HashMap都可以。在你发现热key以后,把热key加载到系统的JVM中。


针对这种热key请求,会直接从jvm中取,而不会走到redis层。假设此时有十万个针对同一个key的请求过来,如果没有本地缓存,这十万个请求就直接怼到同一台redis上了。

现在假设,你的应用层有50台机器,OK,你也有jvm缓存了。这十万个请求平均分散开来,每个机器有2000个请求,会从JVM中取到value值,然后返回数据。避免了十万个请求怼到同一台redis上的情形。


(2)备份热key 这个方案也很简单。不要让key走到同一台redis上不就行了。我们把这个key,在多个redis上都存一份不就好了。接下来,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。


四、数据库优化


1、主从复制


主从复制一般采用多主多从的方案

v2-dfbd36af8f86a2a172a6d14eaf6bd781_1440w.jpg

(1)多主条件下数据一致性问题


也就是两台主数据库同时更新了数据,以谁的为主


①一种方法是根据时间戳进行判断


最后写入的,也就是时间戳在后面的,覆盖时间戳在前面的。

v2-735a24e3f4b47e50067614e3ac2bce5a_1440w.jpg

②还有一种冲突的解决方案是通过投票进行解决


(2)主节点挂掉了怎么办?


多主多从模式中,几台主服务器相互监督观察,只要对面的有更新自己也更新;

v2-ac05942e836f9edbf524d92ab9ec1608_1440w.jpg


2、分库分表


(1)垂直分库分表


垂直分表意味着对这个表大部分增删改查的操作需要跨库,系统开销太大,一般不使用。垂直分库也会带来事务等问题,解决办法是2pc


(2)水平分库分表


实现方案如下:


1、根据数值范围


按照时间区间或ID区间来切分。例如:将userId为1~9999的记录分到第一个库,10000~20000的分到第二个库,以此类推。


优点:


(1)单表大小可控

(2)天然便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移

(3)使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题。


缺点:


热点数据可能较为集中,造成压力。


2、根据数值取模


例如:将 Customer 表根据 no字段切分到4个库中,余数为0的放到第一个库,余数为1的放到第二个库,以此类推。


优点:

数据分片相对比较均匀,不容易出现热点和并发访问的瓶颈


缺点:

(1)扩容比较麻烦,新增加一个数据库时,需要重新hash


3、分库分表出现的问题


(1)事务一致性问题(垂直分库问题)


分布式事务


当更新内容同时分布在不同库中,垂直分库,不可避免会带来跨库事务问题。跨分片事务也是分布式事务,没有简单的方案,一般可使用"XA协议"和"两阶段提交"处理。


最终一致性


只要在允许的时间段内达到最终一致性即可,可采用事务补偿的方式。也就是基于日志,进行同步;


2、跨节点关联查询 join 问题(垂直分库问题)


1)全局表


全局表,也可看做是"数据字典表",就是系统中所有模块都可能依赖的一些表,为了避免跨库join查询,可以将这类表在每个数据库中都保存一份。这些数据通常很少会进行修改,所以也不担心一致性的问题。


2)字段冗余


利用空间换时间,为了性能而避免join查询。例如:订单表保存userId时候,也将userName冗余保存一份,这样查询订单详情时就不需要再去查询"买家user表"了。


3)数据组装,多次请求


在系统层面,分两次查询,第一次查询的结果集中找出关联数据id,然后根据id发起第二次请求得到关联数据。最后将获得到的数据进行字段拼装。


4)ER分片


关系型数据库中,如果可以先确定表之间的关联关系,并将那些存在关联关系的表记录存放在同一个分片上,


3、跨节点分页、排序、函数问题(水平分库问题)


(1)分页问题


需要先在不同的分片节点中将数据进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序,最终返回给用户。

v2-054cc5c02dbf81f7600bf27c000aebce_1440w.jpg

(2)函数问题


在使用Max、Min、Sum、Count之类的函数进行计算的时候,也需要先在每个分片上执行相应的函数,然后将各个分片的结果集进行汇总和再次计算,最终将结果返回。


4、分布式ID问题(水平分库问题)


在分库分表的环境中,数据分布在不同的分片上,不能再借助数据库自增长特性直接生成,否则会造成不同分片上的数据表主键会重复。简单介绍下使用和了解过的几种 ID 生成算法。


(1)Twitter 的 Snowflake(又名“雪花算法”)


这种方案把64-bit分别划分成多段,分开来标示机器、时间等,如下图所示:

v2-ceecb6109ae1bc72ebcef9b3dc1a6c41_1440w.jpg

这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。根据这个算法的逻辑,只需要将这个算法用Java语言实现出来,封装为一个工具方法,那么各个业务应用可以直接使用该工具方法来获取分布式ID,只需保证每个业务应用有自己的工作机器id即可,而不需要单独去搭建一个获取分布式ID的应用。


(2)利用zookeeper生成唯一ID


zookeeper主要通过其znode数据版本来生成序列号,可以生成32位和64位的数据版本号,客户端可以使用这个版本号来作为唯一的序列号。


(3)Redis生成ID


这主要依赖于Redis是单线程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY来实现。

OK。有问题还望批评指正。

相关文章
|
2月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
521 3
|
3月前
|
人工智能 Kubernetes 数据可视化
Kubernetes下的分布式采集系统设计与实战:趋势监测失效引发的架构进化
本文回顾了一次关键词监测任务在容器集群中失效的全过程,分析了中转IP复用、调度节奏和异常处理等隐性风险,并提出通过解耦架构、动态IP分发和行为模拟优化采集策略,最终实现稳定高效的数据抓取与分析。
Kubernetes下的分布式采集系统设计与实战:趋势监测失效引发的架构进化
|
13天前
|
缓存 Cloud Native 中间件
《聊聊分布式》从单体到分布式:电商系统架构演进之路
本文系统阐述了电商平台从单体到分布式架构的演进历程,剖析了单体架构的局限性与分布式架构的优势,结合淘宝、京东等真实案例,深入探讨了服务拆分、数据库分片、中间件体系等关键技术实践,并总结了渐进式迁移策略与核心经验,为大型应用架构升级提供了全面参考。
|
29天前
|
存储 NoSQL 前端开发
【赵渝强老师】MongoDB的分布式存储架构
MongoDB分片通过将数据分布到多台服务器,实现海量数据的高效存储与读写。其架构包含路由、配置服务器和分片服务器,支持水平扩展,结合复制集保障高可用性,适用于大规模生产环境。
178 1
|
1月前
|
消息中间件 缓存 监控
中间件架构设计与实践:构建高性能分布式系统的核心基石
摘要 本文系统探讨了中间件技术及其在分布式系统中的核心价值。作者首先定义了中间件作为连接系统组件的"神经网络",强调其在数据传输、系统稳定性和扩展性中的关键作用。随后详细分类了中间件体系,包括通信中间件(如RabbitMQ/Kafka)、数据中间件(如Redis/MyCAT)等类型。文章重点剖析了消息中间件的实现机制,通过Spring Boot代码示例展示了消息生产者的完整实现,涵盖消息ID生成、持久化、批量发送及重试机制等关键技术点。最后,作者指出中间件架构设计对系统性能的决定性影响,
|
6月前
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
242 5
|
4月前
|
监控 算法 关系型数据库
分布式事务难题终结:Seata+DRDS全局事务一致性架构设计
在分布式系统中,CAP定理限制了可用性、一致性与分区容错的三者兼得,尤其在网络分区时需做出取舍。为应对这一挑战,最终一致性方案成为常见选择。以电商订单系统为例,微服务化后,原本的本地事务演变为跨数据库的分布式事务,暴露出全局锁失效、事务边界模糊及协议差异等问题。本文深入探讨了基于 Seata 与 DRDS 的分布式事务解决方案,涵盖 AT 模式实践、分片策略优化、典型问题处理、性能调优及高级特性实现,结合实际业务场景提供可落地的技术路径与架构设计原则。通过压测验证,该方案在事务延迟、TPS 及失败率等方面均取得显著优化效果。
295 61
|
5月前
|
监控 Linux 应用服务中间件
Linux多节点多硬盘部署MinIO:分布式MinIO集群部署指南搭建高可用架构实践
通过以上步骤,已成功基于已有的 MinIO 服务,扩展为一个 MinIO 集群。该集群具有高可用性和容错性,适合生产环境使用。如果有任何问题,请检查日志或参考MinIO 官方文档。作者联系方式vx:2743642415。
1829 57
|
9月前
|
存储 缓存 NoSQL
分布式系统架构8:分布式缓存
本文介绍了分布式缓存的理论知识及Redis集群的应用,探讨了AP与CP的区别,Redis作为AP系统具备高性能和高可用性但不保证强一致性。文章还讲解了透明多级缓存(TMC)的概念及其优缺点,并详细分析了memcached和Redis的分布式实现方案。此外,针对缓存穿透、击穿、雪崩和污染等常见问题提供了应对策略,强调了Cache Aside模式在解决数据一致性方面的作用。最后指出,面试中关于缓存的问题多围绕Redis展开,建议深入学习相关知识点。
646 8
|
5月前
|
消息中间件 缓存 算法
分布式开发:数字时代的高性能架构革命-为什么要用分布式?优雅草卓伊凡
分布式开发:数字时代的高性能架构革命-为什么要用分布式?优雅草卓伊凡
298 0
分布式开发:数字时代的高性能架构革命-为什么要用分布式?优雅草卓伊凡
下一篇
开通oss服务