1性能关键指标2基于服务的系统组合方式请求占用系统资源2.1基础服务2.2集成服务2.3混合服务混合服务的资源消耗2.4系统资源消耗3通用系统优化技巧3.1代码调优3.2数据库调优参考这篇文章面向团队中初级开发人员,比较浅。很多同学在实际开发中害怕提高系统的QPS,因为他们认为如果QPS太高,系统会挂掉;基于这种心理,他们会尽量减少系统的请求量,甚至有人会把大量的处理放在服务中。处理,这样当有外部请求发出时,服务会处理所有的业务(比如把for循环的计算放在服务器上)。这种方式降低了系统的请求量,但是是否降低了系统的QPS呢?这样系统是更安全还是更危险?首先,让我们介绍一下基本概念。1.性能关键指标系统吞吐量(Throughput)吞吐量是指单位时间内系统处理的请求数,反映了系统的整体处理能力。响应时间(系统延迟Latency)一个请求的平均响应时间一般来说,系统的性能受到系统吞吐量和响应时间这两个条件的制约,两者缺一不可。比如我的系统可以承受100万并发,但是系统延迟超过2分钟,那么这个100万负载就没有意义了。系统延迟低,但吞吐量低,同样没有意义。一般来说,对于一个系统?吞吐量(Throughput)越大,系统延迟(Latency)就越差。因为请求量太大,系统太忙,响应速度自然就低。?系统延迟(Latency)越好,支持的吞吐量(Throughput)就会越高。因为Latency短,处理速度快,所以可以处理更多的请求。?并发数系统可以同时处理的请求/事务数。?QPS(又称TPS,Querypersecond/transactionpersecond)并发/响应时间QPS作为一个整体可以概括系统吞吐量和延迟这两个指标,因此也是系统最重要的指标之一。但是当系统的QPS增加时,会对系统造成什么影响,或者说我们如何避免QPS增加对系统造成的危害呢?下面我们看一下服务系统的主要模式和系统资源的消耗。2.服务系统组成方式2.1基础服务最基础的服务一般包括两个操作:业务逻辑处理和DB读写。发送请求时,会消耗哪些系统资源?请求占用系统资源发送请求时,常规请求会消耗一些资源:CPU(负责计算)、系统内存、网络链接等系统自有资源;如果我们的系统是基于Java的,还涉及到JVM资源的占用,JVM堆和栈资源,其中Heap是一个比较重要的指标。如果这个请求需要和DB进行交互,在连接DB进行操作的过程中会消耗系统的数据库连接池资源。相应的,在DB端,会消耗DB的计算资源,DB的计算最重要的指标是DB的响应时间和DB的连接数。2.2综合服务这种服务是相对于基础服务的另一个极端。这种服务只依赖于其他服务,没有自己的数据。请求占用系统资源在这个系统中,我们可以把依赖的服务看成是DB,但是在请求过程中不再消耗系统的数据库连接池资源。2.3混合服务这种体系结构是我们最常用的结构。它不仅有自己的业务数据,还有一些计算依赖和其他服务。混合服务的资源消耗会在这个结构中整合以上两种结构的系统消耗。2.4系统资源消耗SystemLoadSystemCPUUtilization如果系统的CPU使用率已经很高,说明我们的系统是一个计算程度非常复杂的系统。这时候如果QPS无法提升,我们就需要通过增加机器快速扩容的方式来共享计算来提高系统的吞吐量。如果系统内存的CPU使用率一般,但是系统的QPS已经加载不出来了,说明我们的机器不是在忙于计算,而是受到了其他资源的限制,比如内存或者io。这时候先检查一下是不是内存不够用。如果内存不够用,那就赶紧扩容吧。对于Java项目来说,JVM中的Heap信息也是内存的直接反映,比如Java老年代的内存比例,是否发生FullGC等等。系统IO系统的IO一般与CPU的使用率是相反的。CPU使用率高的时候,IO使用率不大,IO使用率高的时候,CPU使用率一般都不高。网络带宽(可支持的网络链接数)当我们自己系统的网络带宽被占满时,相当于堵住了系统的出入口。去。在我们的系统中,连接池经常用来连接DB,HTTP连接池也用来向依赖系统发起服务,或者线程池用来提供其他服务。很多时候,由于系统自身的连接池本身有最大连接数的限制,系统连接数会被耗尽,而单个系统中的其他资源还是正常的。这时候可以适当增加连接数来提高系统的吞吐量,但是这种方法需要谨慎,因为连接池过多会更快的消耗系统资源,将压力转嫁给依赖的系统。取决于系统的性能DB性能DB性能往往是系统的根源,因为一旦DB出现重大问题,不仅会导致一个系统出现问题,还可能导致所有系统出现业务逻辑问题取决于数据库。在一般的开发中,在实践中,遇到最多的问题就是SQL不当导致DB读写性能低下,比如不使用索引读写SQL;例如数据库表的锁定范围不合适;当你达到自己的极限时,可以考虑更换机器,更换系统的硬盘,或者增加阅读库等,但是这方面的优化内容非常复杂,会有专门的空间稍后再讨论。依赖服务的性能依赖于其他人的服务,许多人不确定他们系统的性能。如果可能,可以紧急扩展下游系统,解决自身的性能问题;但是对于自己的系统,可以使用fastfailure和interface。降级方式实现。如果上述系统自身指标和依赖系统的指标都比较正常,但是系统的QPS还是加载不出来,说明系统内部有问题,比如系统卡顿了。在系统优化之前需要进行配置文件测试和分析。根据2:8原则,20%的代码消耗了你80%的性能。如果找到20%的代码,就可以优化80%的性能。3常用系统优化技巧3.1代码调优调用接口在异步调用依赖服务时,采用异步并行调用的方式,将多个耗时请求合并发送,可以减少很多不必要的等待时间。IO异步缓存系统中最常用的文件io就是记录日志。记录日志时,设置合适的日志缓存,使用异步方式写入日志文件;在必要的地方记录日志,避免日志滥用,不仅对io造成压力,还浪费系统硬盘空间。在某些极端情况下,系统吞吐量会因硬盘空间耗尽而大幅下降。对于其他需要文件读写的操作,建议使用异步方式,减少阻塞的可能性。API的请求和响应不使用太大的对象。太大的请求和响应会增加网络带宽的压力,太大的字节容易造成数据丢失。适当使用缓存是互联网服务中最常用的优化方法,这里不再赘述。慎用线程有人说线程是恶的,因为多线程的瓶颈在于互斥锁和同步锁,以及线程上下文切换的代价,如何少用或不用锁才是根本。另外,在系统中使用线程池时,避免因线程池模式和数量限制设置不当而成为系统瓶颈。3.2数据库调优数据库加锁的方式。在并发的情况下,锁对性能的影响非常非常大。各种隔离级别,行锁,表锁,页锁,读写锁,事务锁,各种先写先读机制。最好的表现就是不锁。因此,分库分表,冗余数据,减少一致性事务处理,可以有效提升性能。使用索引读写数据时,需要在where条件中检查索引的使用情况。避免SQL级别的join操作SQL中join操作的索引优化是一个非常复杂的问题,因为互联网项目经常变化,数据表的索引也会不断优化。如果你使用join,它可能无法正确索引;而且SQL级别的索引的功能可维护性也很差。对于某些结果集,在query中加入适当的limit,而不是select*,但要明确指出每个字段。如果有多个表,一定要在字段名前加上表名,不要让引擎去计算。不要使用Having,因为它会遍历所有记录。性能再差不过了。尽可能使用UNIONALL而不是UNION。如果索引太多,insert和delete会变慢。而update如果更新大部分索引会很慢,但如果只更新一个,只会影响一张索引表。关于MySQL的优化,现在也有很多相关的资料。推荐使用高性能MySQL(第二版)。这本书对MySQL的高性能有比较深入的探讨。原文:http://blog.brucefeng.info/post/high-qps-service
