浅谈分布式系统 - 架构演进

2023-11-07

目录

1. 架构演进

1.1 单机架构

1.2 什么是分布式架构

1.3 数据库和应用分离

1.4 引入负载均衡

1.5 引入数据库读写分离

1.6 引入缓存

1.7 数据库分库分表

1.8 微服务架构

2. 分布式系统下的常见概念


1. 架构演进

1.1 单机架构

单机架构只有一台服务器, 这个服务器需要负责所有的工作.

假定现在有一个电商网站 :

 当用户想要查看商品列表的时候, 有以下几个步骤 :

  • 应用程序服务器通过 selct 语句发送查询请求给 MySQL 服务器;
  • MySQL 服务器处理请求, 查到数据后返回给应用程序;
  • 应用程序通过 Http 协议响应给用户, 显示到浏览器界面;

-- 单机架构在某些场景下, 也是可以省去数据库的, 比如说有些网站只是介绍一下这个公司, 或者这个公司的产品, 就不太需要数据库, 但是大部分网站还是需要使用到数据库的.

-- 单机架构虽然没有分布式架构听起来那么高大上, 但是千万不要瞧不起这个东西, 毕竟绝大部分公司的产品都是这种单机架构. 随着计算机硬件的发展, 现在一台主机的性能都是非常高的, 一台主机就可以支持非常高的并发和非常大的数据存储, 应付几万个用户还是绰绰有余的, 但是绝大部分中小型公司的用户可能还达不到这个水平.

1.2 什么是分布式架构

--- 随着业务进一步增长, 用户量和数据量都水涨船高了, 一台主机难以应付了, 这时候就需要引入更多的主机, 引入更多的硬件资源.

一台主机的以下几个主要资源是有限的 : 

  • CPU
  • 内存
  • 硬盘
  • 网络

服务器每收到一个请求的时候, 都是需要消耗一台主机的主要资源, 如果同一时刻处理的请求多了, 此时就可能会导致某个硬件资源不够用了, 无论是哪个方面不够用了, 都可能会导致服务器处理请求的时间变长, 甚至于处理出错.

遇到资源不够用的情况该怎么解决 : 

1. 开源 : 增加更多的硬件资源.

    但是一台主机上面能够增加的硬件资源也是有限的, 取决于主板的扩展能力.并且不是说新买来的机器就可以直接解决问题了, 也需要软件上做出对应的调整和适配.

2. 节流 : 做出软件上的优化.

    这个就很考验程序员的经验和技术水平, 需要通过性能测试, 找到是哪个环节出现了瓶颈, 再对症下药.

使用上述开源的方法, 一旦引入多台主机了, 咱们的系统就可以称为是 "分布式系统" 了.

 -- 引入分布式系统其实是无奈之举, 因为分布式架构下, 系统的复杂程度会大大提高, 出现 bug 的概率会变高, 进而咱们加班的概率以及丢失年终奖的概率也随之提高了!!

1.3 数据库和应用分离

前边由于硬件资源的不够用, 导致处理请求的时间过长, 或者请求错误, 所以接下来我们就可以针对应用服务器和数据库服务器分别部署在两台主机上. 

1. 应用服务器里面可能会包含很多业务逻辑, 所以比较吃 "CPU" 和 "内存" 资源. 而硬盘资源使用则相对较少.

2. 数据库服务器则需要更大的硬盘空间, 更快的数据访问速度. 

于是我们可以针对不同服务器的类型, 配置不同的硬件类型. 比如说给应用服务器配置机械硬盘(便宜, 慢), 给数据库配置固态硬盘 (贵, 快), 从而达到一个更高的性价比.

1.4 引入负载均衡

