性能优化 (一):性能测试、优化方法论、性能分析

用户头像
dony.zhang
关注
发布于: 2020 年 07 月 22 日
性能优化(一):性能测试、优化方法论、性能分析

性能优化需从设计、编码、测试、监控运维等整个研发全流程的各个环节全局思考,形成有反馈的闭环。

性能测试

性能优化的前提和基础是性能测试,通过性能测试才能知道当前系统的性能基准数据,通过数据分析才能知道性能的瓶颈,再针对具体问题具体优化。



其实网站性能标准可从不同视角来看:

  • 主观视角:用户感受的性能,其实现在也可进行监控,通过客户端/前端 性能监控平台;

  • 客观视角:性能指标衡量,服务器的基础设施监控,接口性能监控等;

1. 指标

  • 响应时间:完成一次任务花费的时间, 反映系统快慢

  • 并发数:同时处理的任务数,还包含:在线用户数,系统用户数等指标,反映系统的负载特性

  • 吞吐量:单位时间内完成的任务数,包含:TPS, QPS, HPS,反映系统的处理能力

吞吐量 = (1000/ 响应时间ms)* 并发数

  • 性能计数器:描述服务器或OS性能的一些数据指标。system load(运行线程数+等待线程数,最优是CPU核数), 线程数,进程数,CPU, 内存,磁盘,网络使用率

2. 性能测试分类

  • 性能测试:以系统规划的性能指标为预期目标,对系统不断施加压力,验证系统在资源可接受范围内,是否能达到性能预期;

  • 负载测试:再不断增加并发请求施加压力,直到系统某项或多项性能指标达到安全临界值,系统资源已呈现饱和状态,系统的处理能力不但不能提高,反而下降;

  • 压力测试:超过安全负载情况下持续加压,直到系统崩溃或不能再处理任何请求,以此获得系统最大压力承受能力;

  • 稳定性测试:在特定硬件、软件、网络环境下,加载一定业务压力,使系统运行一段较长时间,监测系统的是否稳定。加载的压力是波浪式的。

3. 性能测试分析

  • 系统资源使用率

  • 响应时间

  • 并发用户数

4. 压测平台本身的性能问题

随着并发数的增加,会造成压测机器的资源的消耗,也可能引起压测机器的崩溃。资源消耗包含:

  • 连接端口不够用

  • 线程数不够

  • 内存不够

  • 网络带宽不够

  • 单机瓶颈



可通过下面方式优化:

i) 分布式集群,通过主控机控制执行机个数

ii) 压测平台集群的监控,合理规划容量

iii) 执行完测试程序,及时清理和释放资源

iv) 对异常、超时任务做一些策略处理

v) 定期重启机器

大规模分布式系统性能优化方法论

重要:不能优化一个没有测试的系统;不能优化一个不了解的系统

1. 一般方法

也可以叫做性能优化的步骤:

  • 性能测试:获得性能基准数据,性能指标

  • 性能数据分析:发现性能与资源的瓶颈点

  • 架构与代码分析:瓶颈关键所在

  • 架构与代码优化:优化关键技术点,平衡资源利用

  • 性能测试:形成性能优化闭环

2. 分层思想

性能优化是一个系统的工程,需在系统服务的整个链路中涉及到的硬件、软件、网络等各方面因素找到瓶颈,进行对应的优化

  • 机房与骨干网络 性能优化:异地多活多机房架构,专线网络,CDN建设

  • 服务器与硬件 性能优化:使用更优的CPU。磁盘、内存、网卡

  • 操作系统 性能优化:OS参数调优

  • 虚拟机 性能优化:虚拟机参数调优

  • 基础组件 性能优化:基础组件更换或更新

  • 软件架构 性能优化:见下文

  • 软件代码 性能优化:见下文

3. 软件架构性能优化

软件架构的优化,对性能优化有时起到决定性的作用。目前常用的三板斧

  • 缓存

从内存或最近路径读取数据,可减少RT, 数据库访问,CPU计算,优化读操作

  • 异步

可及时响应,提升用户体验;削峰填谷,减少负载压力;优化写操作

  • 集群

增加更多的计算资源,提升并发,吞吐量,响应时间。

技术难点:如何使多台服务器对使用者而言看起来像一台服务器。

4 . 软件代码性能优化

代码设计和编写,也是性能问题的一个重要因素。需合理利用现有的经验和技巧

  • 并发编程:多线程,锁

  • 资源复用:线程池、对象池

  • 异步编程: 生产者消费者

  • 数据结构&算法:数据、链表、hash表、树

  • 设计模式:面向对象,函数式编程, 23中设计模式

性能分析

分析性能瓶颈,需熟悉整个计算机体系的基本运行原理,才能快速判断问题的根源

1. 操作系统

  • 程序运行时架构:从静态程序 --->运行的动态进程

程序代码从保存在磁盘上,加载到内存中,通过调度到CPU执行,变成一个有生命周期的进程。

一个进程的内存模型:可执行代码、堆内存空间、栈内存空间、进程数据结构等

  • OS多任务运行

CPU的核数是有限,进程是通过分时执行来处理很多任务的,会分成很小的时间片

  • 进程运行期状态:就绪、运行、阻塞等

  • 进程 vs 线程

进程:从OS中获取基本的内存空间,所有线程共享的进程内存地址空间, 堆内存

