系统: 一群有关联的个体 , 规则, 能力(产生了新能力)
子系统
模块:逻辑角度 -> 组件复用
组件: 物理角度 -> 单元分离
框架: 组件规范:mvc,等
架构:结构
首先,系统是一群关联的个体组成,这些个体可以是子系统, 模块, 组件等;
其次, 系统中的个体需要根据某种规则运作, 架构需要明确个体运作和协作的规则;
第三,顶层结构的说法可以更好的区分系统和子系统。
三难
机器语言 -> 汇编语言 -> 高级语言
解决软件系统复杂度的问题。
找复杂点
性能, 高可用, 扩展性, 存储高可靠, 安全性,成本。
单机:操作系统
多机:任务分配, 任务分解
无中断
计算高可用:
存储高可用:
传输线路(故障)
状态决策:
变化层, 接口稳定层
抽象层, 实现层
低成本 -> 附加约束
安全
规模: -> 量变引起质变 而带来的复杂度:
选择(不确定性)
列出复杂度问题,排序依次解决;
理解需求,找复杂度 -> 可用排查法,从不同角度逐一分析
每秒TPS , 平均值, 峰值(2-4倍平均值)
设计目标用峰值计算。
复杂度
TPS:写入
QPS:查询
eg. 目前TPS 是115, QPS是1150,
则峰值 x 3, TPS是345, QPS是3450 -> 这个量级不要求高性能;
预留后续发展, 按 峰值 x 4, TPS是 1380, QPS是13800 -> 高性能
不同的公司会有不同的复杂度分析: 安全,或成本
eg. 消息队列
前浪微博:复杂度体现在
常见错误:
1.设计最优秀的方案
2. 只做一个方案
应设计3-5个备选方案。
备选方案差异要比较明显
备选方案的技术不要只限于已熟悉的技术
备选方案不用过于详细 -> 应关注技术选型,而不是细节
eg. 设计备选方案 - > 高性能读取,写入,高可用存储,读取。
1. 用kafka
2. 集群 + mysql 存储
高性能读取,写入 -> Java, 基于netty开发消息队列
高可用存储,读取 -> 服务器的主备方案, mysql的主备复制。
3. 集群 + 自研存储方案
可参考kafka,自己实现一套存储和复制方案。
分场景使用
1. 360度环评表: 列出我们需要关注的质量属性点,分别从这些点的维度评估每个方案,再综合选合适当时情况的最优方案。
常见的方案质量属性点:性能,可用性, 硬件成本, 项目投入,复杂度,安全性,可扩展性,可维护性等。
2. 按优先级选择
确定方案 的关键细节
详细设计方案阶段,可能遇到一种极端情况,发现备选方案不可行。
一般情况下,主要原因是涉及备选方案时遗漏了关键技术点或关键质量属性。
这种情况下,可通过以下方式避免:
本质:将访问压力分散到集群中的多个节点,但没分散存储压力。
基本原理:将数据库读写操作分散到不同的节点上。
主 + 从 集群: 主负责读/写,从负责读。
不同于 主 + 备
基本实现:
有两个复杂点:1. 主从复制延迟; 2. 分配机制
1. 复制延迟
先假设延迟1秒,数据写入主库后立刻访问从库,读取不到最新数,例如注册,会有业务问题。
它的常规解决方法:
2. 分配机制
将读写区分,访问不同的数据库,一般有两种方式:程序代码封装和中间件封装
2.1 程序代码封装:在代码中抽象一个数据访问层,实现读写分离和数据库连接管理。
特点:实现简单,可根据业务做定制化功能;
每个语言实现一次;
故障情况下,如果主从发生切换,则需要所有系统都修改配置并重启;
eg. 淘宝的 TDDL(Taobao Distributed Data Layer)
基本原理:基于集中式的配置Jdbc datasource实现: 有主备,读写分离
基本架构
2.2 中间件封装
指独立出一套系统,实现读写分离和数据库服务器连接的管理。
一般建议,程序语言封装或用成熟中间件
Mysql Router,
Atlas : 基于Mysql Proxy
思考:
读写分离一般用于实现什么场景?支撑多的业务规模?
读多写少,实时性要求不高
分散存储
数据量 从千万到亿 , 就会有单台瓶颈。
业务分库
按业务分到不同的数据库 -> 分库能支撑百万甚至千万规模业务。
存在问题:join 问题(不同库), 事务, 成本等。
分表
垂直拆分, 水平拆分
垂直:
水平:
复杂性
1. 路由 - 范围路由(分布不均匀), hash路由(分布均匀), 配置路由(用单独的表记录路由信息)
2. join
3. count (count相加,记录数表)
4. order by
实现方式:代码封装,中间件封装
not only SQL, 弥补数据库缺陷
关系数据库缺点:
NoSQL分4类
Redis
eg, lpop 移除list第一个元素。
缺点:不支持完整的ACID, 只保证隔离性和一致性,无法保证原子性和持久性。
文档数据库
特点:no-schema,可存储和读取任意数据,大部分是Json格式存储
优点:
适合场景:电商和游戏
缺点:不支持事务,无法join操作。
列式数据库
对比,关系数据库,行式存储:读多列,效率高,能一次完成对一行对个列写操作。
列式数据库:对某个列统计,节省I/O;有更高的压缩比(行:3:1 - 5:1; 列: 8:1, 30:1)
一般将列式存储用在大数据分析和统计场景;主要针对部分单列操作,且写入后无需再更新,删除;
全文搜索引擎
基本原理:倒排索引,是一个索引方法,是建立单词到文档的索引。
使用方式:将关系数据转换为文档数据Json。
Elasticsearch是分布式文档存储方式,每个字段的所有数据默认被索引。
某些复杂场景,单靠存储系统,性能提升是不够的。
a. 要经过复杂运算后得到的数据
b. 读多写少的数据
缓存-基本原理:将可能重复使用的数据放到内存,一次生成,多次使用,避免每次使用都访问存储系统。
mechache 单台支持50000 TPS以上。
缓存-架构设计要点:
1. 缓存穿透:指缓存中没数据,都查了存储系统。
有两种情况:
a. 存储数据不存在 - 解决:可设置一个默认值
b. 缓存数据生成耗费大量的时间或资源
2. 缓存雪崩:指缓存失效后,引起系统性能急剧下降的情况
解决:
a. 更新锁机制:对缓存更新操作加锁保护,集群时需要设置分布式锁。
b. 后台更新:由后台线程更新缓存,而不是业务线程;缓存本身设置有效期永久,后台定时更新;
当内存不足时,会踢掉数据,可有两种办法:
1>. 后台除定时更新缓存,还要频繁读缓存
2>. 业务线程发现缓存失效,通过消息队列发一条消息通知后台线程更新缓存。
还可以用后台更新进行缓存预热。
3. 缓存热点
对于一些特别热点的数据,大部分业务请求都命中同一份缓存数据,则这份数据所在的缓存服务器压力也很大。
缓存热点的解决方案是:复制多份缓存副本,将请求分散到多个缓存服务器,减轻缓存热点导致后台服务器压力。
一个细节要注意:不同的缓存副本不要设置统一的过期时间,应设置一个过期时间范围,不同副本过期时间是指定范围的随机值。
实现方式:
磁盘,操作系统,cpu,内存,网络,编程语言,架构, 都可能影响达到高性能。
架构师要考虑:高性能架构的设计
集中两个点
还和编码有关
架构设计决定系统性能的上限,实现细节决定下限。
单服务器性能关键之一:服务器采用并发模型。
关键设计点:
这两个设计点都和操作系统的I/O模型及进程模型相关。
I/O模型: 阻塞, 非阻塞, 同步,异步
进程模型: 单进程, 多进程, 多线程
单服务器高性能模式:PPC, TPC
PPC
: process per connection ,指每次有新的连接就新建一个进程,去专门处理这个连接的请求。
适合连接没那么多的情况。
缺点:
一般情况下,PPC最多能处理的并发连接数就几百。
prefork -> 提前创建进程
TPC
Thread per connection. 每次有新的连接就新建一个线程,专门处理这个连接的请求。
TPC基本流程:
略
TCP虽然解决了fork代价高和进程通信复杂的问题,但有其他问题:
因此,几百连接的场景,更多用PPC
prethread -> 提前创建线程
PPC和TPC无法支持高并发
Reactor
资源复用,进程池
read阻塞 -> 改为了非阻塞,进程轮询多个连接(当连接太多,轮询效率低)
再改进 -> 只有连接上有数据,进程才去处理 ->