目录
背景
我的负责任系统在去年年初完成了功能构建,然后开始进入促销阶段。随着促销的逐步加深,已经受到了很多赞美和很多表现。
当我第一次收到Tucao时,我们的心情就是这样:
当越来越多的性能以性能为基础时,我们意识到必须改善接口性能问题的优先级。
然后,我们遵循接口性能监视1周。目前,我们的心情就是这样:
有20多个慢速界面,5个接口的响应时间超过5s,1超过10s,其余的时间高于2s,并且稳定性小于99.8%。
作为出色的后端程序员,这些数据绝对是难以忍受的,我们立即进入了优化界面的漫长方面。这篇文章是我们漫长的工作之旅的摘要。
什么问题会导致界面性能问题
这个问题有很多答案,您需要根据您的业务情况进行分析。
在此处进行不完整的摘要:
问题解决了
|慢速查询(基于MySQL)So所谓的深度分页问题涉及MySQL分页的原则。在正常情况下,MySQL的分页是这样写的:
当然,从学生表中检查20个100到120的数据当然是含义。MySQL将发现前120片数据,放弃前100件,然后返回20件。
当然,当分页不大时,当然没有问题。随着分页的加深,SQL可能会变得这样:
目前,ISQL将检测到10,00020个数据并放弃10,000件。这样的大量数据不得更快。
如何解决它?通常,最好的方法是添加一个条件:
这样,MySQL将采用主密钥索引,直接连接到10,000,然后找到20个数据。最后查询的最大ID以参数方式传递给接口提供商,并将提供通信成本(呼叫聚会:老挝tzu不会更改!)。
这是最简单的问题。我们可以通过:
检查表的索引。互联网上有太多的指数句子,我不会详细介绍。顺便说一句,顺便说一下,在添加索引之前,您需要考虑是否需要添加此索引。如果吸引力的场区别非常低,即使添加了索引,也不会生效。
另外,景点的变更操作可能导致锁定仪。执行SQL时,请确保处于低峰时段(血液和眼泪的历史!!!)
这是对缓慢查询的最困难分析。尽管MySQL提供了解释以评估某个SQL的查询性能,但使用了一个索引。
但是,为什么索引失败?mySQL不会告诉我们我们需要分析自己。从总体上讲,索引失败的原因是这些(可能还不完整):
需要明确提出的是,在添加索引时应评估场区别不佳的情况。如果区别很差,则根本不需要添加该索引。
对于一些示例,例如:
此外,如果它不符合上述所有索引,但是MySQL仍然不使用相应的索引,为什么?
这与MySQL的SQL优化有关。优化SQL时,MySQL将选择适当的索引。MySQL自己的选择算法可能会计算此索引不会提高性能,因此放弃。
在这种情况下,您可以使用Force索引关键字强制索引(建议在修改之前进行实验,是否真的会提高查询效率):
我对查询的加入太多了。从总体上讲,不建议使用子查询,可以更改以加入以进行优化。在同一时间,不应与Join相关的过多表。一般而言,2-3片适用。
具体相关的表是安全的。它需要特定的问题和特定的分析。如果每个表的数据量很小,则成千上万的零件,那么相关表可以更合适,否则需要少花费。
此外,应该提到的是,在大多数情况下,加入是在内存中完成的。如果匹配量很小,或者Job_buffer集相对较大,则速度不会慢。
但是,当联接数据的数据量相对较大时,MySQL将使用在硬盘上为多个表创建临时表的方法。这种明显的效率极低。磁盘的IO并不快,必须关联。
通常,当遇到这种情况时,建议从代码级别分开,首先查询表格上表的数据,然后使用关联的字段作为条件来查询关联的表以形成映射,然后在业务层上组装数据。
一般而言,该索引比加入要快。毕竟,内存中的缝线数据比网络传输和硬盘IO要快得多。
如果仅查看代码,则不容易检查,最好将其与监视和数据库日志结合使用。仍然很慢,高度怀疑其中有太多元素。
一旦这是一个问题,就更容易解决它,但是将其分为一个组,然后检查一次组一次。如果您想更快,则可以介绍多个线程。
此外,如果IN的元素在一定程度上仍然很大,则最好有一个限制:
这个问题,无法解决简单代码的维修,并且需要更改整个数据存储架构。或底层MySQL表或数据库+平板电脑;或者是直接更改基础数据库,并将MySQL转换为专门为大数据设计的数据库。
这项工作是一个系统的项目,需要严格的研究,解决方案设计,方案审查,绩效评估,开发,测试和联合调整。同时,需要严格的数据迁移方案,回滚计划,降级措施和故障治疗计划。
除了上述团队的内部工作外,还可能还进行跨系统沟通工作。毕竟,已经进行了重大变化。在下游系统中调用接口的方法可能需要更改。
出于考虑,这不再是展开。作者很幸运能参与数据库细分工作,其中数量为1亿个数据。他在整个过程的复杂性方面具有深刻的经验,并将在将来分享。
|复杂的业务逻辑通常在周期中称为相同的代码。每个循环的逻辑是一致的,并且无关。
例如,我们想初始化一个列表,将12个月的数据预设到前端:
每个月的明显数据计算是彼此独立的。我们可以使用多线程的方式:
如果它与上面的循环呼叫不同,而是一次又一次的呼叫,并且呼叫中没有依赖性,那么它也可以以多线程的方式执行,例如::::::::::
查看代码:
然后,您可以使用完整的未来解决:
通过这种方式,可以并行执行两个逻辑A B,并且可以并行执行D E的两个逻辑,并且最大执行时间取决于哪个逻辑较慢。
|线程池的设计是不合理的。有时,即使我们使用线程池并并行完成任务,接口的执行效率仍然不够快。在这种情况下可能会发生什么?
首先,这种情况应该怀疑线程池的设计是否不合理。我认为有必要在此处查看线程池的三个重要参数:核心线程的数量,最大线程数和等待队列。
这三个参数如何坐标?当创建线程池时,如果没有预热的线程池,则线程池中的线程为0。当任务提交到线程池时,启动了核心线程。
当核心线程已满时,如果要到达任务,请让任务输入等待队列并开始等待。
如果队列也填充,则启动了非核心线程操作。
如果螺纹总数达到最大线程数,则仍有任务可以到达,然后开始根据线程池放弃规则。
那么,此操作原理和接口运行时间之间有什么关系?
在调查过程中,只要发现问题的原因,解决方案就很明显。根据业务拆分线程池,依此类推,它不过是调整线程池的参数。
|不合理的锁设计是不合理的:有两种类型的锁类型:不合理或锁定太厚。
锁定类型的不合理典型场景是阅读和写作锁定。换句话说,可以共享阅读,但是当阅读时,您不能编写共享变量。在写作时,无法执行阅读和写作。
当我们可以阅读写锁定时,如果我们互相排斥锁,则在读取的情况下,效率将大大降低,而读写远不止写作。
锁定是不合理的锁设计的另一种常见情况。如果锁的范围太大,则锁定时间将太长,例如:
该逻辑总共有三个部分,计算出,上传结果并发送消息。显然,上传结果和发送消息已完全解锁,因为这不会用共享变量染色。
因此可以更改为:
|FullGC(FullGC,机器的重新启动和线程已满)会导致此问题。作者遇到了时间任务太大而导致FullGC的时间。该线程的代码存在是由高RSS内存引起的,这会导致机器重新启动并等待许多原因。
有必要将特定的监视和特定方案结合起来,以进行特定分析,然后进行大规模的事务分割,重新规划线池等。
|Wanjin油解决方案是从我们单位中的一位老师那里学到的,但是作者感到非常合适。这些金油解决方案通常可以解决界面的大多数缓慢问题,并且通常是我们解决界面效率问题的最终解决方案。
当我们真的没有办法找出问题或没有优化空间时,我们可以尝试这种油。
缓存是时间空间的解决方案。它是高性能存储介质上的数据备份(例如:内存,SSD硬盘等)。
当您有触摸服务器的请求时,您可以从缓存中读取数据优先级。如果您无法读取它,请从硬盘或通过网络获取数据。
由于内存或SSD远高于硬盘或网络IO,因此接口响应速度将变得更快。该缓存适合于数据读数的应用远大于数据编写,并且数据更改并不频繁。
从技术选择的角度来看,有:
当然,Memcachd现在几乎没有使用,因为他没有Redis的优势。Tair是Ali开发的分布式高速缓存中间件。他的优势是,从理论上讲,它可以动态扩展大型数据缓存存储的存储容量。
当然,与单个Redis缓存相比,他具有优势,他与可扩展的Redis簇的比较需要进一步研究。
此外,当前的缓存模型通常是键值模型。如何设计提高缓存率的关键是一个大学问题。优质钥匙设计和不良钥匙设计的性能大不相同。
此外,关键设计没有一定的规则,需要与特定业务方案结合进行分析。各种公司共享的相关文章基本上是最大的空间。
此方法通常是一种业务解决方案,并且在订单或支付系统中使用更多。
例如:当我们付款时,我们需要调用专用付款系统接口。经过一系列验证和存储工作后,系统需要致电银行接口以执行付款。
由于付款的严格要求,银行侧接口的执行可能很慢,然后拖动整个付款接口的性能。
目前,我们可以使用快速成功:当必要的验证和存储完成后,立即返回成功,并告诉呼叫聚会中级“付款”。
然后致电银行界面。在获得付款结果后,上游系统的回调接口的最终结果将调用“结果”或“失败”。这样,付款过程可以异步执行以提高付款接口的效率。
当然,为了防止多企业派对统一访问该党,可以将结果扔进Kafka,让呼叫党听取其自己的结果。
总结
本文是工作中遇到的性能优化问题的简单摘要。可能有不完整的地方。欢迎大家讨论和交流。