这些底层技术才是一个优秀架构师必须掌握的

本文深入探讨了底层技术的重要性,通过实例分析了CPU缓存对程序性能的影响,以及存储设备的访问优化策略,强调了掌握底层技术对软件开发人员的必要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上层应用开发的多了之后,对底层技术的接触就越来越少了。以至于很多人有了“底层技术无用论”的观点。很多人认为学习框架多好啊,大家都在用,跳槽的时候也能用的上。学习那些底层技术干啥,平时都用不到。

本号并不这么认为。我们先举一个活生生的例子,比如我们现在有个Web服务应用,崩溃重启后在绑定套接字的时候出现报错(socket_bind(): unable to bind address [98]: Address already in use。),导致服务端无法工作。问题比较明确,是地址(端口)被占用了。你这时候可能会猜测那个程序占了端口呢?大家都清楚,服务器端口的使用都是严格受限的,肯定是这个程序。但是可能会疑惑:“这个程序不是刚刚起来吗?!”如果你只是使用API,不懂得底层的原理,别说解决问题,可能都不知道如何下手。这个问题我们先放到这里,后面再具体解释,这里只是想说明一下底层技术的重要性

另外一个比较典型的例子是关于前端开发的很多人热衷于学习各种框架。框架虽然能帮助我们解决一些问题,节省开发成本并降低开发周期。但是,学习框架并不能掌握技术的根本,从而导致自己能力没有本质的提升。我们以前端框架为例,在过去的几年当中,JQuery、Bootstrap、Angular和Vue等等等等,轮番上阵。这个框架你还没用熟悉呢,结果又来了一个新的框架,让你应接不暇。而这些框架最本质的东西其实就是JS、CSS和HTML等内容,只有学会这些基础技术,才能游刃有余。如果这些基础技术不熟悉,而投入大量精力学习框架,这就好像还没学会走,就想着跑,最后自己可能摔得满头是包。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsamlieHljVGp2S2ZxYWliSXZzVlVGbnNybTRVdzRkaFZEa1NrZVVBTVZmdHpxdXlCWFNvdkYwU3JnLzY0MD93eF9mbXQ9anBlZyZ0cD13ZWJwJnd4ZnJvbT01Jnd4X2xhenk9MSZ3eF9jbz0x

 

可能扯的有点远,前面的例子只是想告诉大家底层技术的重要性。对于我们搞软件开发的人来说,底层技术其实相当于大厦的地基,地基不稳,大厦是很危险的。当然,计算机技术的细分领域很多,每个领域又有自己的底层技术,因此我们不可能都有涉及。今天我们介绍的底层技术则是最为通用的技术,也就是计算、存储、网络和数据结构与算法

关于计算相关的内容

计算机技术自然核心是计算了。毫不夸张的说,所有应用都要依赖于计算,小到单机小游戏,大到电商或者云计算平台。因此,计算问题自然是我们最为关心的问题了。说到计算,最主要的自然是程序的性能了,如果我们开发的程序的性能提升一倍,就相当于硬件成本降低了50%。对于互联网这种需要大量计算资源的应用,其价值可见一斑。

我们先看一个具体的例子。下面是一段C语言的代码,代码很简单,就是将二维数组中的内容做加一操作。但是如果你测试一下两段代码的耗时的话,就会发现两者有四倍的性能差异。大家可以观察一下图中两端代码的差异,并思考一下为什么有如此之大的差异。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsanQ0VzJZOW5iRzF5SXhzMWI3WWdqc2liQkxlc0FBWFJ1V3hiMFdzQnpCdUpnWFJGWk03a1JoSHcvNjQwP3d4X2ZtdD1qcGVnJnRwPXdlYnAmd3hmcm9tPTUmd3hfbGF6eT0xJnd4X2NvPTE

 