线程:自己私有地址范围,其他线程不能访问 栈内存

  • 线程安全:某些代码修改内存堆或进程共享内存中的数据时,多个线程同时执行,可能会出现同时修改同一数据情况。就可能产生线程安全的问题。

  • 临界区:多个线程访问共享资源的这段代码,解决线程安全问题主要方法时使用锁,将临界资源加锁,只有获取锁才能执行临界区代码。

  • 阻塞:锁会引起线程阻塞,导致线程即不能继续执行,也不能释放资源。耗尽资源,导致系统崩溃。

解决办法:

i)限流:控制请求数,减少创建的线程数

ii)降级:关闭部分功能,尽快释放线程

iii)避免阻塞:异步IO; 无临界区(Actor模型)



2. 锁

  • 锁原语CAS

CAS(V, E, N), 如果V值等于E,则将V值设为N;若不同,什么都不做。

CAS是一种系统原语,执行必须是连续的,在执行过程中不允许被中断。



Java通过CAS原语在对象头中修改Mark Word实现加锁

偏向锁:一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低取锁的代价(消除数据在无竞争情况下的同步原语);

轻量级锁:当是偏向锁时,被另一个线程所访问,会升级为轻量级锁,其他线程通过自旋(循环)的形式尝试获取锁,不会阻塞;

重量级锁:当时轻量级锁时,另一个线程虽然自旋,但不会一直持续下去,到一定次数时,还没获取到锁,就会进入阻塞,膨胀为重量级锁,会让其他申请的线程进入阻塞,性能降低;

  • 多CPU情况下锁

总线锁:使用处理器的LOCK信号,当一个处理器在内存总线上输出此信号是,其他处理器的请求被阻塞,该处理器独占内存。

缓存锁:只内存区域缓存在cache中,允许缓存一致性机制(会阻止同时修改由两个以上处理器缓存的内存区域数据,当其他处理回写已被锁定的缓存行数据时,会使缓存行无效)保证操作的原子性。



  • 其他几种锁的介绍

是否按申请顺序获取锁:i) 公平锁;ii) 非公平锁

可重入锁:某个线程已经获得某个锁,可再次获取锁而不会出现死锁;

是否可被多个线程持有:i) 独享锁/互斥锁:一次只能被一个线程所持有;ii) 共享锁:可被多个线程所持有;iii) 读写锁:多个读线程之间并不互斥,而写程序要求与任何线程互斥;

认为并发操作是,数据会不会发生修改:i)乐观锁:同一个数据并发操作,是不会发生修改的,在更新数据时,检查是否已经被修改过,如果修改过,就放弃 ; ii)悲观锁:认为对同一个数据并发操作时,一定是会发生修改的。哪怕没修改,也会认为修改。采用加锁的形式,不加锁的并发操作一定会出问题。

分段锁:细化锁的粒度,就仅针对数组的一段进行加锁操作

自旋锁:尝试获取锁不会立即阻塞,而是采用循环方式去尝试获取锁,好处:减少线程上下文切换的消耗,缺点:循环会消耗CPU;

3. 文件系统和硬盘I/O



数据库文件系统的数据结构

  • B+树

leaf page 为数据节点, 其他都为Index page;

query:按照Index page搜索数据;

insert: 分三种情况, i) leaf page 没满, Index page没满, 直接插入;ii) leaf page 满, Index page没满,拆分leaf;iii) leaf page 满, Index page满,拆分leaf, Index;

delete: 分三种情况, i) leaf fill factor 没满50%, Index fill factor 没满50%, 直接删除;ii) eaf fill factor 满50%, Index fill factor 没满50%,,合并leaf;iii) eaf fill factor 满50%, Index fill factor 满50%,,合并leaf, Index;





  • LSM树

基于日志+跳表的数据结构,WAL(write ahead log) +skiplist +分层有序表sstable。优化了写的性能,同时兼顾查询性能





文件控制块

文件系统将硬盘以块为单位进行划分,每个文件占若干个块,通过一个文件控制块FCB记录每个文件占据的数据库块。

Linux Inode文件控制块:

i) inode记录文件权限、所有者、修改时间,文件大小等属性信息,以及文件数据块硬盘地址索引

ii) inode是固定结构,能记录的硬盘地址索引数是固定15个索引

iii) 可存储 12 + 256 + 256 * 256 + 256 * 256 * 256个数据块, 每个块 4k, 也就是单个文件最大不超过70G



RAID磁盘阵列

通过分片在在多个磁盘上,提供存储和读取的吞吐量和访问速度;通过循环冗余校验,提高数据可靠性,安全性,容错能力。



分布式文件系统 HDFS

HDFS采用Master/Slave的架构来存储数据,这种架构主要有四部分组成。

i) Client: 文件切分,上次HDFS时,将文件分成一个一个Block, 进行存储;与NameNode交互,获取文件位置信息;与DataNode交互,读取或写入数据;提供一些命令来管理HDFS; 通过一些命令来访问HDFS;

ii) NameNode: 是master,是一个主管,管理者。管理HDFS的名称空间;管理数据块Block映射信息;配置副本策略;处理客户端读写请求;

iii) DataNode: 是Slave, NameNode下达命令,执行实际的操作。存储实际的数据块;执行数据块的读写操作;

iv) Secondary NameNode: 不是NameNode的热备,NameNode挂掉,并不能马上替换并提供服务。 辅助NameNode, 分担其工作量;定期合并fsimage和fsedits, 并推送给NameNode; 紧急情况下,可辅助恢复NameNode;



发布于: 2020 年 07 月 22 日 阅读数: 111
用户头像

dony.zhang

关注

专注成就专业 2018.07.06 加入

程序员

评论

发布
暂无评论
性能优化(一):性能测试、优化方法论、性能分析