由于应用服务器比较吃 "CPU" 和 "内存", 如果把 CPU 或者内存吃没了, 此时应用服务器就顶不住了. 于是我们就需要引入更多的应用服务器, 才能有效的解决上述问题.

       当用户发送请求过来时, 请求会先到达负载均衡器(网关服务器), 然后负载均衡器再将请求分发给下面的应用服务器. (上图看起来是两个应用服务器, 实际上可能是多个)

       假设有 1w 个用户请求, 有两个应用服务器, 此时在引入负载均衡的前提下, 就可以让每个应用服务器承担 5k 的访问量. 如果应用服务器搞个十台, 八台的, 那不就更加能够降低每一台机器所承担的压力. 看到这, 是不是会觉得 "多线程" 有点像!! 但是多线程还只是局限于一台主机, 局限在一个 CPU 上, 而此处的负载均衡则是引入更多的主机, 引入更多的 CPU, 每一台主机里仍然可以按照多线程的方式去安排任务.

       此处的负载均衡器就有点像一个公司的领导, 要负责管理, 要负责把任务分配给每个员工. 而分配任务需要分配的相对均匀一点, 对于负载均衡器来说, 就会牵扯到很多的负载均衡具体的算法. (例如 : 轮询...)

那是否会出现请求量大到负载均衡器也扛不住了呢 ? 

-- 虽然负载均衡器是一个 "领导", 只需要布置任务, 一般情况下, 不会说处理不过来, 但是处理不过来也是有可能的, 如果真的处理不过来了, 我们就可以引入更多的负载均衡器.

1.5 引入数据库读写分离

      当我们引入更多的应用服务器时, 确实能够处理更高的请求量了, 但随之数据库服务器, 要承担的请求量也就更多了, 那怎么办呢 ? 还是要么开源, 要么节流. 由于节流门槛高, 更复杂, 于是我们可以引入更多的机器.

        当我们引入多台数据库服务器时, 数据库服务器又分为两大类, 一种叫做主数据库(master), 一种叫做从数据库(slave). 主数据库负责写 (增删改), 从数据库负责读(查), 并且从数据库中的数据需要从主数据库中同步, 也就是说上面的应用服务器需要读数据的时候, 就从从数据库中读, 需要写数据的时候, 就在主数据库中写, 同时主数据库也会实时地, 定期地将数据同步到从数据库

        这样从一个数据库既负责读又负责写, 过渡到了两个数据库, 一个数据库负责写, 一个数据库负责读, 从而就把每一台机器的压力就降低了.

        而且实际应用场景中, 读的频率是比写的频率要高很多的, 所以一般使用到数据库分离时, 主数据库服务器一般是一个, 从服务器可以有多个 (一主多从). 同时这多个从服务器又可以使用负载均衡的方式让上面的应用服务器进行访问, 这样又可以进一步降低每一台从服务器的压力.

1.6 引入缓存

由于数据库天然有个问题, 响应速度 "慢" !! 不管怎么进行分库, 它终究是要从硬盘读取数据, 而读硬盘在某些场景下是接受不了的, 所以为了更进一步的提高数据访问效率, 还可以将数据进行 "冷热" 拆分, 其中热点数据放到缓存中, 而缓存的访问速度往往比数据库快很多. 

        根据二八原则: 20% 的数据, 能够支持 80% 的访问量. 所以缓存服务器中只是存储了一小部分热点数据, 也就是会被频繁访问到的数据, 而数据库中存储的仍然是完整的全量数据.  

        既然缓存那么快, 为啥不把数据都存在缓存中呢 ? 前面咱们聊到过, 缓存它虽然快, 但是小, 所以需要缓存服务器和数据库服务器搭配来使用, 这才是在有限成本下的一个比较划算的方案. (Redis 就是出现在缓存服务器这个位置) 

        后续应用服务器想要读数据的时候, 就可以先从缓存中读取, 如果缓存中存在,, 就不需要读数据库了, 如果缓存中不存在, 那么再去读数据库. 由于二八原则, 所以大部分的应用服务器请求都能在缓存中找到答案, 这就是数据库服务器所承担的压力又进一步减小了.

此时读数据读的又快, 同时数据库服务器压力又小!!

【问题】缓存和数据库中存在数据同步, 数据一致性问题 ?

如果后续应用服务器在数据库服务器中将数据修改了, 比如说缓存中存储某件商品的价格 20 元, 后续搞活动, 应用服务器在数据库中将价格改到 15, 这就需要缓存和数据库进行数据同步了. 但是这件事也不容易, 可能数据库中的数据改成功了, 但是缓存中的数据改失败了; 也可能数据库中的数据改失败了, 而缓存中的数据改成功了. 所以这件事情又变得复杂起来了!!

(如何解决后续我学会了再慢慢交代)

