分布式架构的演进
本质
去IOE运动
- 去IBM的存储设备和小型机
- 也即去除Oracle数据库
- 阿里巴巴B2B、淘宝和支付宝都是用大量EMC2的存储设备,也有少量DELL的存储设备,主要是EMC2的存储设备性价比高;
需要干预混乱的系统
无序到有序
分:把系统按照子系统
,模块
,组件
拆分出来
划分边界
- 分与合
- 分
- 混乱系统打散
- 用户中心拆出来
- 抽离公共服务
- 混乱系统打散
- 合
- 分离的组件整合在一起
- 分
分类
业务架构,应用架构,技术架构
- 业务架构
- 业务流程
- 业务模块
- 输入输出
- 业务域
- 应用架构
- 数据交互关系、应用形式、交互方式
- 技术架构
- 平台选型、如操作系统、中间件、设备、多机房、水平扩展、高可用等问题
- 有时候中间件需要按照技术做改造
抽象思维很重要
系统边界
代码重构,工具类
演进过程
任何创业公司问题
- 用更少的人解决更多的问题
- 招合伙人
- 期权
- 招合伙人
- 快速迭代
- 简单架构发展
- 访问量越高
- 技术能力要求越高
- 双十一
- 技术能力要求越高
电商
- 用户(用户注册、用户管理)
- 商品(商品展示、商品管理)
- 交易(下单、支付)这些功能
最开始的架构
.war
放在tomcat+db
这个地方要注意的是,各个功能模块之间是通过 JVM 内部的方法调用来进行交互的,而应用和数据库之间是通过 JDBC 进行访问。
优化
db单独服务器
计算机性能提升
- CPU
- 达到峰值,增加资源效果不高
- 上下文切换
- 文件 IO
- 日志写入
- 磁盘处理速度慢
- 网络 IO
- 带宽不够
- 内存
- 内存泄漏
- 内存不足
垂直扩容
表示通过升级
或者增加单台机器的硬件
来支撑访问量以及数据量增长的方式
- 好处
- 技术难度比较低,运营和改动成本也相对较低。
- 缺点
- 机器性能是有瓶颈的,同时升级高性能的小型机或者大型机,成本是非常大的。这也是阿里去 IOE 的一个原因之一
增加 CPU 核心数:增加 CPU 后系统的服务能力能够得到大的增长,比如响应速度、同时可 以处理的线程数。但是引入 CPU 后也会带来一些显著的问题
- 锁竞争加剧;多个线程同时运行访问某个共享数据,那么就涉及到锁竞争,锁竞争激烈时会导致很多线程都在等待锁,所以即时增加 CPU 也无法让线程得到更快的处理。当然这里是有调优手段的,可以通过调优手段来降低锁竞争
- 支撑并发请求的线程数是固定的,那么即时增加 CPU,系统的服务能力也不会得到提升
- 对于单线程任务,多核心 CPU 是没有太大的作用的
增加内存:
增加内存可以直接提成系统的响应速度,当然,也有可能达不到效果,就是如果JVM 堆内存是固定的。
水平扩容
通过增加机器来支撑访问量及数据量增长的方式,称为水平伸缩,水平伸缩理论上来说没有瓶颈,但是缺点是技术要求比较高,同时给运维带来了更大的挑战垂直伸缩和水平伸缩都有各自的有点,我们在实际使用过程中都会对两者做结合,一方面要考虑硬件升级的成本,一方面要考虑软件改造的成本。
引入负载均衡设备
硬件负载
F5
软件负载
会带来session相关的问题
负载均衡算法
轮询
将请求按顺序轮流分配到后台服务器上,均衡的对待每一台服务器,而不关心服务器实际的连接数和当前的系统负载
缺点: 当集群中服务器硬件配置不同、性能差别大时,无法区别对待
加权轮询
不同的后台服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不一样。跟配置高、负载低的机器分配更高的权重,使其能处理更多的请求,而配置低、负载高的机器,则给其分配较低的权重,降低其系统负载,加权轮询很好的处理了这一问题,并将请求按照顺序且根据权重分配给后端
随机
通过系统随机函数,根据后台服务器列表的大小值来随机选取其中一台进行访问。随着调用量的增大,其实际效果越来越接近于平均分配流量到后台的每一台服务器,也就是轮询法的效果。
优点:简单使用,不需要额外的配置和算法。
缺点:随机数的特点是在数据量大到一定量时才能保证均衡,所以如果请求量有限的话,可能会达不到均衡负载的要求。
源地址哈希法
根据服务消费者请求客户端的 IP 地址,通过哈希函数计算得到一个哈希值,将这个哈希值和服务器列表的大小进行取模运算,得到的结果便是要访问的服务器地址的序号。采用源地址哈希法进行负载均衡,相同的 IP 客户端,如果服务器列表不变,将映射到同一个后台服务器进行访问。
最小连接数
前面几种方式都是通过对请求次数的合理分配最大可能提高服务器的利用率,但是实际上,请求次数的均衡并不能代表负载的均衡。
所以,引入了最小连接数法。它正是根据后端服务器当前的连接情况,动态的选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能的提高后台服务器利用率,将负载合理的分流到每一台服务器。
session问题
session:服务端保存
cookie:客户端保存
在会话开始时,给当前会话分配一个唯一的会话标识(sessionid),然后通过 cookie 把这个标识告诉浏览器,以后在每次请求的时候,浏览器都会带上这个会话标识来告诉 web 服务器请求属于哪个会话。
在 web 服务器上,各个会话有独立的存储,保存不同会话的信息。
如果遇到禁用 cookie 的情况,一般的做法就是把这个会话标识放到 URL 的参数中。
session共享
- session复制
- 缺点
- 网络开销
- 数据变化都要同步到其他机器
- 机器越多,网络开销越大
- 数据变化都要同步到其他机器
- 内存占用
- 网络开销
- 利用成熟的技术做 session 复制,比如 12306 使用的 gemfire,比如常见的内存数据库如 Redis
- 缺点
- session共享
- Session 数据不保存到本机而且存放到一个集中存储的地方,修改 Session 也是发生在集中 存储的地方。Web 服务器使用 Session 从集中存储的地方读取。这样保证了不同 Web 服务 器读取到的 Session 数据都是一样的。存储 Session 的具体方式可以是数据库
- 存在问题:
- 读写 Session 数据引入了网络操作,这相对于本机的数据读取来说,问题就在于存在时延和不稳定性,不过我们的通讯基本都是发生在内网,问题不大。
- 如果集中存储 Session 的机器或者集群有问题,就会影响到我们的应用。
相对于 Session Replication,当 Web 服务器数量比较大、Session 数比较多的时候,这个集中存储方案的优势是非常明显的。
数据库压力变大,读写分离
- 数据如何同步
- master/slave 数据存储方式
- 我们希望通过读库来分担主库上读的压力,那么首先需要解决的是怎么复制到读库的问题。数据库系统一般都提供了数据复制的功能,我们可以直接使用数据库系统自身的机制。不同的数据库系统有不同的支持,比如 Mysql 支持 Master+slave 的结构提供数据复制机制
- 数据如何路由
- 增加一个读库对结构变化产生了一定的影响,也就是我们的应用需要根据不同的情况来选择不同的数据库源
- mycat
- sharding-jdbc
- 需要耦合与代码,使用其语法规则
搜索引擎其实是一个读库
-
lucence
-
solr
-
elasticSearch
- 开源的
- 提供了商业服务
搜索引擎会基于原始数据构建一些关键的索引,我们需要针对
搜索集群的使用方式和读库的使用方式是一样的,只 是构建索引的过程基本都是需要我们自己来实现。
可以从两个纬度对搜索引擎构建索引的方式进行规划,
一个是按照全量/增量划分。
一种是按照实时/非实时划分。
全量方式用于第一次建立索引,可能是新建,也可能是重建。
而增量的方式是在全量的基础上持续更新索引。
实时和非实时提现在索引更新的时间上,实时是最好的,非实时主要考虑到对数据源头的保护。
总的来说,搜索引擎技术解决了站内搜索时某些场景下的读的问题,提供了更好的查询效率。
加速数据读取的利器-缓存及分布式存储
在大型网站中,基本上就是在解决存储和计算的问题,所以存储是一个很重要的支撑系统。网站建设初期我们都是从关系型数据库开始的,而且很多时候为了方便,我们会把一些业务逻辑放在数据库里面去做,比如触发器、存储过程。虽然在前期能够很方便的解决问题,但是在未来的发展过程中会带来很多的麻烦,比如数据量大了以后,要做分库分表操作等. 同时,业务发展到一定的体量以后,对存储的需求不能完全通过关系型数据库来满足
分布式文件系统
对一些图片、大文本,使用数据库就不合适了,所以我们会采用分布式文件系统来实现文件存储,分布式文件系统有很多产品、比如淘宝的 TFS、google 的 GFS。还有开源的 HDFS,oss
NoSQL
NoSQL 我们可以理解成 Not Only SQL、或者是 No SQL。 两种意思都是为了表达在大型网站中,关系型数据库可以解决大部分问题,但是对于不同内容的特征、访问特征、事务特征等对存储的要求是不一样的。
NoSQL 是定位于是文件系统和 SQL 关系型数据库之间的范畴。
hbase,mongoDB,Redis
数据缓存
大型网站内部都会用到一些数据缓存,主要用于分担数据库的读的压力,缓存系统一般是用来保存和查询键值对的。应用系统中一般会把热点数据放入到缓存,而缓存的填充也应该是由应用系统完成。如果数据不存在,则从数据库独处数据后放入缓存。随着时间的推移,当缓存容量不够需要清除数据时,最近不被访问的数据就会被清理掉。还有一种方式就是在数据库的数据发生变化后,主动把数据放入到缓存系统中,这样的好处是数据变化时能够及时更新缓存的数据,不会造成读取失效。
页面缓存
除了数据缓存外,我们还可以对页面做缓存,数据缓存可以加速应用在响应请求时的数据读取数度,但是最终展示给用户的还是页面,有些动态产生的页面或者访问量特别高的页面,我们会对页面或者内容做一些缓存。
弥补关系型数据库的不足,引入分布式存储
我们应用最多的主要还是关系型数据库,但是在有些场景中,关系型数据库不是很合适。所以我们会引入分布式存储系统,比如 redis、mongoDB、cassandra、HBase 等。根据不同的场景和数据结构类型,选择合适的分布式存储系统可以极大提高性能。分布式系统通过集群提供一个高容量、高并发访问、数据冗余融债的支持。
读写分离后,数据库又遇到瓶颈
通过读写分离以及在某些场景用分布式存储系统替换关系型数据库的方式,能够降低主库的压力,解决数据存储方面的问题,不过随着业务的发展,我们的主库也会遇到瓶颈。推演到现在,我们的网站各个模块:交易、商品、用户数据都还是存储在一个数据库。
尽管增加了缓存、读写分离的方式,但是数据库的压力仍然在持续增加,因此我们可以对数据垂直拆分和水平拆分来解决数据库压力问题
专库专用,数据垂直拆分
垂直拆分的意思是把数据库中不同的业务数据拆分到不同的数据库中,那么根据我们推演的例子, 把用户、交易、商品的数据分开
不同业务的数据从原来的一个数 据库拆分到了多个数据库中,那么 就需要考虑到如何处理原来单机 跨业务的事务
- 使用分布式事务解决
- spring cloud alibaba -> fescar(seata)
- RocketMQ
- 去掉事务或者不追求强事务的
- 支持对数据进行垂直拆分后,解决了把所有业务数据放在一个数据库中的压力问题,并且也可以根据不同 业务的特点进行更多的优化
垂直拆分后,遇到瓶颈,数据水平拆分
数据水平拆分就是把同一个表的数据拆分到两个数据库中,产生数据水平拆分的原因是某个业务的数据表的数据量或者更新量达到了单个数据库的瓶颈,这个时候就可以把表拆到两个或者多个数据库中。
数据水平拆分与读写分离的区别是,读写分离解决的是读压力大的问题,对于数据量大或者更新量大的情况并不起作用。
数据水平拆分与数据垂直拆分的区别是,垂直拆分是把不同的表拆分到不同的数据库,而水平拆分是把同一个表拆分到不同的数据库中。
水平拆分带来的影响
- sql 路由问题,需要根据一个条件来决定当前请求发到那个数据库中
- 主键的处理,不能采用自增 id,需要全局 id
- 由于同一个业务的数据被拆分到不同的数据库,因此涉及到一些查询需要跨两个数据库获取,如果数据量太大并且需要分页,就比较难处理了
数据库问题解决后,应用面对的挑战
应用拆开,从一个应用变为两个甚至是多个。
原有问题
- 部署维护困难
- 业务耦合度高
- 性能瓶颈
- 测试麻烦
业务特性拆分
我们还可以按照用户注册、用户登录、用户信息维护等再拆分,变成三个系统,不过这样拆分后在不同系统中会有一些相似的代码,比如用户相关的代码,如何能够保障这部分代码的一致以及如何对其他模块提供复用也是需要解决的问题。
而且,这样拆分出来的新系统之间没有直接的相互调用
服务化
- 代码集中管理,更好维护
- 每个服务可以按照不同的吞吐量要求提升性能
- 方便统一维护
服务监控
- 链路监控zipkin,traceID
- 硬件监控
- CPU
- 内存
- 磁盘
- 全链路压力测试
- Loadrunner
- jmeter
- apache ab工具
- 单节点压力测试
意义
定义
- 组件是分布在网络计算机上
- 组件之间仅仅通过消息传递来通信并协调行动
分布式系统其实也可以认为是一种去中心化的实现思路,对于用户来说是无感知的
意义:
- 升级单机处理处理你能力的性价比越来越低
- 单机处理能力存在瓶颈
- 对于稳定性和可用性的要求
- 我们知道,单台机器的处理能力能力包括 CPU、内存、磁盘和网络。
摩尔定律吧, 当价格不变时,每隔 18 个月,集成电路上课容纳的晶体管数量会增 加一倍,性能也会增加一倍。意味着随着时间的退役,单位成本的支出所能购买的计算能力 在提升,但是我们要求处理器的性能越高,所需要付出的成本也就越高。所以通过不断提升 单台机器的性能锁带来的产值是不划算的,同时,处理器本身也存在性能瓶颈。 还有一个很重要的因素,稳定性和可用性方面,在单机环境中是提供不了的。
所以势必需要分布式系统来解决