写点什么

性能优化学习笔记

用户头像
Yangjing
关注
发布于: 2020 年 11 月 15 日

性能测试:系统性能的主要技术指标

性能优化的前提和基础,也是性能优化结果的检查和度量标准。不同视角下的网站性能有不同的标准,也有不同的优化手段

主观视角:用户感受的性能(异步操作、体验提高主观感受)

客观视角:性能指标衡量的性能(响应时间、并发数、吞吐量、性能计数器)

  • 响应时间:系统发出请求到收到最后的响应数据所需的时间,直观的反映了系统的“快慢”

  • 并发数:能够《同时处理》的《请求数目》(不是在线用户数、系统用户数),这个数字也反映了系统的负载特性。淘宝最高并发数 百万级别

  • 吞吐量:单位时间内系统处理的请求的数量,体现系统的处理能力。TPS(每秒事务数)、QPS(每秒查询数)

吞吐量=(1000/响应时间ms)* 并发数。如果响应时间是1秒。吞吐量=并发数

  • 性能计数器:描述服务器或操作系统性能的一些数据指标。System Load、对象线程数、内存、CPU、磁盘、网络...



性能测试方法

性能测试:按初期规划的性能指标,对系统不断加压,验证在系统的资源可接受范围内,是否达到性能预期

负载测试:性能测试中不断增大压力,直到系统指标出现安全临界值,测试出系统能承受的最大负载

压力测试:超过安全负载后,继续施压,直到系统奔溃,以此获取最大承受压力

稳定性测试:被测试系统在特定的硬件、软件、网络条件下,给系统加载一定业务压力,使系统运行一段较长的时间,以此检测系统是否稳定。



架构师 需要考虑 成本和收益的平衡,需要考虑承担的风险。



测试结果:

  • 并发数,使用不同的并发数,得出下面的指标

  • 响应时间 ms

  • TPS

  • 错误率 (%)

  • Load

  • 内存(G)

  • 备注



全链路压测

前面是针对的某一接口进行针对性的测试,如果需要看系统整体性能,需要全链路压测。一般情况不需要这种测试,只有整体的大促情况需要全链路压测。

将相关链路完整的串联起来进行施压,尽可能模拟真实用户的行为,当系统整体流量上来时,才会暴露出问题。

难点:

  • 牵连的基础设施、中间件系统多,如何确保流量压测通顺,压测的数据怎么构造

  • 怎么保证对线上无影响

  • 大型促销活动所带来的巨大流量要怎样制作



  1. 数据构造:线上数据 -> dump(日志、网络探针) -> 筛选、脱敏、Id隔离、订正 ->

  2. 数据隔离:读操作没问题;写操作会影响(逻辑隔离、虚拟隔离、物理隔离)

  3. 流量构造:各个CDN服务器发起测试的请求,通过流量控制平台管理



性能优化 - 系统性能优化的分层思想

两个基本原则

  • 不能优化一个没有测试的软件(做决策需要数据、实际情况的支撑)

  • 不能优化一个不了解的软件 (不要为了使用某技术、知识而用它,要知道是为了解决什么问题)



性能测试的主要指标

  • 响应时间:完成一次任务花费的时间

  • 并发数:同时处理的任务数

  • 吞吐量:单位时间完成的任务数

  • 性能计数器:System Load、线程数、进程数、CPU、内存、磁盘、网络使用率



性能优化的一般方法

  • 性能测试,获得性能指标

  • 指标分析,发现性能与资源瓶颈点。哪里与预期不符合

  • 架构与代码分析,寻找性能与资源瓶颈关键所在。是什么原因导致的瓶颈点

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

  • 性能测试,进入性能优化闭环



系统性能优化的分层思想

  • 机房与骨干网络性能优化

异地多活(多机房架构)。解决高可用性问题

专线网络与自主 CDN 建设

  • 服务器与硬件性能优化

更好CPU、网卡、磁盘

  • 虚拟机的性能优化

不同的垃圾回收算法

  • 基础组件性能优化

Apache、Nginx、中间件、数据库连接池、Jetty

  • 软件架构性能优化

缓存、异步、集群

  • 软件代码性能优化

遵循面向对象的设计原则与设计模式编程,很多时候性能不好是因为代码太烂

并发编程(多线程与锁)、资源复用(线程池与对象池)、异步编程(生产者与消费者)、数据结构



操作系统 - 计算机如何处理成百上千的并发请求

程序运行时架构

程序是静态的,程序需要加载到内存,交给CPU根据可执行的程序代码,一行一行运行起来以后,被称为进程。

操作系统多任务运行环境

进程在CPU分时执行;

进程的运行期状态:运行 - 就绪(只能CPU执行) - 阻塞(等待或者睡眠)

进程 VS 线程

服务器应用通常是单进程多线程,减少进程间 CPU 切换

进程从操作系统获得基本的内存空间,所有的线程共享着进程的内存地址空间。而每个线程也会拥有自己私有的内存地址范围,其他线程不能访问。

每个线程有自己的线程栈空间,线程栈中记录不同线程内部不同的局部变量的值。栈适合不断调用的规律,先进后出。

线程安全

当某些代码修改内存堆(进程共享内存)里的数据的时候,如果多个线程在同时执行,就可能会出现同时修改数据的情况。

场景:对象存储在堆中,不同线程栈中对堆中的对象操作时

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

lock.lock(); // 线程获取锁
i++;
lock.unlock(); // 线程释放锁

加锁导致了阻塞。

锁会引起线程阻塞,阻塞导致线程既不能继续执行,也不能释放资源。进而导致资源耗尽,最终导致系统崩溃。



如何避免阻塞引起的崩溃

  • 限流:控制进入的请求数,进而减少创建的线程数

  • 降级:关闭部分功能程序的执行,尽早释放线程

  • 反应式:异步;无临界区(Actor模型)



锁 - 锁原语CAS 与 各类锁

锁原语,CAS(V, E, N)。原语的执行必须是连续的,在执行过程中不允许被中断。

  • V 表示要更新的变量

  • E 表示预期值

  • N 表示新值

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



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

对象头中包括:Mark Word(64 bit)、Klass Word(64 bit)、数组长度(64 bit)

锁升级:获取锁 -> 失败,自旋,轻量级锁 -> 失败,添加在锁队列中,为重量级锁

偏向锁:指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价

轻量级锁:当锁是偏向锁时,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能

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



总线锁 VS 缓存锁

总线锁:处理器发送 LOCK# 信号,内存总线上输出此信号时,其他处理器的请求将被阻塞,该处理器独占内存。

缓存锁:通过缓存一致性保证操作的原子性



公平锁:先来等来锁的线程优化获得锁

非公平锁:获取锁的顺序不是按申请锁的顺序

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

独享锁/互斥锁:该锁一次只能被一个线程所持有

共享锁:该锁可以被多个线程所持有

读写锁:多个读线程之间并不互斥,而写线程则要求与任何线程互斥

悲观锁:对于一个数据的并发操作,先加锁然后进行操作的形式。悲观锁认为不加锁的并发操作一定会出现问题。

乐观锁:在更新数据的时候,检查是否已经被修改过,如果修改过,就放弃。

分段锁:设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组的一段进行加锁操作。

JDK ConcurrentHashMap 是通过分段锁的形式来实现高效并发操作的。

自旋锁:通过 CAS 循环获取锁。



异步并发式编程框架 akka

Actor 编程模型

Akka runs on the JVM

Core concept: Actor

怎么实现事务?



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

Yangjing

关注

还未添加个人签名 2017.11.09 加入

还未添加个人简介

评论

发布
暂无评论
性能优化学习笔记