问题先放一下,我们回到我们今天的主角,CPU。CPU是计算依赖的硬件,大家都知道计算是在CPU内完成的。我们先看一下CPU长什么样。CPU是计算机的核心单元,它负责从存储设备读取数据,经过计算后将生成的新数据再存储起来。这就好像一个大型工厂的生产车间,将原材料加工成半成品或者成品(我们后面单独用一个章节介绍CPU相关的内容)。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsakxqRFZTTThaTVYyeEVERzZpY3ZFTW1KbG9HRVRwTjJvVHpBcEJpYzVpY0pvaWFjV3hqMXlCVkFrTGcvNjQwP3d4X2ZtdD1qcGVnJnRwPXdlYnAmd3hmcm9tPTUmd3hfbGF6eT0xJnd4X2NvPTE

了解了CPU的基本功能,我们再解剖了看看它的五脏六腑长什么样。下图是一个简化的CPU内部结构图,最为核心的组件就是计算单元(ALU)、寄存器(很多寄存器)和高速缓存。另外就是通过总线接口与外部的内存进行连接。这里面最核心的组件就是ALU了,其原理很简单,就是完成加减乘除运算。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsanVyOGppYW9HRk9VcXkwaWNqRXkwMHcyTTNCNmVMa1VSVnFpYVJpYmF0bGtTQmxEYkpxSFg3Zng2R1EvNjQwP3d4X2ZtdD1qcGVnJnRwPXdlYnAmd3hmcm9tPTUmd3hfbGF6eT0xJnd4X2NvPTE

 

 

CPU要进行运算,就需要原料,而原料需要从内存搬运。有一个事实我们需要记住,就是访问内存的代价(延时)是访问寄存器的100倍左右。最早的CPU是直接访问内存的,后来随着ALU性能的提升,发现有问题,就在ALU和内存之间增加了缓存。现代CPU缓存通常为3级缓存,分别是L1、L2和L3,其中L1和L2是CPU核独有的,而L3是同一颗CPU的多核共享的。其基本的架构如下图所示。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsakVBWGlhaWNZNHRHbjVVSHVSSGljOVlpYk5pYThZazNacWF6VEVxNUVpYjJ4MTBTZ2lhMjYwYkpCWGpnbkEvNjQwP3d4X2ZtdD1qcGVnJnRwPXdlYnAmd3hmcm9tPTUmd3hfbGF6eT0xJnd4X2NvPTE

 

这里面有个关键问题是缓存的容量是远远小于主(内)存的容量的,因此,缓存中的数据通常是主存数据的很小的一部分。由于应用访问数据有区域局部性的特点,因此缓存中的数据通常是程序需要的数据,也就是ALU接下来要用的数据。另外一个需要注意的地方是从主存读取数据到缓存是有一定粒度(专业术语叫缓存行)的,当前处理器通常是64字节。如下图所示,主存中的内容被读取到缓存中。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsanRzMVlGRWpFaWFYV1EzalR6MUN3RjVXbjRpY29XRHpMMzR2QVdFUEZOVVhDS2JBem5qdUpxcExRLzY0MD93eF9mbXQ9anBlZyZ0cD13ZWJwJnd4ZnJvbT01Jnd4X2xhenk9MSZ3eF9jbz0x

 

然后,我们回到一开始的关于上面两段程序的性能问题来。上面代码中一个是逐行访问二维数组,另外一个是逐列访问二维数组。具体示意图如下图所示。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsanZrT0FHNms5TzBRS05rcEFrcndwT1dLTjNScEkzanVkRWlicjB1ZGN1NDRYMWJ0OHZzNVBnQWcvNjQwP3d4X2ZtdD1qcGVnJnRwPXdlYnAmd3hmcm9tPTUmd3hfbGF6eT0xJnd4X2NvPTE

 

在逐行访问时,访问的地址是以4字节为单位跳跃的,由于缓存行大小是64字节,因此很容易命中缓存。而逐列访问时,每次跳跃4096字节,远远超越了缓存行的大小,从而导致数据大部分是从内存读取的。也正是因为这个,导致两个程序有四倍的性能差异。

