那些不得不说的性能优化套路(一)

简介: 你有没有想过,为什么跨行转账要告诉你2小时内到账,而不是立即到账?为什么抖音那么多用户同时在使用,却很少出现崩溃的情况?电商网站是如何支撑住双十一全国人民买买买的?

你有没有想过,为什么跨行转账要告诉你2小时内到账,而不是立即到账?为什么抖音那么多用户同时在使用,却很少出现崩溃的情况?电商网站是如何支撑住双十一全国人民买买买的?


性能优化对一个产品的重要性不言而喻,它直接影响网站的用户留存率,APP在商店的评分和用户粘性。一个响应慢的应用,即便它功能再强大,也留不住用户。


性能优化对一个程序员同样非常重要——如果你是一个有追求的程序员的话。我们说,大多数人的职业生涯发展都应该是一个T字型,要在某一方面有深度,也要有广度。而性能优化,恰恰是一个既需要深度,又需要广度的话题。相信很多程序员都有一个成为架构师的梦,如果想要成为一个系统架构师,那性能优化是不得不学习了解的。


下面将从各个方面,把我知道的关于性能优化的各种“套路”介绍给大家,欢迎大家看完后留言交流探讨。


找到最慢的节点

我们谈性能时,一般来说有两个指标,一个是响应时间,一个是吞吐量。

说到响应时间,最直观的感受就是快与慢。打开一个网页需要多少毫秒、点击一个按钮需要等待多长时间、刷一个抖音视频需要加载多久?这些都是在说响应时间。

吞吐量指的是系统在单位时间能够承受的请求数量,反映是系统的承载能力。

下面列出一些常见的影响性能的地方。

网络传输

影响响应时间的因素有很多,我们可以通过一些监控去测试。但我可以很负责任的告诉你,在大多数场景,一次完整的网络请求中,最多的时间往往是消耗在网络传输上。

网络传输有很多种,比如服务端到客户端的请求和响应,还有服务端各个微服务之间的接口调用耗时,还有应用与数据库、应用与缓存、应用与消息中间件等等之间的网络传输。

当然,大多数时候,我们会把后端的一些东西尽量放在一个机房里面,这样可以直接走内网,网络传输时间不会很高。但相比于大多数应用代码的执行时间来说,这些网络传输也是一笔不小的消耗。

SQL查询

我们应用的数据可能会持久化在不同的地方,不管是关系型数据库、非关系型数据库、还是搜索引擎,一旦数据量上去了,如果没有做好性能优化,查询的时候很容易就耗费大量的时间。

最常见的就是SQL慢查询了,一旦产生了慢查询,轻则响应时间变慢,重则拖满线程池导致整个服务不可用。所以SQL查询也是我们经常会考虑到的性能优化方向。

线程等待

线程等待指的是线程同步造成的问题。现代服务器往往是多核的,我们通常会使用线程池来发挥多核服务器的优势。但使用多线程经常会遇到的问题就是线程的同步问题。

在高并发下,线程同步其实是一个很危险的操作。如果临界区的操作比较耗时,就会导致大量的线程等待、堆积,最终撑满线程池。比如上面提到的慢SQL就常常会导致这个问题。

所以我们在使用多线程的时候,一定要小心谨慎。尽量弄清楚它的原理,想办法避免或者更轻量级地上锁。比如Yasin前几天发的几篇关于ThreadLocal的文章,里面就介绍了在某些场景可以用ThreadLocal避免线程的同步。

多线程是一块比较大,也比较难的知识点,也是互联网大厂的入门门槛,面试必问,工作中也会经常用到。这里推荐我之前参与写作的一本关于多线程的开源电子书《深入浅出Java多线程》,在我的公众号“编了个程”回复“多线程”即可领取这本书的电子版。

Full GC

Full GC其实影响整个应用性能的概率比较小。但如果你的程序没写好,或者JVM参数没有设置好,造成了频繁Full GC或者Full GC时间过长,也是有可能会影响性能的。