1.7 数据库分库分表

咱们引入分布式系统, 不光要能够应对更高的请求量(并发量), 同时也要能够应对更大的数据量. 请求量咱们目前解决了, 那么是否可能会出现, 一台数据库服务器已经存不下数据了呢 ? 答案无疑是肯定的. 虽然一台服务器存储的数据量可以达到几十个 TB, 即使如此也可能会存不下, 比如 : 短视频. 于是我们就需要针对数据库进行分库分表.

        最初我们的架构是一个数据库服务器里头有多个数据库(create database...), 而分库就相当于引入多个数据库服务器, 每个数据库服务器存储一个或者一部分数据库. 

【对应上图】 用户表由一个主服务器来存, 然后引入多个从服务器, 商品表由一个主服务器来存, 然后引入多个从服务器......

         此时应用服务器想要读取用户表, 就可以访问用户表的服务器, 想要读取商品表, 就可以访问商品表的服务器, 当然读数据之前仍然可以先读缓存, 缓存没有再读数据库(主从). 

         如果某一张表特别大, 比如交易表大到一台主机存不下, 我们也可以针对表进行拆分. 可以将交易表拆成多张表, 分别存储到不同的数据库服务器上. 

注意数据库的分库分表操作解决的是数据量过大的问题, 而不是并发问题. 另外数据库分库分表如何实现, 还是要结合实际的业务场景来展开.

1.8 微服务架构

回到应用服务器上面来, 咱们之前的应用服务器, 一个服务器程序里面做了很多的业务 : 用户,商品,交易等等. 这就可能导致这一个服务器的代码变得越来越复杂. 为了更方便于代码的维护, 就可以把这样的一个复杂的服务器, 拆分成更多的, 功能更单一的, 但是更小的服务器. 于是我们就可以引入微服务!!

 由于系统总的功能是一定的, 现在单个服务器变小, 所以服务器的种类和数量就增加了. 用户相关的搞一组服务器, 商品相关的搞一组服务器, 交易相关的再搞一组服务器, 每一组服务器里面可能又有多台服务器来应对更高的请求量, 同时每组服务器各自又有各自的存储集群和缓存模块.

-- 目的 --> 引入微服务是为了解决 "人" 的问题!! 

        当应用服务器复杂了, 势必就需要更多的人来维护了. 人一旦多了, 就需要配套的管理来把这些人给组织好. 如果管理不到位, 搞了那么多人, 结果都在摸鱼, 或者帮倒忙, 这就会适得其反. 并且如果用户, 商品, 交易相关的人员都坐在一块, 到时候用户这边的人搞出来一个问题, 他们又会觉得是商品或者交易那边没做好导致这边出问题, 就会容易扯皮....

        于是我们可以划分组织结构, 将这些人分成多个组, 然后进行分工, 每个组再别配备领导进行管理. 同时呢, 一个组的技术人员就可以专注于一个模块了, 就可以把这个模块把握的更好. 我们的应用服务器就可以按照功能, 拆分成多组微服务, 就可以有利于上述人员的组织结构分配了.

那么啥时候会涉及到人的问题 ??  --> 大厂

如果是小公司, 就那么几个, 十几个开发, 此时搞微服务就没太大的必要了.

【引入微服务的代价1】系统的性能下降

在引入微服务之前, 我们的用户, 商品, 交易这写模块的相互调用, 直接进程内就能调用. 而现在我们需要跨主机通过网络进行通信, 而网络通信这件事情可就比进程内通信慢太多太多了. 

        要想保证性能不下降那么多, 就只能引入更多的机器, 更多的硬件资源, 换句话来说就是 "充钱"!!, 所以说为什么大厂要搞微服务. 不过目前的硬件水平还是很给力的, 网卡现在有个 "万兆网卡", 它的读写速度已经能超过硬盘的读写速度了, 哪怕是在固态硬盘面前, 万兆网卡依然有优势, 所以有了万兆网卡, 虽然系统的性能会有所下降, 但也不至于下降太多.

-- 万兆网卡 [贵] -- 

        使用万兆网卡虽然有防止性能下降过多的效果, 但是不是光一个万兆网卡就完事了, 首先万兆网卡就很贵了, 其次它还得配备万兆交换机, 万兆路由器, 甚至还要配备万兆带宽的网线, 这一套装备下来可得花不少钱, 所以说微服务可不是一些中小公司折腾的起的, 还得是大厂.