通过上面的介绍,我们应该记住两个关键点,一个是访问内存的代价比较高,因此在编程时尽量减少对内存的直接访问;另外一个是充分利用缓存的优势。关于如何做到上面两点,具体细节我们后续专门介绍。

关于存储相关的内容         

数据最终都要存储在存储设备上,否则系统一断电所有东西都丢了,这个道理大家都懂。这里的存储包括磁盘和SSD硬盘等内容。本文主要从存储设备及管理设备的文件系统分析存储相关关键技术。存储中最为重要的有两个方面,一个是存储数据的可靠性,另外一个是存储数据的性能。

本文先从存储的性能说起,可靠性我们后续专门介绍。在存储领域使用最多的还是普通机械磁盘。机械磁盘的内部解剖图如下图所示,其数据的读写是通过一个机械臂完成的。机械臂摆来摆去,想想就知道不会太快。机械磁盘是IBM发明的,第一块磁盘的寻道时间(机械臂定位到目的位置的时间)在600毫秒左右。而现代的机械磁盘寻道时间有了比较明显的改善,但由于其机械特性的原因,其耗时还是比较长的,大概是4-8毫秒的样子。

以下是付费内容

 

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsajM0VTR2RVpZUXMxYmo4TnpRVTdxV1BIaWFzcmJGYzVCbUZyZ29uMm9DdG5yekE1TWZmWVBoaWNnLzY0MD93eF9mbXQ9anBlZyZ0cD13ZWJwJnd4ZnJvbT01Jnd4X2xhenk9MSZ3eF9jbz0x

 

这个耗时是内存的近10万倍,是寄存器耗时的千万倍。因此机械磁盘的速度相对内存来说,无异于蜗牛对高铁的速度鉴于机械磁盘的上述缺陷,在软件层面做了很多考量,从而保证性能最佳。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsanExbUFwWDh2SWlhdnNRYzdpYzdaaWNoTnl0cUpQZ0dHTWZlZUpjend2UGdTRUZvU3RYZEt6OWljaWJBLzY0MD93eF9mbXQ9anBlZyZ0cD13ZWJwJnd4ZnJvbT01Jnd4X2xhenk9MSZ3eF9jbz0x

 

我们通常在使用硬盘的时候不会直接写代码访问(不排除个例),而是通过操作系统提供的接口访问。这个操作系统的接口通常是文件系统的接口。为了便于理解,我们先看一下对于Linux操作系统来说,磁盘系统的整个软硬件栈,从上到下分别是:文件系统、通用块层、设备驱动层和设备层(具体的硬件设备,可以理解为磁盘)。

在这里有两个层面的软件对磁盘的访问做了优化,一个是文件系统,另外一个是通用块层。其中文件系统的核心功能是磁盘数据管理的功能,但考虑到磁盘的缺点,因此在读写数据方法做了一些性能方面的优化。而通用块层则主要是针对磁盘的特性进行了各种优化。

文件系统对磁盘访问的性能优化是通过页缓存(页缓存其实就是内存)完成的,这个页缓存与CPU中的缓存有异曲同工之妙。文件系统通过页缓存在数据写和读两方面分别作了优化

写方面的优化主要是延迟批量写,也就是数据先写到页缓存中,经过积累后再磁盘驱动提交。这种积累和延迟写主要目的是为了增加数据的连续性,也就是为了规避磁盘机械臂的摆动,因为磁盘机械臂摆动是最耗时的。

读方面的优化主要是预读功能,预读就是根据当前应用读取数据的模式,提前将数据读到内存当中。由于应用访问数据的区域局部性特点,这种预读就可以避免应用直接从磁盘读取数据的延时,从而提高读性能。

通用块层的主要作用是针对磁盘做IO调度,通俗的讲就是决定哪个IO先发送到磁盘,哪个后发送到磁盘。

aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9lMTNzdk5DR1VpYXB1Y0o5Yk1GajYxYmRlaWNWUGliVDlsamxpYkh5ZE52NTVaUHFQaWFlSlppY3huTmhzTFNJWXF4N0dTRmJaVnpPVUNmc1NBOGNvaE1Ydm5vQS82NDA_d3hfZm10PWpwZWcmdHA9d2VicCZ3eGZyb209NSZ3eF9sYXp5PTEmd3hfY289MQ

 