对JVM有所了解的朋友都知道,Full GC会STW (Stop The World),这段时间程序会暂停,也就没法响应用户。

G1对Full GC做了优化,把单线程的Full GC变成了多线程并行Full GC。但如果Full GC频繁,仍然会影响应用的性能。

这里列一下Full GC频繁的原因,有兴趣的朋友可以自己再深入了解一下:

  • 老年代设置的空间太小
  • 永久代空间不足
  • 程序中写了很多大对象
  • 晋升老年代的代数阈值设置太小


高频优化思路

针对上面提到的几个最容易影响程序性能的点,下面介绍一些高频的性能优化思路,大多数都是针对网络传输的,从各种角度去减少网络传输的消耗。从这些思路入手,大概率可以很明显地提升性能,小伙伴们可以作为参考。

CDN

前面提到,一般来说,一个用户请求大多数时间是消耗在网络传输上的,尤其是客户端与服务端之间的网络传输。

CDN全称是“Content Delivery Network”,翻译过来叫内容分发网络。原理其实很简单,就是把资源分发到全国各地甚至是世界各地,使得用户可以就近取得资源,缩短网络传输的距离,降低延迟,所以可以很明显地提升响应速度和成功率。

CDN原理

所以CDN多是用于文件分发,比如网站的css、js、图片、视频等资源文件。曾经听过一句话:如果你的网站没有使用CDN,那么使用CDN基本上可以让你的网站性能得到大幅度提升

CDN一般是和OSS配合起来使用的。现在各大主流云厂商基本都提供了OSS和CDN的产品,我自己的个人网站是使用的七牛云,有10G的免费容量,比较适合于个人站长。

压缩

另一个优化网络传输消耗的思路就是压缩了。CDN的思路是让网络传输的距离更短,压缩的思路是让网络传输的内容更小。尤其适用于js/css等文本文件,压缩收益非常高,往往能节省很多的网络传输开销。

图片和视频当然也可以压缩,不过需要选择合适的压缩算法。比如我们用微信发送图片时,如果不点击“发送原图”,那图片就会被微信压缩,虽然可能没那么高清,但是文件小很多,使得用户可以更快收到图片,提升用户体验。

现在大多数网站都会使用nginx作为前端服务器或者负载均衡器,在nginx里面可以非常方便地启动gzip压缩功能:

server{
    gzip on;
    gzip_buffers 32 4K;
    gzip_comp_level 6;
    gzip_min_length 100;
    gzip_types application/javascript text/css text/xml;
    gzip_vary on;
} 

在浏览器打开“开发者控制台”,查看资源的网络请求,可以查看该资源是否启用了压缩算法。

使用gzip

注意:gzip压缩算法比较适用于html、js、css等文本文件,不适用于图片等二进制文件。对图片使用gzip压缩收益不高,反而可能会增加体积。

预加载

前面讲到了两个网络传输的优化思路。我们可以另辟蹊径:既然网络传输那么消耗时间,那我们偷偷在不忙的时候提前下载好不行吗?

大家可以做一个实验:刷抖音刷到一半,等一会儿,然后停掉自己的网络,再往下刷,可以发现还能刷好几个视频。

这就是因为抖音使用了预加载技术,当你在专心致志地看一个有趣的视频的时候,这个时候网络其实是空闲的,抖音就在悄悄下载后面几个视频,这样你就可以一直刷刷刷,用户体验就会很顺畅。

试想一下,如果不使用预加载,用户每次往下刷,都得等几秒钟把这个视频下载下来才能看,那自然用户体验极差。

当然了,预加载并不适用于所有场景。毕竟预加载是提前下载,并不是不下载。如果不是有特定的业务场景,其实也没必要使用预加载。不合理地使用预加载甚至有可能会影响正常的业务,还有可能造成数据不一致的问题。

慢SQL优化

很多应用后端会使用关系型数据库来持久化数据。如果数据量大了,索引设置不合理,就很有可能会产生慢SQL。