【引入微服务的代价2】系统的复杂程度提高, 可用性受到影响

在微服务环境下, 服务器更多了, 出现问题的概率就更大了, 这就需要一系列的手段来保证系统的可用性了. 比如 : 更丰富的监控报警, 配套的运维人员等等..

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浅谈分布式系统 - 架构演进 的相关文章

  • 使用 EVAL、SCAN 和 DEL 的 Redis 通配符删除脚本返回“非确定性命令后不允许写入命令”

    因此 我正在寻求构建一个 lua 脚本 该脚本使用 SCAN 根据模式查找键并删除它们 原子地 我首先准备了以下脚本 local keys local done false local cursor 0 repeat local resul
  • Spring RedisTemplate:8次调用后方法键挂起

    我使用 Spring RedisTemplate spring data redis 1 7 1 与 Redis 进行通信 我需要通过正则表达式获取然后删除键 例如 context user1 我用的方法 RedisTemplate key
  • python 3.5 中的 json.loads 和 Redis

    我使用 json dumps 创建了一个 JSON 对象 并在 Redis 列表中将其 RPUSH ed 当使用 LRANGE redis lrange 返回 JSON 时 我收到一个二进制字符串 b si 00 ff 所以 json lo
  • Stackexchange.redis 缺乏“WAIT”支持

    我在客户端应用程序正在使用的负载均衡器后面有 3 个 Web API 服务器 我正在使用这个库来访问具有一个主服务器和几个从服务器的 Redis 集群 目前不支持 WAIT 操作 我需要此功能来存储新创建的用户会话并等待它复制到所有从属服务
  • 我的 Redis 自动生成的密钥

    我不知道我的 Redis 版本 4 0 9 到底发生了什么 我正在运行一个应用程序并使用 Redis 来存储我的数据库 但是 然后 Redis 自动创建 3 个新键 Backup1 Backup2 Backup3 并删除我的所有数据 这是我
  • redis集群不断打印日志WSA_IO_PENDING

    当我启动redis集群的所有redis服务器时 所有这些服务器不断打印类似WSA IO PENDING clusterWriteDone的日志 9956 03 Feb 18 17 25 044 WSA IO PENDING writing
  • 为什么Redis中不建议使用KEYS?

    在Redis中 建议不要使用按键命令 https redis io commands KEYS 为什么会这样呢 是因为它的时间复杂度是 O N 吗 或者是别的什么原因 我做了下面的实验来证明KEYS命令有多么危险 当带有 KEYS 的一个命
  • 如何在节点redis客户端上设置读取超时?

    在 github 上我没有看到读取超时的选项 https github com NodeRedis node redis https github com NodeRedis node redis There s connect timeo
  • 如何统计 Redis 流中未读或已确认的消息?

    使用 Redis 5 0 3 假设我们创建一个名为streamy和一个消费群体consumers XGROUP CREATE streamy consumers MKSTREAM 然后向其中添加一些消息 XADD streamy messa
  • Spring Data Redis - Lettuce连接池设置

    尝试在 spring data redis 环境中设置 Lettuce 连接池 下面是代码 Bean LettuceConnectionFactory redisConnectionFactory GenericObjectPoolConf
  • Redis 排序集和解决关系

    我正在使用 Redis 排序集来存储我正在处理的项目的排名 我们没有预料到 我们想要如何处理关系 Redis 按字典顺序对具有相同分数的条目进行排序 但我们想要做的是对具有相同分数的所有条目给予相同的排名 例如在以下情况 redis 127
  • Caffeine Expiry 中如何设置多个过期标准?

    我正在使用 Caffeine v2 8 5 我想创建一个具有可变到期时间的缓存 基于 值的创建 更新以及 该值的最后一次访问 读取 无论先发生什么都应该触发该条目的删除 缓存将成为三层值解析的一部分 The key is present i
  • 在 Kubernetes/Openshift 中将客户端-服务器流量保持在同一区域的最佳方法?

    我们运行兼容 Kubernetes OKD 3 11 的本地 私有云集群 其中后端应用程序与用作缓存和 K V 存储的低延迟 Redis 数据库进行通信 新的架构设计将在两个地理上分布的数据中心 区域 之间平均划分工作节点 我们可以假设节点
  • 有没有办法在 ruby​​ 中重新定义 []=+

    我正在尝试编写一个简单的 DSL 针对 Redis 并且我想自己定义 I have def key val redis zadd name val key end 我想定义 def key val redis zincrby name va
  • Java 将字节转换为二进制安全字符串

    我有一些以字节为单位的数据 我想将它们放入Redis中 但是Redis只接受二进制安全字符串 而我的数据有一些二进制非安全字节 那么如何将这些字节转换为二进制安全字符串以便将它们保存到 Redis 中呢 Base64 对我有用 但它使数据更
  • Redis、会话过期和反向查找

    我目前正在构建一个网络应用程序 并想使用 Redis 来存储会话 登录时 会话会使用相应的用户 ID 插入到 Redis 中 并且过期时间设置为 15 分钟 我现在想实现会话的反向查找 获取具有特定用户 ID 的会话 这里的问题是 由于我无
  • Laravel 所有会话 ID 与 Redis 驱动程序

    在我的应用程序中 我希望允许某些用户能够注销除他 她之外的所有其他用户 当会话驱动程序设置为文件时 我已经完成了此功能 但现在我使用 redis 作为会话驱动程序 并且我无法找到任何方法来列出所有当前会话 就像我在文件时所做的那样司机 问题
  • 有没有办法让特定的key在集群模式下定位到特定的redis实例上?

    我想让我的多锁位于不同的redis实例上 我发现redission可以指定一个实例来执行命令 但是如果该命令与key相关 则指定的实例会将命令传输到另一个实例 你能给我一些建议吗 你可以 但这并不是微不足道的 首先 Redis 在键中使用大
  • StackExchange.Redis的正确使用方法

    这个想法是使用更少的连接和更好的性能 连接会随时过期吗 对于另一个问题 redis GetDatabase 打开新连接 private static ConnectionMultiplexer redis private static ID
  • Scala 使用的 Redis 客户端库建议

    我正在计划使用 Scala 中的 Redis 实例进行一些工作 并正在寻找有关使用哪些客户端库的建议 理想情况下 如果存在一个好的库 我希望有一个为 Scala 而不是 Java 设计的库 但如果现在这是更好的方法 那么仅使用 Java 客

