写点什么

Week_07 总结

用户头像
golangboy
关注
发布于: 2020 年 11 月 08 日

性能测试

性能测试的意义

  • 性能优化的基础

  • 性能优化结果的度量标准

性能标准

主观性能

用户感受到的性能

客观性能

性能指标衡量的性能

性能指标:

  • 响应时间

  • 并发数:系统在某一时刻,同时处理的请求数(注意与在线用户数,系统用户数区别)

  • 吞吐量:单位时间内处理的请求数,如请求数/每秒,业务数/每小时,访问人数/每天。

  • TPS:每秒事务数

  • HPS:每秒 http 请求数

  • QPS:每秒查询数

  • 性能计数器

  • 系统负载

  • 线程数

  • 进程数

  • cpu

  • 内存

  • 磁盘

  • 网络使用率

性能测试方法


性能测试

验证系统在资源可接受范围内,能否达到性能预期

负载测试

达到了最初的预期后,持续增加压力,直到系统达到临界值,此时再加压,性能不升反降

压力测试

系统超过安全负载后,直到系统崩溃或不在处理任何请求,此时系统能承受的最大压力


通过这样的图表,很容易反应系统指标。有助于后续分析决策

稳定性测试

在特定软硬件网络环境条件下,加载一定的业务压力,使系统运行较长时间,检测系统是否稳定

全链路性能压测

特定业务场景下,相关链路完整串联,并同时施加压力,尽可能模拟用户行为。当系统的流量进入后,必定会暴露系统性能瓶颈

全链路压测的难点

  • 压测数据的构造

  • 压测数据模型如何贴近真实情况

  • 线上压测,如何保障对线上的无影响

  • 测试的流量如何构造

数据构造


数据隔离

  • 逻辑隔离

  • 方法:测试数据和业务数据放一起,通过特殊标识区分

  • 缺点:污染线上数据,存在数据安全隐患

  • 虚拟隔离

  • 方法:对写数据的位置进行 mock,不真正的入库

  • 缺点:压测结果的准确性产生干扰

  • 物理隔离

  • 方法:所有写数据的地方进行压测流量识别,写到隔离的位置,包括存储,缓存,搜索引擎等等

  • 缺点:额外的资源

流量构造

通过 cdn 进行流量构造



性能优化

优化原则

  • 不能优化一个没有经过性能测试的软件

  • 不能优化一个不了解的软件

性能优化的流程

  1. 性能测试

  2. 根据测试指标进行分析,发现性能和资源瓶颈点

  3. 架构与代码分析,定位瓶颈点

  4. 架构与代码以及其他方面进行优化,优化关键技术点,平衡资源利用

  5. 再次进行性能测试

系统优化的分层思想

从上到下,从全局到细节,全方位从不同维度,思考可优化的点。

  1. 机房与骨干网络的性能优化

  2. 异地多活,多机房架构

  3. 专用网络与自主 cdn 建设

  4. 服务器与硬件性能优化

直接换更好的硬件设施,性能提升是数量级的。网络吞吐量如果持续达到最大值,说明网卡是瓶颈

  1. 操作系统性能优化

例:通过一段时间内 cpu 使用率的分析(系统 cpu 使用率与用户态 cpu 使用率)

tips: linux 大页类型有两类:普通大页(Huge Pages)和透明大页(Transparent Huge Pages),这些大页是为了适应越来越大的系统内存管理而出现的。二者的区别是普通大页管理是预分配的方式,而透明大页管理则是动态分配的方式。

示例中由开启透明大页导致的 cpu 系统使用率高问题,说明跑的应用程序,是密集型内存运算的程序

  1. 虚拟机性能优化

例子中,讲了 java 虚拟机的垃圾回收对性能的影响。对应 golang 中的运行时系统和垃圾回收机制。

  1. 基础组件性能优化

基础组件的不同版本,性能也不一样

  1. 软件架构性能优化

优化手段有缓存,异步,集群

读操作通过缓存来优化;写操作通过异步的方式来优化;通过集群扩展系统整体处理能力

  1. 软件代码性能优化

易于阅读的代码是好代码

  • 并发编程(多线程与锁)提高性能

  • 池化技术(线程池,对象池)

  • 异步编程(生产者消费者)

  • 数据结构的优化

操作系统并发请求的处理

系统设计时,架构师脑子里应该有整个运行时的视图

锁引起阻塞

锁的使用会引起线程阻塞,如果阻塞时间过长,同时大量请求进入,会导致系统崩溃


避免阻塞引起崩溃

  • 限流:控制进入系统的请求数。进入系统的请求越多,越会消耗系统的资源(cpu,内存,网络),这些资源消耗过大,会导致系统的不可用

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

  • 反应式编程

  • 轻量级锁(CAS 指令):通过 cpu 上的 CAS 指令实现。

  • 重量级锁(互斥锁):通过阻塞线程+自旋锁+cpu 指令的方式(等待队列和引用计数器)

  • CAS 锁的问题

CAS 指令针对指定的 cpu 进行操作,多 cpu 情况下会有多个线程进入临近区,导致锁失败。这种情况下,需要操作系统和硬件本身去支持。

总线锁:某 cpu 在内存总线上发出 lock 信号后,独占内存,其他 cpu 阻塞。(代价过大,使性能急剧下降)

缓存锁:如果内存里数据已经被缓存到了 cpu 缓存行中,且在 lock 期间被锁定,那么当 cpu 计算完毕后,回写内存时,通过缓存一致性机制保证操作的原子性。

  • 可重入锁(读写锁)

  • 悲观锁:(业务级别说法)读取数据之前加锁,哪怕它在临界区中未修改数据

  • 乐观锁:(业务级别说法)先读取数据,改写操作时,再判断此期间是否有其他线程进行了写入。(理解程度较浅)

  • 分段锁:哈希结构中使用较多

  • 自旋锁:通过循环不停的去获取锁,此间线程不会阻塞,不会切换上下文,但是会消耗 cpu

Actor 模型

与 erlang 中的 actor 一样,每一个 actor 有自己的 mailbox,用于接收消息队列。有一组线程池。轻量级 actor 如果有消息时,会以操作系统协程的方式将逻辑放入线程上下文中执行。调度的粒度更细,协程更轻量。


用户头像

golangboy

关注

还未添加个人签名 2018.09.18 加入

还未添加个人简介

评论

发布
暂无评论
Week_07 总结