生产环境最好加上慢SQL监控和分析的工具。阿里出品的Druid就很不错。对很多中小型项目来说已经足够了。

慢SQL优化一般有几个思路。

  1. 先看是不是没有命中索引,如果没有命中,是否可以调整索引或者SQL语句?
  2. 看能不能从业务代码层面解决?
  3. 能不能在应用层面加缓存?
  4. 考虑是否需要分库分表?

索引这个东西大家应该或多或少都接触过或者听说过。分析索引需要有一定的数据库基础,这里推荐《高性能MySQL》这本书,对索引讲得比较清晰。我的个人网站yasinshaw.com上面也有我之前写的关于MySQL的系列文章:

搜索关键字MySQL

从业务层面解决也是可以思考的一个点。比如我之前优化过的一个慢SQL。优化前的做法是用count查询数据库还有多少数据,如果大于0,就delete掉这批数据,每次delete 3k条。结果每天数据库都有几十万条符合这个查询条件的数据,导致每次count查询都会耗费许多时间,即使走了索引,也得扫描几十行。而delete也因为数据量太大,执行时间超过1秒,收到告警。

long count = dao.count(condition);
while(count > 0) {
 dao.delete(condition);
    count = dao.count(condition);
}

优化思路也很简单,因为delete的时候会返回删除的行数。所以我们直接用这个数据来决定是否退出循环就行了,根本不需要count查询。同时把批量删除的行数从3k条修改为1k条,这样两个慢SQL问题就都解决了。

long deletedNumber = 0;
do {
    deletedNumber = dao.delete(condition);
} while (deletedNumber > 0)

缓存和分库分表会在下文做详细介绍,这里不赘述。

JVM调优和升级

频繁的GC会占用大量的JVM资源,还会浪费CPU的资源。所以如果是生产环境,推荐给JVM也加上监控和告警,这样能够随时观察JVM的状况是否健康。

尤其是对于Full GC,要格外注意,因为Full GC会Stop The World。前段时间我们有一个应用就是因为永久代空间不足,触发Full GC,而每次回收效果又不好,导致一遍又一遍地Full GC,影响应用的正常工作。

Java也在对JVM不断进行优化和升级,比如最新的ZGC,在大堆下性能表现优异。G1也非常不错,如果项目条件允许,建议使用稍微新一点的垃圾收集器。

目录
相关文章
|
6天前
|
C++ UED
C/C++ 性能优化思路
C/C++ 性能优化思路
59 0
|
6天前
|
消息中间件 缓存 NoSQL
如何做性能优化?
如何做性能优化?
|
6天前
|
Web App开发 缓存 前端开发
当面试官问我前端可以做的性能优化有哪些
当面试官问我前端可以做的性能优化有哪些
|
6天前
|
缓存 前端开发 JavaScript
为什么面试官这么爱问性能优化?
为什么面试官这么爱问性能优化?
|
8月前
|
缓存 前端开发 JavaScript
前端面试的性能优化部分(13)每天10个小知识点
前端面试的性能优化部分(13)每天10个小知识点
41 0
|
8月前
|
缓存 前端开发 安全
前端面试的性能优化部分(14)每天10个小知识点
前端面试的性能优化部分(14)每天10个小知识点
54 0
|
8月前
|
缓存 前端开发 JavaScript
前端面试的性能优化部分(12)每天10个小知识点
前端面试的性能优化部分(12)每天10个小知识点
39 0
|
8月前
|
缓存 前端开发 JavaScript
前端面试的性能优化部分(5)每天10个小知识点
前端面试的性能优化部分(5)每天10个小知识点
34 0
|
8月前
|
缓存 前端开发 测试技术
前端面试的性能优化部分(10)每天10个小知识点
前端面试的性能优化部分(10)每天10个小知识点
85 0
|
8月前
|
JavaScript 前端开发 API
前端面试的性能优化部分(1)每天10个小知识点
前端面试的性能优化部分(1)每天10个小知识点
53 0