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

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

三、缓存数据库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。有问题还望批评指正。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
2月前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
2月前
|
存储 JSON 数据库
Elasticsearch 分布式架构解析
【9月更文第2天】Elasticsearch 是一个分布式的搜索和分析引擎,以其高可扩展性和实时性著称。它基于 Lucene 开发,但提供了更高级别的抽象,使得开发者能够轻松地构建复杂的搜索应用。本文将深入探讨 Elasticsearch 的分布式存储和检索机制,解释其背后的原理及其优势。
190 5
|
23天前
|
运维 供应链 安全
SD-WAN分布式组网:构建高效、灵活的企业网络架构
本文介绍了SD-WAN(软件定义广域网)在企业分布式组网中的应用,强调其智能化流量管理、简化的网络部署、弹性扩展能力和增强的安全性等核心优势,以及在跨国企业、多云环境、零售连锁和制造业中的典型应用场景。通过合理设计网络架构、选择合适的网络连接类型、优化应用流量优先级和定期评估网络性能等最佳实践,SD-WAN助力企业实现高效、稳定的业务连接,加速数字化转型。
SD-WAN分布式组网:构建高效、灵活的企业网络架构
|
28天前
|
消息中间件 关系型数据库 Java
‘分布式事务‘ 圣经:从入门到精通,架构师尼恩最新、最全详解 (50+图文4万字全面总结 )
本文 是 基于尼恩之前写的一篇 分布式事务的文章 升级而来 , 尼恩之前写的 分布式事务的文章, 在全网阅读量 100万次以上 , 被很多培训机构 作为 顶级教程。 此文修改了 老版本的 一个大bug , 大家不要再看老版本啦。
|
1月前
|
存储 安全 API
单元化架构,分布式系统的新王!
【10月更文挑战第9天】
107 0
单元化架构,分布式系统的新王!
|
1月前
|
存储 SQL 消息中间件
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
47 0
|
3月前
|
弹性计算 Cloud Native Windows
核心系统转型问题之核心系统需要转型到云原生分布式架构的原因如何解决
核心系统转型问题之核心系统需要转型到云原生分布式架构的原因如何解决
|
3月前
|
机器学习/深度学习 分布式计算 Cloud Native
云原生架构下的高性能计算解决方案:利用分布式计算资源加速机器学习训练
【8月更文第19天】随着大数据和人工智能技术的发展,机器学习模型的训练数据量和复杂度都在迅速增长。传统的单机训练方式已经无法满足日益增长的计算需求。云原生架构为高性能计算提供了新的可能性,通过利用分布式计算资源,可以在短时间内完成大规模数据集的训练任务。本文将探讨如何在云原生环境下搭建高性能计算平台,并展示如何使用 PyTorch 和 TensorFlow 这样的流行框架进行分布式训练。
126 2
|
3月前
|
监控 Java 开发者
随着软件开发的发展,传统单体应用已难以适应现代业务需求,微服务架构因此兴起,成为构建可伸缩、分布式系统的主流
随着软件开发的发展,传统单体应用已难以适应现代业务需求,微服务架构因此兴起,成为构建可伸缩、分布式系统的主流。本文探讨Java微服务架构的设计原则与实践。核心思想是将应用拆分为独立服务单元,增强模块化与扩展性。Java开发者可利用Spring Boot等框架简化开发流程。设计时需遵循单一职责、自治性和面向接口编程的原则。以电商系统为例,将订单处理、商品管理和用户认证等拆分为独立服务,提高可维护性和容错能力。还需考虑服务间通信、数据一致性及监控等高级话题。掌握这些原则和工具,开发者能构建高效、可维护的微服务应用,更好地应对未来挑战。
86 1