系列文章目录
1.SpringBoot整合RabbitMQ并实现消息发送与接收
2. 解析JSON格式参数 & 修改对象的key
3. VUE整合Echarts实现简单的数据可视化
4. List<HashMap<String,String>>实现自定义字符串排序(key排序、Value排序)
5. 使用JAVA代码实现生成二维码
6. BCrypt加密算法的使用及原理
更多文章可看我主页哦~
文章目录
前言
性能优化?相信大家并不陌生。对于学生来说,可能直接的接触机会较少,但对算法与数据结构的学习却是不可或缺的。这些基础知识不仅帮助解决具体问题,也为提升整体系统性能奠定了基础。无论是在数据库查询优化、代码开发的效率提升,还是架构设计中的性能考量,性能优化都是系统设计中至关重要的一环。
在接下来的内容中,我将深入探讨如何从多个层面进行有效的性能优化,帮助大家更全面地理解和应用这些最佳实践。
一、数据库优化
数据库优化主要分为以下四类:SQL编写优化、索引设计、查询优化、数据存储与选择。但对于开发者来说常用到的是前三类,着重去理解前三类的优化方式。数据存储与选择作为了解。
1.1 SQL编写优化
优化SQL查询是提升数据库性能的首要步骤,如何做好sql的编写是至关重要的。
1.1.1 禁用select *
首先,尽量避免使用SELECT *,仅选择必要的列。
原因:
- 查询表的所有列数据,可能有些表的列很多,而且有些列会存在一些文本等大量的不必要的查询数据,导致查询性能下降。需要哪些数据就查询哪些列才是最好的。
- 使用select* 会使索引失效,明确列出所需的列可以帮助优化器更好地利用索引,从而提高查询速度。
因此,我们在编写sql查询数据时,正确写法如下:
-- 查询所有用户的用户名及对应的手机号
select username,phone from user;
1.1.2 避免在WHERE子句中使用函数或计算
其次,避免在WHERE子句中使用函数或计算,这会使得索引失效。
原因:
- 索引通常在原始列的值上进行优化,而函数或计算会改变列的值,使得索引失效,从而导致全表扫描,显著降低查询性能。
- 计算或调用函数会导致数据库在执行查询时对每一行数据进行额外的计算。这会增加CPU和内存的使用,特别是当表很大时,这种开销会变得显著。
最好使用后端代码去处理逻辑,sql只执行固定值查询数据。
因此,我们在编写sql时,最好在where后面跟固定值。例如:
-- 查询xintai的用户名及对应的手机号
select username,phone from user where loginname = 'xintai';
1.1.3 使用JOIN代替子查询
最后,使用JOIN代替子查询能提升效率,特别是当子查询涉及大数据集时。
原因:
- 子查询尤其是嵌套子查询,可能会导致多次重复查询,增加性能开销。
- JOIN 操作可以在数据表之间直接合并数据,减少了中间结果集的存储和处理,从而减少了资源消耗。
因此,在执行多表复杂查询时,尽量避免去嵌套select子查询。正确写法如下:
-- 查询所有员工及其所在部门的名称
SELECT e.name,
d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.id;
1.2 索引设计
索引是加速查询的核心工具。设计有效的索引需要考虑查询的具体模式。创建单列索引对经常用于查询的列很有帮助,但复合索引(多个列组合)在多列查询中能显著提高性能。
如何使用索引?这里以mysql举例创建索引:
-- 给user表的username列添加索引,索引名称为idx_username
CREATE INDEX idx_username ON user (username);
这样在执行查询username列的数据时,就会更加快速响应结果。具体的索引为什么会提升查询效率自行学习了解。
需要注意的是,索引虽然加速了读取操作,但会增加写操作的开销,因此应平衡索引的数量与性能需求。
1.3 查询优化
利用数据库的EXPLAIN工具分析查询计划是优化查询的关键。它能帮助识别全表扫描、未使用索引等问题,并提供改进建议。通过调整SQL语句和优化索引,可以减少查询时间。还可以使用缓存机制(如查询缓存)减少对数据库的频繁访问,进一步提升查询性能。
这里以Mysql为例,EXPLAIN 命令可以帮助查看查询计划,了解索引是否被使用,从而做出相应的调整。代码如下所示:
EXPLAIN SELECT * FROM table_name WHERE column_name = 'value';
1.4 数据存储与选择
选择合适的存储引擎直接影响数据库性能。例如,InnoDB引擎支持事务和行级锁,而MyISAM则提供更高的读取性能。根据应用需求选择最适合的存储引擎。数据存储策略也至关重要,使用分区表可以将数据分散到不同的物理文件中,提高查询性能并简化数据管理。同时,定期的数据归档策略可以帮助减少活跃数据表的大小,提高系统的整体性能。
二、代码开发
最为开发人员,代码编写提升效率是非常必要的。相同的业务下,每位开发者开发出的代码效率都有所差别。而对于一些有经验的开发人员在拿到需求后,理解需求的同时梳理开发逻辑,在开发时就会避免一些不必要的资源消耗。
2.1 算法与数据结构
数据结构是开发人员的必修课,然而算法对于大多数开发者而言其实在学习时并不是很重视。而且在大学期间对于算法都是靠自己的兴趣学习理解较多,这也忽略了算法对提升系统性能的影响。建议大家多去刷一些算法题,例如去力扣等平台上刷题。在通过后会有对于运行效率的可视化图展示,便于大家去找到自身的不足,从而去不断地优化算法。下图是我的leetcode刷题查看一些执行用时(当然还有执行消耗内存图,这里不多展示),可看到还有优化空间哈~
为什么要将算法呢?按照一个简单的例子来说,一道简单的数组问题,能使用双指针解决的问题,肯定要比循环的时间、空间复杂度要低。建议大家有空多去刷算法,对于编程思路会有很大的提升~
2.2 内存管理
对于JAVA中而言,有垃圾回收机制。对于堆栈中的信息会有惰性或定时检查删除的机制。所以对于我们开发来说也就不需要去释放对象等操作。但是还是要从代码开发方面说一下,也算是开发规范,例如:
- new对象尽量别再循环中进行,能减少尽量减少。
- 非公共方法禁止使用static,这也是为了垃圾回收机制更好的去回收。
- 公用的对象只new一次,方法用到时进行传参使用。
2.3 并发编程
对于在某一时间处理的业务较多时,可以使用并发编程去分配线程执行各个业务。从而达到主线程的资源较为充足,避免出现延迟的情况。这个我在性能篇–并发实现数据高效同步文章中有详细讲解什么是并发、线程与进程的关系以及如何实现并发场景的案例分析。
但大家在处理并发时,也会带来很多的问题,包括如何保证线程安全下处理数据。这里在我们使用时需要注意。例如stringBuilder和StringBuffer在哪些场景下怎么使用需要理解。在并发场景要保证线程安全,就需要使用StringBuffer。包括如何去避免死锁的情况发生,这些都是需要考虑的。
理解并发编程和内存管理对于编写高效、稳定的代码至关重要,特别是在多线程环境和大规模系统中。在保证系统稳定的前提下提高代码的效率才是最重要的。
三、架构设计
3.1 服务划分
大家在初始学习时都是单体架构模式,所有功能模块都被打包到一个单一的应用程序中。虽然开发和部署较简单,但随着应用的增长,维护和扩展会变得困难。
现在大多数的平台都在采用微服务,将系统拆分成多个小型、独立的服务,每个服务专注于单一功能。服务通过网络进行通信(通常使用 REST API、gRPC 等),这样的优点就是也显而易见,拆成多个小模块,哪个服务有问题直接排查对应服务就可以,更容易去修改并且不会影响其他服务之间的状态。具体的优点如下:
- 可维护性:小型服务更易于理解和修改。
- 扩展性:可以独立地扩展各个服务以适应不同的负载。
- 容错性:服务的隔离性使得单个服务的失败不会影响整个系统。
- 技术多样性:可以使用不同的技术栈来实现不同的服务。
这里注意数据库也可以是一个或多个单独的服务,大家可能理解不了多个是为什么?访问数据库频繁时,一个服务可能资源有限。所以会涉及多个数据库服务,实现读写分离、主从复制、分库分表等操作。
还是同样的,也会有一些潜在的问题需要大家考虑,例如在微服务下怎样实现服务间的通信?如何处理分布式事务?数据一致性?大家还需思考,查找相关资料~
3.2 缓存机制
缓存机制(Caching)用于临时存储计算结果或数据,以减少重复计算或重复访问慢速存储的次数,从而提高系统性能。分为内存缓存、磁盘缓存以及CDN三类,具体信息如下:
-
内存缓存:将数据存储在内存中,如使用 Redis、Memcached。这种缓存访问速度极快,适合频繁访问的数据。
-
磁盘缓存:将数据存储在磁盘上,如使用本地文件系统或数据库。这种缓存适用于数据量大且访问频率较低的场景。
-
CDN:用于缓存静态内容(如图片、视频、网页等),将内容分发到离用户更近的节点,减少延迟和带宽消耗。
这样做的好处就是可以避免重复的计算以及数据访问,从而减少数据库的压力,有效地提高系统的并发处理能力。
缓存的设计固然是好的,但在设计时要考虑一些热点数据处理(缓存击穿、雪崩等)以及数据的一致性等问题,让其成为数据库最坚实的“盾”。
3.3 负载均衡
负载均衡:用于将请求或负载分散到多个服务器或资源上,像上面提到的微服务时,可能有多个数据库服务器去处理,但是在请求时,该分配到哪个服务器,如何请求的规则又是怎样的?这就是负载均衡干的事情。通过配置有效的把接口请求进行分配到各个服务器。使各服务器的压力处于均衡状态~
这里我只了解springcloud中的Ribbon,通过配置对应的策略进行服务器的分发请求。具体的策略如下:
- 轮询(Round Robin):依次将请求分发给每个服务器,简单而公平。
- 最少连接(Least Connections):将请求分发给当前连接数最少的服务器,适用于连接负载不均匀的场景。
- 加权轮询(Weighted Round Robin):根据服务器的能力(权重)分发请求,更强的服务器处理更多的请求。(能力越强,责任越大~)
- IP 哈希(IP Hash):根据请求的 IP 地址分配到固定的服务器,适用于需要会话保持的场景。(这个解释一下,例如百度就是用的这个策略,全国各地多会有服务器,在用户访问时肯定会通过ip接到最近的服务器处理请求~)
优点显而易见,例如我们去吃饭(两家店是连锁店,并且就在附近),去了一家发现排队人员很多,可以去另一家~ 请求的接口也是一样,只不过它不会自己思考,需要负载均衡去告诉它去哪里更好~
但如何保持用户会话呢?发到相同服务器?如何实现策略的配置?这些还需要大家思考~
总结
系统的优化方面有很多,作为开发人员最主要的就是要对于数据库的访问以及代码开发的细节点进行优化处理。但对于架构师来说更多的是注重服务的原则以及微服务的设计模式。这篇内容重在让大家有一定的理解,具体的优化方法还需大家在今后的开发或者设计中思考~
本篇文章到此就结束啦!有问题或者更好的优化理解可以在评论区多加讨论哦~ 一起学习进步