随机推荐

  • Vuforia 中在脚本中添加识别图被识别和丢失的回调

    start Vuforia 的unity sdk中有一个重要的脚本 DefaultTrackableEventHandler cs 这个脚本在场景中拖入ImageTarget的预制体时会自动挂载 using UnityEngine usin
  • 快速排序【模板+边界处理】

    全文目录 快速排序的思想 快速排序演示图 代码模板 i 和 j 的取值和循环处理 i 和 j 的取值 循环条件判断 边界问题 什么是边界问题 如何处理边界问题 为什么要这样处理边界问题 区间划分问题 取最值问题 mid 取中间值时是否需要
  • LLVM学习之基础知识

    介绍 学习LLVM与代码混淆技术课程总结出来的经验 希望能够帮助到大家理解 1 我们首先学会LLVM的基础知识 2 如何搭建LLVM的环境 3 编译一个Hello world 4 LLVM IR基本语法 LLVM搭建环境 Ubuntu 20
  • 让AI生成的图像转化为视频

    Hello 让AI生成图像相信都不陌生 毕竟相关内容铺天盖地 如何让AI生成的图像再转化为视频呢 让图像动起来 让图像去说话 或许是一件非常有趣的一件事情呢 那么 我们接下来要不要去试一试 马上去体验一下具有前瞻性的前沿科技呢 我想 你一定
  • 48脚STM32内部基准电压校准ADC的一些心得记录

    STM32的48脚的单片机因为没有Vref Vref 所以我们使用一些高精度参考电压芯片来提高ADC的精度很不方便 这里还有一种方法可以参考下 STM32内部有一个专门用于校准的稳压器VREFINT 它由外部的VSSA供电 他的电压一般为1
  • k8s-day1-k8s简介及基础架构

    1 K8S简介 Kubernetes是容器集群管理系统 是一个开源的平台 可以实现容器集群的自动化部署 自动扩缩容 维护等功能 Kubernetes是一个完备的分布式系统支撑平台 具有完备的集群管理能力 多扩多层次的安全防护和准入机制 多租
  • python 程序员进阶之路:从新手到高手的100个模块

    在知乎和CSDN的圈子里 经常看到 听到一些 python 初学者说 学完基础语法后 不知道该学什么 学了也不知道怎么用 一脸的茫然 近日 CSDN的公众号推送了一篇博客 题目叫做 迷思 Python 学到什么程度可以面试工作 真实反映了
  • win电脑主板设置的Bios密码清除方法

    bios保存着计算机最重要的基本输入输出的程序 开机后自检程序和系统自启动程序 它可从CMOS中读写系统设置的具体信息由于我们一般很少需求进入BIOS设置 因此久而久之 很多朋友容易忘记Bios密码 那么Bios密码忘记了怎么办呢 这是最近
  • opencv中 aplacian算子、Scharr和Sobel算子的API参数

    1 Laplacian算子 cv2 Laplacian src ddepth dst ksize scale delta borderType src 输入图像 通常为灰度图像 单通道 ddepth 输出图像的深度 一般使用cv2 CV 6
  • Spring中有哪些情况会导致@Bean注入失效呢?

    转自 Spring中有哪些情况会导致 Bean注入失效呢 下文笔者讲述Spring中导致 bean注入失败的相关情况说明 如下所示 实现思路 1 component scan扫描路径配置错误 2 Conditional修饰条件 3 bean
  • 学习swift的资料

    https developer apple com library content documentation Swift Conceptual Swift Programming Language CollectionTypes html
  • 数据库表创建索引

    创建一个表 创建名为 Person 的表 有四个列 列名是 LastName FirstName Address 以及 Age 定义列的长度 CREATE TABLE Person LastName varchar 30 FirstName
  • 使用scrapy框架爬取51job的关于python的职位,并且进行分析

    example py 爬虫的主文件 大部分的爬虫逻辑都在这 coding utf 8 import scrapy import pyecharts from items import Scrapy3Item class ExampleSpi
  • cpp: Strategy Pattern

    Gold h 此文件包含 Gold 类 策略模式 Strategy Pattern C 14 2023年5月1日 涂聚文 Geovin Du Visual Studio 2022 edit pragma once ifndef GOLD H
  • MySql中的数据修改

    1 insert插入操作 单行插入的语法格式 INSERT INTO student 列表1 列表2 列表3 列表4 列表5 列表6 VALUES 值1 值1 值1 值1 值1 值1 INSERT INTO student id name
  • python赋值、深拷贝和浅拷贝的区别详解

    一 前言 在python中 对象赋值实际上是对象的引用 当创建一个对象 然后把它赋值给另一个变量的时候 python并没有拷贝这个对象 而只是拷贝了这个对象的引用 二 区别 1 直接赋值 默认浅拷贝传递对象的引用而已 原始列表改变 被赋值的
  • 使用企业微信登录小程序

    概述 当小程序在企业微信端运行时 需要通过对应的登录接口获取到当前企业微信用户在当前企业的员工身份信息 开发者需特别关注 当小程序在微信端运行时由微信派发和验证code参数 当小程序在企业微信端运行时由企业微信派发和验证code参数 两个平
  • vue+element-ui实现一键切换皮肤

    element ui可以自己定义主题并下载 选择好自己想要的主题 下载到本地 我下载了一套暗黑模式 一套默认的用来白天黑夜模式切换 文件目录如下 在项目的index html文件中 在sideBar vue页面中 新增下拉框选择模式
  • 快速搭建TP6-02

    快速搭建TP6 02 1 配置多应用config app php return 应用地址 app host gt env app host 应用的命名空间 app namespace gt 是否启用路由 with route gt true
  • 浅谈分布式系统 - 架构演进

    目录 1 架构演进 1 1 单机架构 1 2 什么是分布式架构 1 3 数据库和应用分离 1 4 引入负载均衡 1 5 引入数据库读写分离 1 6 引入缓存 1 7 数据库分库分表 1 8 微服务架构 2 分布式系统下的常见概念 1 架构演