针对机械磁盘来说,最为重要的就是通用块层会进行IO的重排序(根据逻辑地址排序)。如上图所示,假设上层应用按时间顺序发送1、2、3、4和5等5个请求的时候。此时,通用块层并不会按照时间顺序发送给磁盘,而是按照图中红色虚线箭头的顺序(1、5、2、4、3)发送给我。这样,磁盘的机械臂就不用来回摆动,从而大大提升其性能。

其实说了半天,这里有一点是需要我们注意的,那就是机械磁盘不善于处理IO地址差异比较大的请求(会导致机械臂频繁摆动),这是我们在做架构设计的时候需要注意的。虽然操作系统和通用块层为我们做了很多工作,但其能力毕竟有限,因此我们在设计的时候也必须考虑。后面我们会通过实例给大家介绍大牛公司在设计应用的时候是如何考虑的。

由于篇幅问题,今天先到这里,后面再给大家介绍其它必备的底层技术。

 

<think>好的,用户现在让我扮演一个优秀的系统架构设计师。之前他询问过学习系统架构需要哪些知识,现在他希望我以这个角色来回答问题。我需要先回顾之前的对话,看看他之前的需求是什么。 在之前的对话中,用户问的是系统架构设计开发需要学习的其他知识,我给出了详细的扩展知识体系,包括底层基础、分布式系统、可观测性、性能优化、安全合规、新兴技术和软技能等。现在用户可能希望我以架构师的身份,提供更具体的设计思路或解决实际问题的方法。 用户当前的问题可能有两个层面:一是验证我是否能够胜任架构师的角色,二是希望得到更贴近实际应用的指导。因此,我的回应需要展示出架构师的思维过程和实际经验,同时保持专业性。 我需要考虑用户可能的深层需求。他可能正在学习系统架构设计,需要实例或方法论来指导实践。作为架构师,我应该强调核心原则、设计流程和常见问题的解决方案,同时提供案例来增强说服力。 接下来,我需要组织回答的结构。通常,系统架构设计会包括核心原则、设计流程、关键技术、案例分析等部分。结合之前的扩展知识,可以分步骤详细说明每个阶段需要考虑的因素,并举例说明如何处理典型问题。 同时,要避免使用过于技术化的术语,保持解释的清晰易懂,但又不失专业性。需要展示出对复杂系统的把控能力,以及在实际项目中权衡不同方案的经验。 可能还需要加入一些软技能的内容,比如沟通协调、风险管理,因为这些也是架构师的重要职责。此外,强调持续学习和适应新技术的能力,以体现架构师的成长性。 最后,确保回答符合用户的要求,使用中文,结构清晰,内容详实,并且以自然的口语化方式表达,避免使用Markdown格式,保持段落简洁。</think>好的,我现在作为系统架构设计师的角色,以实际工作视角为你解析架构设计的核心逻辑和实战方法。以下是系统架构设计的核心要: --- ### **一、架构师的核心思维模式** 1. **全局权衡能力** - **典型场景**: - 选择Redis Cluster(CP)还是Cassandra(AP)时,需评估业务对数据一致性的容忍度(如秒杀库存能否接受短暂误差) - 决定是否引入Service Mesh:当微服务超过50个且跨语言(Java+Go)时,Istio的流量管理收益>性能损耗 2. **抽象建模能力** - **案例实操**: - 设计电商优惠券系统时,用有限状态机(`CouponStatus`)描述「锁定-核销-过期」状态变迁 - 对IM消息系统建模时,区分「必达消息」(RocketMQ事务消息)与「轻量通知」(UDP+重试补偿) 3. **故障推演能力** - **经典Checklist**: - 当ETCD集群节宕机超过半数,如何设计自动逃生方案(如降级到本地缓存模式) - 网络抖动导致Kafka ISR列表频繁收缩时,调整`replica.lag.time.max.ms`参数的副作用评估 --- ### **二、架构设计标准流程(含具体工具链)** 1. **需求分析阶段** - **核心动作**: - 使用**C4模型**绘制上下文图(明确系统与外部支付/物流等系统的边界) - 通过**SLO/SLI定义工具**(如Google的SLOTH模板)量化系统可用性目标(如99.95% = 年故障时间≤4.38小时) 2. **方案设计阶段** - **关键技术选型框架**: ```python def tech_selection(requirements): if 需要强一致性 and 低延迟: return "Redis + Raft协议" elif 海量数据写入 and 高吞吐: return "Kafka + LevelDB存储引擎" else: return "基准测试(如wrk压测API网关)" ``` - 使用**Terraform**快速搭建多region灾备环境 3. **落地实施阶段** - **关键动作**: - 通过**OpenAPI Generator**自动生成SDK,确保各团队接口规范统一 - 在CI/CD流水线中集成**OWASP ZAP**,每次发布自动扫描API漏洞 --- ### **三、典型问题解决范式(附真实案例)** #### **案例1:高并发支付系统设计** - **问题**: 峰值QPS 10万+,要求99.99%事务成功率 - **解决方案**: 1. **分层削峰**: - 接入层:Nginx + Lua脚本实现令牌桶限流 - 队列缓冲:RocketMQ集群承载突发流量(设置优先级队列保障支付指令优先处理) 2. **热账户优化**: - 采用**账户分桶**策略(user_id % 100分散锁竞争) - 使用**Redisson分布式锁** + 锁超时自动降级(Fallback到本地队列) 3. **对账兜底**: - 开发**离线对账系统**,每小时跑批修复最终一致性偏差 #### **案例2:物联网设备百万级连接** - **挑战**: 50万台设备同时在线,需维持平均心跳延迟≤1s - **架构方案**: - **传输层优化**: - 采用MQTT协议(对比HTTP节省70%流量) - 使用**QUIC协议**改善弱网环境下的连接保持 - **连接管理**: - 基于Netty实现**自适应线程模型**(BossGroup=2线程,WorkerGroup=CPU核心数×2) - 设备状态存储:Redis Cluster(Hash结构存储设备元数据) + 时间序列数据库(InfluxDB存储心跳数据) - **弹性扩展**: - 在K8s中配置HPA,根据TCP连接数自动扩容MQTT Broker Pod --- ### **四、架构师生存指南** 1. **技术雷达维护** - 每季度更新技术选型矩阵(如评估**ClickHouse**是否替代原有Hive+Impala方案) - 建立**技术负债看板**,量化技术升级优先级(如Spring Boot 2.x升3.x的ROI分析) 2. **跨团队协同技巧** - 用**决策日志**(ADR文档)对齐各方认知差异 - 设计**架构适应度函数**(如“API响应时间不得增加15%”)约束需求变更范围 3. **个人能力护城河** - 深度掌握至少一个云平台(如AWS的**Serverless架构设计**认证) - 定期研究顶级公司架构(如学习Twitter的**Timeline Service**重构论文) --- ### **五、避坑指南(血泪经验)** 1. **分布式事务陷阱** - 错误做法:为所有操作添加Saga补偿事务 → 系统复杂度爆炸 - 正确做法:区分**关键事务**(用DTX框架)与**普通操作**(异步消息+人工对账) 2. **过度设计预警信号** - 在日均订单1000时引入Kafka → 运维成本远超收益 - 解决方案:建立**技术引入评审卡**(必须证明现有MySQL无法支撑) 3. **性能优化反模式** - 盲目使用缓存导致数据不一致 → 应先通过**Jepsen测试**验证缓存策略 - 过早优化(如为未来3年后的流量设计分库分表)→ 违反YAGNI原则 --- 如果需要解决具体场景的架构问题,可以直接提供业务需求(例如:“设计一个支持千万用户的活动抽奖系统”),我将给出完整技术方案和选型依据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据存储张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值