有个同学,说是系统中出现性能问题了,说是让我帮助诊断一下。本来是不想花这时间的,结果耐不住对方的死缠乱打,只要答应帮看看。 故事发生的背景是,在文件上传的时候,有时间会有人上传了文件,但是最后没有使用上传的文件,这样就会产生一些垃圾文件。
原来软件作者就想写一个后台定时任务程序,来清除这些垃圾文件?
由于作者坚定的不让我发她的SQL语句(这个我也理解,这么丑陋的SQL),所以这里就不发源代码了,发伪代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void deleteMissLinkFile{ List fileList=getFileList(); List deleteFileList=new ArrayList(); for(file:fileList){ int count1=execute(select count(*) from ...); int count2=execute(select count(*) from ...); int count3=execute(select count(*) from ...); int count4=execute(select count(*) from ...); int count5=execute(select count(*) from ...); if(count1==0&&count2==0&&count3==0&&count4==0&&count5==0){ deleteFileList.add(file); } } delete(deleteFileList); } |
当然,这里我已经给进行了一定的加工,使得看起一漂亮了许多,实际上,嗯嗯,实在是丑。 这个时候的性能情况是怎么样的呢?说是表里的数据只有500多条,但是执行时间要100多秒,但是实际上实际的应用场景都远不止这个数量级,而且随着数据的增加,性能会呈指数级下降。
我说你去加10万条记录测试一下,保证你一晚上算不出来。
好吧,废话少说,接下来看看怎么优化这段程序。
在开始之前,我们可以假设有N个文件,有M个文件引用表,而且假设所有的文件引用表中的记录条数都一样。
很显然,原来的实现方法中执行了:1次文件数查询+N*M次统计操作
最笨的优化方法 先用成本最低的方式来优化一把:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void deleteMissLinkFile{ List fileList=getFileList(); List deleteFileList=new ArrayList(); for(file:fileList){ int count1=execute(select count(*) from ...); if(count1>0)continue; int count2=execute(select count(*) from ...); if(count2>0)continue; int count3=execute(select count(*) from ...); if(count3>0)continue; int count4=execute(select count(*) from ...); if(count4>0)continue; int count5=execute(select count(*) from ...); if(count1>0)continue; deleteFileList.add(file); } delete(deleteFileList); } |
嗯嗯,通过上面的重构,性能马上就可以提升一倍。难看是难看了一点,但是1倍也是不小的提升哦。 原因,原来是要把所有的统计值都算出来,再进行判断,通过上面的重构,平均只要查一半就可以退出了,所以性能会有1倍的提升。
1次文件数查询+N*M/2次统计操作
一般的优化方法 偶当时提醒她说,你可以把内外换换,性能就会提升许多,结果死活听不懂,。
实际上逻辑是这样的,由于统计操作的执行效率是非常低的,而带主键的查询速度是非常快的,也就是把逻辑从:遍历所有的文件看看引用次数是多少,改变成从所有文件列表中删除所有已经引用的文件,其余就是要删除的垃圾文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void deleteMissLinkFile{ List fileList=getFileList(); List refList1=execute(select file from tb1…) for(ref:refList1){ fileList.remove(ref) } List refList2=execute(select file from tb2…) for(ref:refList2){ fileList.remove(ref) } …… delete(deleteFileList); } |
通过上面的优化,需要执行的SQL语句是: 1+m 条SQL语句,其它都是大量的内存数据比对,相对来说,性能会高太多,通过一定的技巧进行一些优化,会有更大的提升。
这种方式,我毛估估比原始的方式,可以提高两个数量级以上。
为什么提高了两个左右数量级还是说比较笨的方法呢?
因为这种方法虽然比原始的方法有了显著的提升,但是还是存在严重的设计问题的。
首先,当数据量比较小的时候(这里的小是指与互联网应用中的数据相比),做完全遍历是没有问题的,但是当数据量比较大的时候,用一条SQL来遍历所有的数据,就是有非常大的问题的。这个时候就要引入一系列的复杂问题来解决,比如:把单机计算变成集群计算,把整个计算变成分段时间,不管怎么样,都是非常复杂的处理过程。
无为而治的方法 下面就要推出最快的、最省事的、效率最高的方法。
其实一般来说,只要是算法都是有优化空间和余地的,因此一般来说本人很少把话说满的。这次本人使用了“最”字,那就是用来表明未来已经没有优化的空间了,那什么样的算法才能没有优化的空间呢?答案就是:啥也不做。
当然了,实际上也不可能啥也不做,问题就在哪里,你不做怎么可能好呢?
实际上就是把任务进行一定的分解。通过把架构进行合理的分析与设计,把所有的文件上传、删除都做成公共的方法(或服务),在需要与文件打交道的地方,凡是与文件打交道的时候,做如下处理:
- 文件上传:在文件上传数据中加一条数据,比如:文件相关信息,唯一标识,引用次数为0
- 文件关联:当数据与文件关联的时候,修改引用次数为+1
- 文件取消关联:当数据与文件取消关联的时候(一般来说是删除或编辑的时候置为空或者换成另外一个的时候),修改引用次数为-1
自次,当要清理垃圾的时候,就非常简单的了,只要:
select ... from ... where ref_times=0
然后进行相应的清理工作就好。
这个时候就优化了处理模式,并且把文件引用数据的维护分解到业务工作的过程当中,可以极大幅度的提升清理垃圾的处理效率。当然有的人说了:如果这么做,会使得我的业务处理过程变慢,那怎么办?其实也没有关系了,你可以把这个变成异步消息的方式,通知文件引用处理去做这件事情就行了,这样就不会影响到你的业务处理效率了。
总结 通过上面的分析,我们对文件上传过程中的垃圾清理过程进行优化,并分析了原来的问题之所在,及后面3种优化方式及其优缺点对比。
当然,实际上许多朋友也会有更好的办法来解决,欢迎大家参与讨论,并批评指正。
如果,你喜欢我的博文,请关注我,以便收到我的最新动态。
如果对我的开源框架感兴趣,可以从这里获取到最新的代码,也可以访问Tiny官网获取更多的消息,或到Tiny社区进行即时交流。
相关推荐
### 基于Devexpress的WinForm开源框架知识点解析 #### 一、DevExpress与WinForms简介 DevExpress是一家专注于为.NET平台提供高质量控件组件的软件公司。DevExpress的产品因其丰富的功能、出色的性能以及良好的用户...
ASP+SQL SERVER 网上购物系统,毕业设计,ASP+SQL SERVER 网上购物系统,毕业设计,ASP+SQL SERVER 网上购物系统,毕业设计ASP+SQL SERVER 网上购物系统,毕业设计,ASP+SQL SERVER 网上购物系统,毕业设计,ASP+SQL...
SSH是指Spring、Struts和Hibernate这三个开源框架的组合,而SQLServer则是由微软公司推出的大型关系型数据库管理系统。在这个"ssh2+sqlserver数据库整合框架"中,我们将深入探讨如何将SSH2框架与SQLServer数据库进行...
第3版、SQL Server、SQL Server 2012 进行深入解析与性能优化
这是一个相当高级的SQL Server监控工具,全面监控SQL Server的活动与性能,分析性能瓶颈,给出优化建议。 监控SQL Server的活动:进程、任务,详细查看当前执行的语句与实际变量值,终止进程 IO/CPU/网络等性能...
《oracle pl/sql程序设计(第5版)(上下册)》基于oracle数据库11g,从pl/sql编程、pl/sql程序结构、pl/sql程序数据、pl/sql中的sql、pl/sql应用构建、高级pl/sql主题这6个方面详细系统地讨论了pl/sql以及如何有效地...
【标题】"又一个开源的仓库管理系统(ASP.NET+SQL SERVER)"揭示了这是一个基于特定技术栈构建的仓库管理软件,其核心开发语言是ASP.NET,数据库管理系统选择的是SQL Server。这个系统选择开源模式,意味着其源代码可...
《SQL Server 2008数据库设计与实现》是关系型数据库设计与开发方面的一本经典著作,由资深数据库专家撰写,旨在向读者介绍世界上最受欢迎的数据库管理系统之一——SQL Server 2008。全书分为三个部分,从基础概念到...
**JeeSite 开源框架详解** JeeSite 是一个基于 Spring Boot、MyBatis、Shiro、Activiti 等主流技术构建的企业级快速开发平台,它提供了丰富的功能模块,旨在简化企业级应用的开发流程,提高开发效率。这个开源框架...
在数据库性能调优领域,Microsoft SQL Server作为一个广泛使用的数据库管理系统,提供了一系列强大的工具和方法来分析和优化数据库性能。其中,动态管理视图(Dynamic Management Views,DMVs)和动态管理函数...
本文将围绕“车站售票管理系统”的SQL课程设计展开,深入探讨如何利用SQL语言构建一个高效、实用的售票系统。 首先,我们要理解车站售票管理系统的功能需求。系统应具备基础的售票、退票、查询余票以及生成统计报告...
本项目是一个高校教务管理系统的实现,涉及到的主要技术包括数据库管理(SQL)、后端编程(Java)以及文档撰写(可能是Word文档,即zzdx.doc)。下面将对这些关键知识点进行详细的解释和探讨。 首先,SQL...
SQL 基础 SQL 首页 SQL 简介 SQL 语法 SQL select SQL distinct SQL where SQL AND & OR SQL Order By SQL insert SQL update SQL delete SQL 高级 SQL Top SQL Like SQL 通配符 SQL In SQL Between ...
一个全开源的ERP系统java写的,亲测可用,代码文档和数据库sql俱全,最新的springboot2.0+框架
在IT行业中,数据库管理系统是核心组成部分,SQL Server和Oracle分别是微软和甲骨文公司推出的两款广泛应用的关系型数据库系统。在企业级应用中,有时需要在不同的数据库系统间进行数据迁移或兼容性处理,这就涉及到...
车站售票管理系统【SQLServer数据库+课程设计文档】 1. 系统需求分析 1.1系统调查 1.2用户需求 2.系统逻辑方案 2.1数据流图(简称为DFD) 2.2数据字典(简称为DD) 2.2.1数据项 3.系统总体结构设计 3.1软件...
在MyBatis框架中,拦截器(Interceptor)是一种强大的工具,可以用来在SQL执行前后进行扩展,例如实现分页、动态SQL修改、日志记录等功能。在这个主题中,我们将深入探讨如何利用MyBatis拦截器实现分页以及动态地...
首先,SQL Server Management Studio (SSMS) 是微软官方提供的一个全面的集成环境,专为管理和开发SQL Server设计。它包含了对SQL Server实例的各种管理工具,例如对象资源管理器,用于浏览和操作数据库、表、存储...
《SQL学生宿舍管理系统数据库课程设计E-R+需求分析说明书》 在进行数据库系统设计时,学生宿舍管理系统是一个常见的实例,它能帮助高校有效地管理和优化住宿资源。本课程设计旨在通过SQL语言实现一个全面的学生宿舍...