本文是 《大型网站技术架构:核心原理与案例分析》的学习和记录
网站性能既是客观的指标,他可以用吞吐量,响应时间等去衡量,同时也是一个主观指标是用户感受到的性能。用户感受到的性能出去网站的响应时间外,还要算上用户计算机解析页面的时间和网络带宽等。
吞吐量和并发量
吞吐量是当前处理任务的速度,如果只有一个任务,那不管怎么提高,吞吐量只能是1,此时不断提高任务数,吞吐量就会跟着提高,但是当达到一个服务器处理的阈值的时候,吞吐量的增加速度就会变得缓慢,超过阈值的时候,吞吐量可能就会不增反降,在提高可能会导致服务器瘫痪,吞吐量为零。
所以吞吐量和并发量是一个先递增后递减的关系。
性能测试的几个类别
性能测试:验证在系统可承受范围内的性能。
负载测试:测试系统平稳运行的临界值。
压力测试:在负载的基础上不断加压,直到系统崩溃的临界值。
性能测试报告 如下例子
性能优化分析:
主要从内存 磁盘 网络 CPU 代码和架构着手
内存CPU和磁盘属于硬件范畴,网络可以用网络请求分析工具查看各个阶段花费的时间,代码和架构则从设计入手
1优化分类:
1.1前端优化:
1.1.1浏览器优化
处理方式 | 具体 | 原因 |
减少http请求 | 合并css,合并js | 每次请求资源都必须发送给服务器一次http请求,显然这会消耗服务器资源,如果将文件合并减少http请求次数则可以有效加快访问速度。 |
使用浏览器缓存 | 缓存css,js图片等 | 类似css,js这些文件更新频率通常是很低的,可以设置http头中的cach-control和expires属性去控制浏览器缓存,可以是数天甚至数月。如果临时紧急需要更新这些文件的时候,就需要在html里更新引用地址,这样html会请求一个全新的文件,而不是旧的缓存。另外更新缓存文件时建议逐步更新,避免突增大量的服务器压力 |
压缩 | 启用g-zip压缩 | 文本压缩可以达到80%,有效减少传输的数据量,但是压缩和解压缩会对服务器和浏览器增加一些压力,毕竟压缩这个动作是需要处理的,在宽带足够的情况下,需要权衡处理方式 |
CSS和js的位置 | css放在顶部,js放在尾部 | 浏览器会在读取玩css的时候对页面渲染,所以建议先读取css,而js的读取对页面展示的影响并不大,所以建议js放最后面。浏览器解析是从上到下解析的,如果js放在前面,那他一定要在解析完js后才会去加载html文本 |
减少cookie的传输 | 减少cookie的传输 | cookie包含在每次的请求中,太大的cookies对响应有很大影响,对于静态资源的访问cookie没有意义,如css和js等 |
1.1.2 CDN加速
CDN是将服务器缓存在离用户近的地方,通常会放在网络运营商的机房里,那么用户的访问第一时间就会跳到网络运营商,如果获取到需要的资源则直接返回,大大加快了访问速度,同时减少了源服务器的压力。CDN一般缓存静态资源,图片文件cssjs静态网页等,而源服务器会自动将数据更新到cdn服务器
1.1.3 反向代理
反向代理
反向代理一方面保护了源服务器
另一方面当用户第一次访问静态资源的时候反向代理将数据缓存下来,后续其他用户获取数据直接在反向代理获取即可。
有的网站也会将动态资源缓存到反向代理中,如贴吧会将热门帖子缓存在代理服务器。推测抖音也会将热门推荐存放在代理服务器。
1.2应用服务器性能优化
1.2.1 分布式缓存
缓存是优化的第一想到的解决方案, 缓存可以缓存数据缓存文件也可以缓存页面片段。
缓存的原理就是将常用的数据更容易获取。
他可以将数据放在获取速度更快的地方,如cpu缓存,这样就不需要从其他存储介质里获取数据,因为相对于cpu处理速度来说,内存和硬盘是很慢的,对于程序来说,将数据缓存在内存里,这样就不需要从数据库里去取(数据库存放在硬盘,也有一些数据库会存放在内存里),相对于硬盘,内存的处理是非常快的,性能的瓶颈就在于硬盘的数据检索和硬盘和内存之间的数据传输。
缓存最好符合以下特点:
是频繁访问的热点数据
更新频率很低
可以允许一段时间内数据不是最新的(如果缓存是定期更新的话,那么在更新前数据可能就是旧的,如果缓存是立即更新的策略的话,服务器开销就会大一点,具体衡量)
缓存如果失效,服务器要保证可以正常运行
防止缓存穿透:如果数据在缓存中查找不到对应数据就要去数据库查询,如果有恶意攻击频繁查询没有的数据那么就会频繁访问数据库,这里如果在缓存中设置对应的键值对的value为null那么就可以阻止这种缓存穿透
1.2.2 分布式缓存架构
JBoss Cache
确切的说我觉得他是集群处理不是分布式,他是将同样的数据放在多个服务器上,如果一个服务器更新了就要同步更新到其他服务器上,通常用于企业应用,因为互联网应用集群规模太大,同步代价高。
Memcached
Mmecached的服务器之间互不通信,可以很轻松的实现扩展。
通信协议简单,如获取数据是get
支持多种客户端,如java c/c++ c# phy python Ruby等
网络通信性能高,基于Libibevent,且稳定。
互不通信。
内存管理高效。
建议这个部分看比较详细的知识集中看完
1.2.3 异步操作
就是前面讲的消息队列机制,在遇到业务敏感的地方需要稍加修改,如用户下单,不能等消费者还没处理完就反馈给用户信息。避免产生交易纠纷。
1.2.4 服务器集群和负载均衡
前面讲了很多。
1.2.5 多线程处理
显然合理的使用多线程是对程序性能的提升有帮助的。大概有一下几个注意点,但是具体的建议学习相关知识做储备,多线程不是只看了皮毛就可以的。
将对象设计为无状态。
使用局部对象。
并发访问资源使用锁。
1.2.6 资源复用
单例模式和对象池。单例是将数据静态化放到方法区spring常用的bean管理,对象池就是管理多个静态对象,比如线程池。
1.2.7 数据结构
这里讲到了hash算法,由于hashcode是根据原始字符串获得的,如果原始字符串很像的话会导致hashcode也很像,降低随机性,数组上的链表跟着变多,这里讲到用MD5去更换一下原始字符串,然后在做hash处理,由于这里对key的占用大小做了增加,所以不确定最终空间和时间的转化效率关系,这里不做深究,后续学习完jvm虚拟机会来进一步说明
1.2.8 垃圾回收
这里对虚拟机的堆栈做了简单说明。
堆用来存放对象,对对象进行管理,创建释放和垃圾回收等,堆栈用于存放线程上下文信息,方法参数,局部变量等。
这里后续专题讨论。
1.3 存储性能优化
1.3.1 机械硬盘换成固态硬盘(略过常识)
1.3.2B+树和LSM树
B+树是一个应用在传统数据库上的索引存储方式,他基于多级索引,对多级索引的各个层次做了规划,通常一个块内放置指定指定索引起始位置的数据,一般是两层或者三层索引可以存放足够多的索引数据。
关于B+树会有专题讨论
对于一个三层结构的B+树,更新一条数据需要五次磁盘IO,第一次读取第一层的节点,第二次缓存第二层的索引结构,在缓存中找到索引所在的范围区间,
第三次去范围对应的磁盘地址中取索引数据(第三层的某一个小块,包含多条索引数据),找到需要的指定数据,第四次根据索引去找到指定数据所在的磁盘位置(索引里存放的是数据在磁盘的地址),第五次修改数据写入到磁盘
NoSql产品使用LSM树
首先LSM是基于内存管理的,效率比磁盘高,其次LSM大多数数据修改操作都是在内存中完成,只有当超过阈值的时候才会去和磁盘做更新操作。
数据还可以通过磁盘列阵RAID来起到提高性能和可靠性的作用,在NoSql中常用的是分布式存储数据,在集群规模上实现了类似RAID的功能。