大型网站架构演化
打造一个高可用、高性能、易扩展、可伸缩且安全的网站。
大型网站软件系统的特点
高并发、大流量
:需要面对高并发用户,大流量访问。高可用
:系统7×24小时不间断服务。海量数据
:需要存储、管理海量数据,需要使用大量服务器。用户分布广泛,网络情况复杂
:许多大型互联网都是为全球用户提供服务的,用户分布范围广,各地网络情况千差万别。安全环境恶劣
:由于互联网的开放性,使得互联网站更容易受到攻击,大型网站几乎每天都会被黑客攻击。需求快速变更,发布频繁
:和传统软件的版本发布频率不同,互联网产品为快速适应市场,满足用户需求,其产品发布频率是极高的。渐进式发展
:与传统软件产业或企业应用系统一开始就规划好全部的功能和非功能需求不同,几乎所有的大型互联网站都是从一个小网站开始,渐进地发展起来的。
大网站架构演化发展历程
1. 初始阶段的网站架构
应用程序、数据库、文件等所有的资源都在一台服务器上。
2. 应用服务和数据服务分离
应用服务器
:需要处理大量的业务逻辑,因此需要更快更强大的CPU数据库服务器
:需要快速磁盘检索和数据缓存,因此需要更快的硬盘和更大的内存文件服务器
:需要存储大量用户上传的文件,因此需要更大的硬盘
3. 使用缓存改善网站性能
缓存在应用服务器上的本地缓存
:访问速度更快,但是受应用服务器内存限制,其缓存数据量有限,而且会出现和应用程序争内存的情况。缓存在专门的分布式缓存服务器上的远程缓存
:远程分布式缓存可以使用集群的方式,部署大内存的服务器作为专门的缓存服务器,可以在理论上做到不受内存容量限制的缓存服务。
4. 使用应用服务器集群改善网站的并发处理能力
负载均衡调度服务器
:将来自用户浏览器的访问请求分发到应用服务器集群中的任何一台服务器上。
5. 数据库读写分离
主从热备功能
:通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到另一台服务器上。
应用服务器在写数据库的时候,访问主数据库,主数据库通过主从复制机制将数据更新同步到从数据库,这样当应用服务器读数据的时候,就可以通过从数据库获得数据。为了便于应用程序访问读写分离后的数据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分离对应用透明。
6. 使用反向代理和CDN加速网站响应
CDN和反向代理
:基本原理都是缓存,区别在于CDN部署在网络提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络提供商机房获取数据;反向代理则部署在网站的中心机房,当用户请求到达中心机房后,首先访问的服务器是反向代理服务器,如果反向代理服务器中缓存着用户请求的资源,就将其直接返回给用户。
7. 使用分布式文件系统和分布式数据库系统
分布式数据库
:是网站数据库拆分的最后手段,只有在表单数据规模非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段是业务分库,将不同业务的数据库部署在不同的物理服务器上。
8. 使用NoSQL和搜索引擎
NoSQL和搜索引擎
:源自互联网的技术手段,对可伸缩的分布式特性具有更好的支持。应用服务器则通过一个统一数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。
9. 业务拆分
业务拆分
:根据产品线划分,将一个网站拆分成许多不同的应用,每个应用独立部署维护。应用之间可以通过一个超链接建立关系(在首页上的导航链接每个都指向不同的应用地址),也可以通过消息队列进行数据分发,当然最多的还是通过访问同一个数据存储系统来构成一个关联的完整系统。
10. 分布式服务
分布式服务
:可以将应用系统中共用的业务提取出来,独立部署。这些可复用的业务连接数据库,提供共用业务服务,而应用系统只需要管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作。
大型网站架构演化的价值观
大型网站架构技术的核心价值是随网站所需灵活应对
驱动大型网站技术发展的主要力量是网站的业务发展
网站架构设计误区
一味追随大公司的解决方案
为了技术而技术
企图用技术解决所有问题
大型网站架构模式
模式的关键在于模式的可重复性,问题与场景的可重复性带来解决方案的可重复使用。
网站架构模式
为了解决大型网站面临的高并发访问、海量数据处理、高可靠运行等一系列问题与挑战,大型互联网公司在实践中提出了许多解决方案,以实现高性能、高可用、易伸缩、可扩展、安全等各种技术架构目标。这些解决方案又被更多网站重复使用,从而逐渐形成大型网站架构模式。
1. 分层
层级 | 功能 |
---|---|
应用层 | 负责具体业务和试图展示 |
服务层 | 为应用层图个服务支持 |
数据层 | 提供数据存储访问服务 |
在实践中,大的分层结构内部还可以继续分层,如:
应用层
视图层(美工负责)
业务逻辑层(工程师负责)
服务层
数据接口层(适配各种输入和输出的数据格式)
逻辑处理层
2. 分割
如果说分层是将软件在横向方面进行切分,那么分割就是在纵向方面对软件进行切分。
3. 分布式
对于大型网站,分层和分割的一个主要目的是为了切分后的模块便于分布式部署,即将不同模块部署在不同的服务器上,通过远程调用协同工作。
分布式解决网站高并发带来问题:
分布式意味着服务调用必须通过网络,这可能对性能造成比较严重的影响。
服务器越多,服务器宕机的概率也就越大,一台服务器宕机造成的服务器不可用可能会导致很多应用不可访问,使网站可用性降低。
数据在分布式环境中保持数据一致性也非常困难。
分布式导致网站依赖错综复杂,开发管理维护困难。
在网站应用中,常用的分布式方案有以下几种:
分布式应用和服务
:将分层和分割后的应用和服务模块分布式部署。改善网站性能和并发性
加快开发和发布速度
减少数据库连接资源消耗
使不同应用复用共同的服务
便于业务功能的扩展
分布式静态资源
:网站的静态资源独立分布式部署,并采用独立的域名,“动静分离”。减轻应用服务器的负载压力
加快浏览器并发加载的速度
有利于网站分工合作
分布式数据和存储
:传统关系数据库和各种NoSQL几乎都是分布式的。分布式计算
:目前网站普遍使用Hadoop及其MapReduce分布式计算框架进行此类批处理计算,其特点是移动计算而不是移动数据,将计算程序分发到数据所在位置以加速计算和分布式计算。分布式配置
:支持网站线上服务器配置实时更新。分布式锁
:分布式环境下实现并发和协同。分布式文件
:支持云存储。
4. 集群
使用分布式已经将分层和分割后的模块独立部署,对于用户访问集中的模块(如网站首页),还需要将独立部署的服务器集群化,即多台服务器部署相同应用构成一个集群,通过负载均衡设备共同对外提供服务。
5. 缓存
缓存就是将数据存放在距离计算最近的位置以加快处理速度。缓存是改善软件性能的第一手段。
CDN
:即内容分发网络,部署在距离用户最近的网络服务商,用户的网络请求总是先到达网络服务商那里,在这里缓存网站的一些静态资源(较少变化的数据),就可以以最快速度返回给用户。反向代理
:属于网站前端架构的一部分,部署在网站的前端,当用户请求到达网站的数据中心时,最先访问到的就是反向代理服务器,这里环迅网站的静态资源,无需将请求继续转发给应用服务器就能返回给用户。本地缓存
:在应用服务器本地缓存着热点数据,应用程序可以在本机内存中直接访问数据,而无需访问数据库。分布式缓存
:将数据缓存在一个专门的分布式缓存集群中,应用程序通过网络通信访问缓存数据。
使用缓存两个前提条件:
数据访问热点不均衡,某些数据会被更频繁的访问,这些数据应该放在缓存中。
数据在某个时间段内有效,不会很快过期,否则缓存的数据会因已经失效而产生脏读,影响结果的正确性。
6. 异步
系统解耦的手段除了分层、分割、分布等,还有异步。业务之间的消息传递不是同步调用,二十将一个业务操作分成多个阶段,每个阶段之间通过共享数据的方式异步执行进行协作。
在单一服务器内通过多线程共享内存队列的方式实现异步。
在分布式系统中多个服务器集群通过分布式消息队列实现异步,分布式消息队列可看做内存队列的分布式部署。
异步架构是典型的生产者消费者模式,两者不存在直接调用,只要保持数据结构不变,彼此功能实现可以随意变化而不互相影响,这对网站扩展新功能非常便利。除此之外,使用异步消息队列还有如下特性:
提高系统可用性
:消费者服务器发生故障,数据会在消息队列服务器中存储堆积,生产者服务器可以继续处理业务请求,系统整体表现无故障。消费者服务器回复正常后,继续处理消息队列中的数据。加快网站响应速度
:处在业务处理前端的生产者服务器在处理完业务请求后,将数据写入消息队列,不需要等待消费者服务器处理就可以返回,响应延迟减少。消除并发访问高峰
:使用消息队列将突然增加的访问请求数据放入消息队列中,等待消费者服务器依次处理,就不会对整个网站造成太大压力。
7. 冗余
访问和负载很小的服务也必须部署至少两台服务器构成一个集群,母的是通过冗余实现服务器高可用。数据库除了定期备份,存档保存,实现
冷备份
外,为了保证在线业务高可用,还需要对数据库进行主从分离,实时同步实现热备份
。
8. 自动化
- 发布过程自动化
- 自动化代码管理
- 自动化测试
- 自动化安全检测
- 自动化部署
- 自动化监测
- 自动化报警
- 自动化失效转移
- 自动化失效恢复
- 自动化降级
- 自动化分配资源
9. 安全
- 通过
密码
和手机校验码
进行身份认证 - 登录、交易等操作需要对网络通信进行
加密
- 防止机器人程序攻击网站,使用
验证码
识别 - 对于常见的用于
攻击
网站的XSS攻击、SQL注入等相应处理 - 对于垃圾信息、敏感信息进行
过滤
- 对交易转账等重要操作根据交易模式和交易信息进行
风险控制
架构模式在新浪微博的应用
基础服务层
:提供数据库、缓存、存储、搜索等数据服务,以及其他一些基础技术服务,这些服务支撑了新浪微博的海量数据和高并发访问,是整个系统的技术基础。中间层(平台服务和应用服务层)
:新浪微博的核心服务是微博、关系和用户,这些服务被分割成为独立的服务模块,通过依赖调用和共享基础数据构成呢个的新浪微博业务基础。API和业务层
:各种客户端(包括Web网站)和第三方应用,通过调用API集成到新浪微博的系统中,共同组成一个生态系统。
这些被分层和分割后的业务模块与基础技术模块分布式
部署,每个模块都部署在一组独立的服务器集群
上,通过远程调用的方式进行依赖访问。
小结
好的设计绝对不是模仿,不是生搬硬套某个模式,而是对问题深刻理解之上的创造与创新。
大型网站核心架构要素
架构:最高层次的规划,难以改变的命运
软件架构:有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。
软件架构:性能、可用性、伸缩性、扩展性和安全性。
性能
浏览器端
:通过浏览器缓存、使用页面压缩、合理布局页面、减少Cookie传输等手段改善性能。CDN
:将网站静态内容分发至离用户最近的网络服务商机房,使用户通过最短访问路径获取数据。反向代理服务器
:缓存热点文件,加快请求响应速度,减轻应用服务器负载压力。应用服务器端
:本地缓存和分布式缓存,通过缓存在内存中的热点数据处理用户请求,加快请求处理过程,减轻数据库负载压力异步操作
:将用户请求发送至消息队列等待后续任务处理,而当前请求直接返回响应给客户。集群
:将多台服务器组成一个集群共同对外服务,提高整体处理能力,改善性能。代码层面
:通过使用多线程、改善内存管理等手段优化性能。数据库服务器端
:索引、缓存、SQL优化等性能优化手段已经比较成熟,NoSQL数据库通过优化数据模型、存储结构、伸缩性等手段在性能方面的优势日趋明显。
可用性
网站高可用框架设计的前提是必然会出现服务器宕机,而高可用设计的目标就是当服务器宕机的时候,服务或者应用依然可用。
网站高可用的主要手段是冗余,应用部署在多台服务器上同时提供访问,数据存储在多台服务器上互相备份,任何一台服务器宕机都不会影响应用的整体可用,也不会导致数据丢失。
对应用服务器
:多台服务器通过负载均衡设备组成一个集群共同对外提供服务,任何一台服务器宕机,只需要把请求切换到其他服务器就可以实现应用的高可用,但是一个前提条件是应用服务器上不能保存请求的会话信息。对存储服务器
:需要对数据进行实时备份,当服务器宕机时需要将数据访问转移到可用的服务器上,并进行数据恢复以保证继续有服务器宕机的时候数据依然可用。对软件质量保证
:通过预发布验证、自动化测试、自动化测试、自动化发布、灰度发布等手段,减少将故障引入线上环境的可能,避免故障范围扩大。
伸缩性
伸缩性是指通过不断向集群中加入服务器的手段来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。
衡量架构伸缩性的主要标准就是是否可用多台服务器构建集群,是否容易向集群中添加新的服务器。加入新的服务器后是否可以提供和原来服务器无差别的服务。集群中可容纳的总的服务器数量是否有限制。
对于应用服务器集群
:只要服务器上不保存数据,所有的服务器都是对等的,通过使用合适的负载均衡设备就可以向集群中不断加入服务器。对于缓存服务器集群
:加入新的服务器可能会导致缓存路由失效,进而导致集群中大部分缓存数据都无法访问。虽然缓存的数据可以通过数据库重新加载,但是如果应用已经严重依赖缓存,可能会导致整个网站崩溃。需要改进缓存路由算法保证缓存数据的可访问性。对于关系数据库
:虽然支持数据复制,主从热备等机制,但是很难做到大规模集群的可伸缩性,因此关系数据库的集群伸缩性方案必须在数据库之外实现,通过路由分区等手段将部署有多个数据库的服务器组成一个集群。对于NoSQL数据库
:为海量数据而生,因此其对伸缩性的支持通常非常好,可以做到在较少运维参与的情况下实现集群规模的线性伸缩。
扩展性
网站的扩展性架构直接关注网站的功能需求。
衡量网站架构扩展性好坏的主要标准就是在网站增加新的业务产品时,是否可以实现对现有产品透明无影响,不需要任何改动或者很少改动既有业务功能就可以上线新产品。
事件驱动架构
:通常利用消息队列实现,将用户请求和其他业务时间构造成消息发布到消息队列,消息的处理者作为消费者从消息队列中获取消息进行处理。通过这种方式将消息产生和消息处理分离开来,可以透明地增加新的消息生产者任务或者新的消息消费者任务。分布式服务
:将业务和可复用服务分离开,通过分布式服务框架调用。新增产品可以通过调用可复用的服务实现自身的业务逻辑,而对现有产品没有任何影响。可复用服务升级变更的时候,也可以通过提供多版本服务对应用实现透明升级,不需要强制应用同步变更。开放平台接口
:吸引第三方开发者,调用网站服务,使用网站数据开发周边产品,扩展网站业务。
安全性
网站的安全架构就是保护网站不受恶意访问和攻击,保护网站的重要数据不被窃取。
小结
性能、可用性、伸缩性、扩展性和安全性是网站架构最核心的几个要素。
瞬时响应:网站的高性能架构
网站性能是客观指标,可以具体体现到响应时间、吞吐量等技术指标,同时也是主观的感受,而感受则是一种与参与者相关的微妙的东西,用户的感受和工程师的感受不同,不同的用户感受也不同。
网站性能测试
不同视角下的网站性能
1. 用户视角的网站性能
从用户角度,网站性能就是用户在浏览器上直观感受到的网站响应素的快还是慢。
用户感受到的时间,包括用户计算机和网站服务通信的时间、网站服务器处理的时间、用户计算机浏览器构造请求解析响应数据的时间。
在实践中使用一些前端优化手段:
- 优化HTML式样
- 利用浏览器端的并发和异步特性
- 调整浏览器缓存策略
- 使用CDN服务
- 反向代理
2. 开发人员视角的网站性能
开发人员关注的主要是应用程序本身及其相关子系统的性能,包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标。
主要优化手段:
- 缓存加速数据读取
- 使用集群提高吞吐能力
- 使用异步消息加快请求响应及实现削峰
使用代码优化手段改善程序性能
- 运维人员视角的网站性能
主要优化手段:
- 建设优化骨干网
- 使用高性价比定制服务器
- 利用虚拟化技术优化资源利用
性能测试指标
1. 响应时间
响应时间是系统最重要的性能指标,直观地反映了系统的“快慢”。
实践中通常采用的办法是重复请求,得到单次请求的响应时间。
2. 并发数
指系统能够同时处理请求的数目,这个数字也反映了系统的负载特性。
网站系统用户数 >> 网站在线用户数 >> 网站并发用户数
测试程序通过多线程模拟并发用户的办法来测试系统的并发处理能力,为了真实模拟用户的行为,测试程序并不是启动多线程然后不停地发送请求,而是在两次请求之间加入一个随机等待时间,这个时间被称作思考时间
。
3. 吞吐量
指单位时间内系统处理的请求数量,体现系统的整体处理能力。
TPS(每秒事物数)是吞吐量的一个常用量化指标,还有HPS(每秒HTTP请求数、QPS(每秒查询数)等。
在系统并发数由小逐渐增大的过程中(伴随着服务器系统资源消耗逐渐增大),系统吞吐量先是逐渐增加,达到一个极限后,随着并发数的增加范儿下降,达到系统崩溃点后,系统资源耗尽,吞吐量为零。
这个过程中,响应时间现实小幅度上升,达到吞吐量极限后,快速上升,到达系统崩溃点后,系统失去响应。
4. 性能计数器
性能计数器是描述服务器或操作系统性能的一些数据指标,包括System Load、对象与线程数、内存使用、CPU使用、磁盘与网络I/O等指标
System Load
:即系统负载,指当前正在被CPU执行和等待被CPU执行的进程数目总和,是反映系统忙闲程度的重要指标。
性能测试方法
性能测试
:以系统设计初期规划的性能指标为预期目标,对系统不断施加压力,验证系统在资源可接受范围内,是否能达到性能预期。负载测试
:对系统不断地增加并发请求以增加系统压力,直到系统的某项或多项性能指标达到安全临界值。压力测试
:查过安全负载的情况下,对系统继续施加压力,直到系统崩溃或不能再处理任何请求,以此获得系统最大压力承受能力。稳定性测试
:被测试系统在特定硬件、软件、网络环境条件下,给系统加载一定业务压力,使系统运行一段较长时间,以此检测系统是否稳定。在不同生产环境、不同时间点的请求压力是不均匀的,呈波浪特性,因此为了更好地模拟生产环境,稳定性测试也应不均匀地对系统施加压力。
性能测试遵循如图所示抛物线规律:
横坐标表示消耗的系统资源,纵坐标表示系统处理能力(吞吐量)。
a~b段
:网站的日常运行区间c点
:系统最大负载点d点
:系统崩溃点
与性能曲线相对应的是用户访问的等待时间(系统响应时间),如:
在日常运行区间,可以获得最好的用户响应时间,随着并发用户数的增加,响应延迟越来越大,直到系统崩溃,用户失去响应。
性能测试报告
测试报告结果反映上述性能测试曲线规律,简单示例:
性能优化策略
1. 性能分析
检查请求处理的各个环节的日志,分析哪个环节响应时间不合理、超过预期。
检查监控数据,分析影响性能的主要因素是内存、磁盘、网络、还是CPU,是代码问题还是架构设计不合理,或者系统资源确实不足。
2. 性能优化
Web前端性能优化
应用服务器性能优化
存储服务器性能优化
Web前端性能优化
一般说来Web前端指网站业务逻辑之前的部分,包括浏览器加载、网站视图模型、图片服务、CDN服务等,主要优化手段有优化浏览器访问、使用反向代理、CDN等。
浏览器访问优化
1. 减少http请求
主要手段是合并CSS、合并Javascript、合并图片。
将浏览器一次访问需要的Javascript、CSS合并成一个文件,这样浏览器只需要一次请求。
图片也可以合并,多张图片合并成一张,如果每张图片都有不同的超链接,可通过CSS偏移响应鼠标点击操作构造不同的URL。
2. 使用浏览器缓存
将更新频率比较低的、使用频率比较高的文件缓存在浏览器中,可以极好地改善性能。通过设置HTTP头中的Cache-Control和Expires的属性,可设定浏览器缓存,缓存时间可以是数天,甚至是几个月。
某些时候,静态资源文件变化需要及时应用到客户端浏览器,可以通过改变文件名实现,即更新Javascript文件并不是更新Javascript文件内容,而是生成一个新的Javascript文件并更新HTML文件中的引用。
使用浏览器缓存策略的网站在更新静态资源时,应采用批量更新的办法,比如需要更新10个图标文件,不宜把10个文件一次全部更新,二十一个文件一个文件逐步更新,并有一定的间隔时间,以免用户浏览器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞的情况。
3. 启用压缩
在服务器端对文件进行压缩,在浏览器端对文件进行解压缩,可以有效减少通信传输的数据量。
压缩对服务器和浏览器产生一定的压力,在通信带宽良好,而服务器资源不足的情况下要权衡考虑。
4. CSS放在页面最上面、JavaScript放在页面最下面
浏览器会在下载完全部CSS之后才对整个页面进行渲染,因此最好的做法是将CSS放在页面最上面,让浏览器尽快下载CSS。
JavaScript则相反,浏览器在加载JavaScript后立即执行,有可能会阻塞整个界面,造成页面显示缓慢,因此JavaScript最好放在页面最下面。
5. 减少Cookie传输
太大的Cookie会严重影响数据传输,尽量减少Cookie中传输数据量。
对于某些静态资源的访问,如CSS、JavaScript等,发送Cookie没有意义,可以考虑静态资源使用独立域名访问,避免请求静态资源时发送Cookie,减少Cookie传输次数。
CDN加速
CDN(Content Distribute Network,内容分发网络)的本质仍然是一个缓存,而且将数据缓存存在离用户最近的地方,使用户以最快的速度获取数据,即网络第一跳。
CDN能够缓存的一般是静态资源,但是这些文件访问频度很高。
反向代理
反向代理服务器具有保护网站安全的作用。
可以通过配置缓存功能加速Web请求。
反向代理可以实现负载均衡的功能,通过负载均衡构建的应用集群可以提高系统总体处理能力,进而改善网站高并发情况下的性能。
应用服务器性能优化
应用服务器就是处理网站的服务器,网站的业务代码都部署在这里,是网站开发最复杂,变化最多的地方,优化手段主要有缓存、集群、异步等。
分布式缓存
网站性能优化第一定律:优先考虑使用缓存优化性能。
1. 缓存的基本原理
缓存指将数据存储在相对较高访问速度的存储媒介中,以供系统处理。一方面缓存访问速度快,可以减少数据访问的时间,另一方面如果缓存的数据是经过计算处理得到的,那么被缓存的数据无需重复计算即可直接使用,因此缓存还起到减少计算的作用。
缓存的本质
:内存的Hash表,网站应用中,数据缓存以一对Key、Value的形式存储在内存的Hash表中。Hash表数据读写的时间复杂度为O(1)。
缓存主要用来存放那些读写比很高、变化很少的数据。
2. 合理使用缓存
频繁修改的数据
如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,应用还来不及读取缓存,数据就已失效的情形,徒增系统负担。一般来说,数据的读写比在2:1以上,即写入一次缓存,在数据更新前至少读取两次,缓存才有意义。
没有热点的访问
缓存使用内存作为存储,内存资源宝贵而有限,不可能将所有的数据都缓存起来,只能将最新访问的数据缓存起来,而将历史数据清理出缓存。
数据不一致与脏读
一般会对缓存的数据设置失效时间,一旦超过失效时间,就要从数据库中重新加载。因此应用要容忍一定时间的数据不一致。在互联网应用中,这种延迟通常是可以接受的,但是具体应用仍需慎重对待。还有一种策略是数据更新时立即更新缓存,不过这也会带来更多系统开销和事务一致性问题。
缓存可用性
缓存是为提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序的处理,可以从数据库直接获取数据。但是当缓存服务崩溃时,数据库会有可能完全不能承受如此大的压力而宕机,进而导致整个网站不可用。这种情况被叫做缓存雪崩,发生这种故障,甚至不能简单地重启缓存服务器和数据库服务器来恢复网站访问。
实践中,有的网站通过缓存热备等手段提高缓存可用性:当某台缓存服务器宕机时,将缓存访问切换到热备服务器上。但是这种设计显然有违缓存初衷,缓存不应该被当作可靠的数据源使用。
通过分布式缓存服务器集群,将缓存数据分布到集群多台服务器上可在一定程度上改善缓存的可用性。当一台缓存服务器宕机,只有部分缓存数据丢失,重新从数据库加载这部分数据不会对数据库产生很大影响。
缓存预热
缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰出来的,这个过程需要花费较长的时间。新启动的缓存系统如果没有任何数据,在重建缓存数据的过程中,系统的性能和数据库负载都不太好,那么最好在缓存系统启动时就把热点数据加载好,这个缓存预加载手段叫做缓存预热。对于一些元数据如城市地名列表、类目信息,可以在启动时加载数据库中全部数据到缓存进行预热。
缓存穿透
如果因为不恰当的业务、或者恶意攻击持续高并发地请求某个不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大压力,甚至崩溃。一个简单的对策是将不存在的数据也缓存起来(其value值为null)。
3. 分布式缓存架构
分布式缓存指缓存部署在多个服务器组成的集群中,以集群方式提供缓存服务,其架构方式有两种,一种是以JBoss Cache为代表的需要更新同步的分布式缓存,一种是以Memcached为代表的不相互通信的分布式缓存。
JBoss Cache
:JBoss Cache的分布式缓存在集群中所有服务器中保存相同的缓存数据,当某台服务器有缓存数据更新的时候,会通知集群中其他机器更新缓存数据或清除缓存数据。
JBoss Cache通常将应用程序和缓存部署在同一台服务器上,应用程序可从本地快速获取缓存数据,但是这种方式带来的问题是缓存数据的数量受限于单一服务器的内存空间,而且当集群规模较大时,缓存更新信息需要同步到集群所有的机器,其代价惊人。因而这种方案更多见于企业应用系统中,很少在大型网站使用。
Memcached
:以后讲到。
4. Memcached
简单的设计、优异的性能、互不通信的服务器集群、海量数据可伸缩的架构。
简单的通信洗衣
远程通信设计需要考虑两方面的要素:
通信协议
:即选择TCP协议还是UDP协议,抑或HTTP协议。通信序列化协议
:数据传输的两端,必须使用彼此可识别的数据序列化方式才能使通信得以完成,如XML、JSON等文本序列化协议,或者Google Protobuffer等二进制序列化序列化协议。
Memcached使用TCP协议(UDP也支持)通信,其序列化协议则是一套基于文本的自定义协议,以一个命令关键字开头,后面是一组命令操作数。例如读取一个数据的命令协议是get\。
丰富的客户端程序
Memcached通信协议非常简单,只要支持该协议的客户端都可以和 Memcached服务器通信,因此Memcached发展出非常丰富的客户端程序,几乎支持所有主流的网站编程语言。
高性能的网络通信
Memcached 服务端通信模块基于Libevent
,一个支持事件触发的网络通信程序库。Libevent的设计和实现有许多值得改善的地方,但它在稳定的长连接方面的表现却正是Memcached需要的。
高效的内存管理
Memcached使用了一个非常简单的办法——固定空间分配。
Memcached将内存空间分为一组slab,每个slab里又包含一组chunk,同一个slab里的每个chunk的大小是固定的,拥有相同大小 chunk的slab被组织在一起,叫作slab_class。
存储数据时根据数据的Size大小,寻找一个大于Size的最小chunk将数据写入。
这种内存管理方式避免了内存碎片管理的问题,内存的分配和释放都是以chunk为单位的。
Memcached采用LRU算法释放最近最久未被访问的数据占用的空间,释放的chunk被标记为未用,等待下一个合大小数据的写入。
这种方式也会带来内存浪费的问题。
互不通信的服务器集群架构
正是集群内服务器互不通信使得集群可以做到几乎无限制的线性伸缩,这也正是目前流行的许多大数据技术的基本架构特点。
异步操作
使用消息队列将调用异步化,可改善网站的扩展性。事实上,使用消息队列还可改善网站系统的性能。
在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也使得响应延迟加剧。
在使用消息队列后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程(通常情况下,该进程通常独立部署在专门的服务器集群上)从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度远快于数据库(消息队列服务器也比数据库具有更好的伸缩性),因此用户的响应延迟可得到有效改善。
消息队列具有很好的削峰作用——即通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。
由于数据写入消息队列后立即返回给用户,数据在后续的业务校验、
写数据库等操作可能失败, 因此在使用消息队列进行业务异步处理后,需要适当修改业务流程进行配合,如订单提交后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单,甚至商品出库后,再通过电子邮件或 SMS消息通知用户订单成功,以免交易纠纷。
使用集群
在网站高并发访问的场景下,使用负载均衡技术为一个应用构建一个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单一服务器因负载压力过大而响应缓慢,使用户请求具有更好的响应延迟特性。
代码优化
1. 多线程
从资源利用的角度看,使用多线程的原因主要有两个:
IO阻塞
多CPU
启动线程数=[任务执行时间/(任务执行时间-IO等待时间)xCPU内核数]
最佳启动线程数和CPU内核数量呈正比,和IO阻塞时间成反比。如果任务都是CPU计算型任务,那么线程数最多不超过CPU内核数。
解决线程安全的主要手段:
将对象设计为无状态对象
:无状态对象是指对象本身不存储状态信息(对象无成员变量,或者成员变量也是无状态对象),这样多线程并发访问时候就不会出现状态不一致。从面向对象设计角度看,无状态对象时一种不良设计。使用局部对象
:在方法内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意识地将这些对象传递给其他线程,否则不会出现对象被多线程并发访问的清形。并发访问资源时使用锁
:多线程访问资源的时候,通过锁的方式使多线程并发操作转化为顺序操作,从而避免资源被并发修改。锁导致线程同步顺序执行,可能会对系统性能产生严重影响。
2. 资源复用
系统运行时,要尽量减少那些开销很大的系统资源的创建和销毁,比如数据库连接、网络通信连接、线程、复杂对象等。
单例
:Java开发常用的对象容器Spring默认构造的对象都是单例(要注意的是 Spring 的单例是 Spring 容器管理的单例,而不是用单例模式构造的单例)。对象池
:通过复用对象实例,减少对象创建和资源消耗。对于数据库连接对象,每次创建连接,数据库服务端都需要创建专门的资源以应对,因此频繁创建关闭数据库连接,对数据库服务器而言是灾难性的,同时频繁创建关闭连接也需要花费较长的时间。因此在实践中,应用程序的数据库连接基本都使用连接池(Connection Pool) 的方式。
3. 数据结构
目前比较好的字符串 Hash 散列算法有Time33算法, 即对字符串逐字符迭代乘以33,求得 Hash值,算法原型为:
hash(i) = hash(i-1) * 33 + str[i]
- 1
- 2
Time33虽然可以较好地解决冲突,但是有可能相似字符串的HashCode也比较接近,如字符串“AA”的HashCode是2210,字符串“AB”的HashCode是2211。 这在某些应用场景是不能接受的,这种情况下,一个可行的方案是对字符串取信息指纹,再对信息指纹求 HashCode。由于字符串微小的变化就可以引起信息指纹的巨大不同,因此可以获得较好的随机散列。
4. 垃圾回收
以JVM为例,其内存主要可划分为堆(heap ) 和堆栈(stack)。堆栈用于存储线程上下文信息,如方法参数、局部变量等。堆则是存储对象的内存空间,对象的创建和释放、垃圾回收就在这里进行。通过对对象生命周期的观察,发现大部分对象的生命周期都极其短暂,这部分对象产生的垃圾应该被更快地收集,以释放内存,这就是 JVM 分代垃圾回收。
存储性能优化
机械硬盘 VS 固态硬盘
- 机械硬盘在数据连续访问(要访问的数据存储在连续的磁盘空间上)和随机访问(要访问的数据存储在不连续的磁盘空间)时,由于移动磁头臂的次数相差巨大,性能表现差别也非常大。
- 可以像内存一样快速随机访问。而且 SSD 具有小的功耗和更少的磁盘震动与噪声。
在网站应用中,大部分应用访问数据都是随机的,这种情况下 SSD 具有更好的性能表现。
B+树 VS LSM树
- 为了改善数据访问特性,文件系统或数据库系统通常会对数据排序后存储,加快数据检索速度,这就需要保证数据在不断更新、插入、删除后依然有序,传统关系数据库的做法是使用B+树。
B+树是一种专门针对磁盘存储而优化的 N 叉排序树,以树节点为单位存储在磁盘中,从根开始查找所需数据所在的节点编号和磁盘位置,将其加载到内存中然后继续查找,直到找到所需的数据。
目前数据库多采用两级索引的 B+树,树的层次最多三层。因此可能需要 5 次磁盘访问才能更新一条记录(三次磁盘访问获得数据索引及行ID然后再进行一次数据文件读操作及一次数据文件写操作)。
- 目前许多 NoSQL 产品釆用 LSM 树作为主要数据结构。
LSM 树可以看作是一个 N 阶合并树。数据写操作(包括插入、修改、删除)都在内存中进行,并且都会创建一个新记录(修改会记录新的数据值,而删除会记录一个删除标志 ),这些数据在内存中仍然还是一棵排序树,当数据量超过设定的内存阈值后,会将这棵排序树和磁盘上最新的排序树合并。当这棵排序树的数据量也超过设定阈值后,和磁盘上下一级的排序树合并。合并过程中,会用最新更新的数据覆盖旧的数据(或者记录为不同版本)。
在需要进行读操作时,总是从内存中的排序树幵始搜索,如果没有找到,就从磁盘上的排序树顺序查找。
在 LSM 树上进行一次数据更新不需要磁盘访问,在内存即可完成,速度远快于 B+树。当数据访问以写操作为主,而读操作则集中在最近写入的数据上时,使用 LSM 树可以极大程度地减少磁盘的访问次数,加快访问速度。
作为存储结构,B+树不是关系数据库所独有的,NoSQL 数据库也可以使用 B+树。同理,关系数据库也可以使用LSM。
RAID VS HDFS
RAID (廉价磁盘冗余阵列)
技术主要是为了改善磁盘的访问延迟,增强磁盘的可用性和容错能力。目前服务器鈒别的计算机都支持插入多块磁盘(8块或者更多 ),通过使用RAID技术,实现数据在多块磁盘上的并发读写和数据备份。
常见的RAID技术:
RAID0:具有极快的数据读写速度,但是RAID0不做数据备份,因为磁盘中只要有一块损坏,数据完整性就被破坏,所有磁盘的数据都会损坏。
RAID1:任何一块磁盘的损坏都不会导致数据丢失,插入一块新磁盘就可以通过复制数据的方式自动修复,具有极高的可靠性。
RAID10:结合RAID0和RAID1两种方案,提高可靠性改善性能,RAID10磁盘利用率较低,有一半磁盘用来写备份数据。
RAID3:RAID3很少在实践中使用。
RADI5:RAID5被更多的使用,避免RAID3频繁写坏一块磁盘的情况。
RAID6:数据可靠,在出现同时损坏两块磁盘的情况下,仍然可以修复数据。
RAID 技术可以通过硬件实现,比如专用的RAID卡或者主板直接支持,也可以通过软件实现。RAID技术在传统关系数据库及文件系统中应用比较广泛,但是在大型网站比较喜欢使用的NoSQL以及分布式文件系统中,RAID技术却遭到冷落。
HDFS以块(Block) 为单位管理文件内容,一个文件被分割成若干个Block当应用程序写文件时,每写完一个Block,HDFS就将其自动复制到另外两台机器上,保证每个Block有三个副本,即使有两台服务器宕机,数据依然可以访问,相当于实现了RAID1的数据复制功能。
当对文件进行处理计算时,通过MapReduce并发计算任务框架,可以启动多个计算子任务(MapReduce Task)同时读取文件的多个Block,并发处理,相当于实现了RAID0的并发访问功能。
HDFS配合MapReduce等并行计算框架进行大数据处理时,可以在整个集群上并发读写访问所有的磁盘,无需RAID支持。
小结
网站性能优化技术是在网站性能遇到问题时的解决方案。而网站的性能问题很多是在用户高并发访问时产生的,所以网站性能优化的主要工作是改善高并发用户访问情况下的网站响应速度。
网站性能对最终用户而言是一种主观感受,性能优化的最终目的就是改善用户的体验,使他们感觉网站很快。