一次SQL语句优化的反思:技术和业务的脱节如何解决?

作者介绍

罗敏,从事Oracle技术研究、开发和服务工作20余年,在Oracle中国公司的10多年,分别在顾问咨询部、技术服务部担任资深技术顾问。曾参与国内银行、电信、政府等多个行业大型IT系统的建设和运维服务工作,为国内主要软件开发商和集成商进行过多场Oracle高级技术应用培训和交流活动。著有书籍《品悟性能优化》、《感悟Oracle核心技术》、《Oracle数据库技术服务案例精选》。

 

今天本人不妨对一条不太复杂的SQL语句在技术上进行深入剖析,与大家共同分享其中的实施经验和实施方法,更对其中折射出的一些深层次问题发表些许感悟。

 

1一条不太复杂的SQL语句
 

这是一条来自某行业数据仓库系统的SQL语句:

 

SELECT /*+ PARALLEL(4) */

     A.NSRZHDAH, A.SSSQQ, A.SSSQZ, A.NSRSBH

      FROM J1_LDM.LDMT02_YE_SBXX A, J1_DI.DI_ZSXM B

     WHERE A.SBQX >= '20160101'

       AND A.SBQX <= '20160331'

       AND A.GZLX_DM IN ('1', '2')

       AND A.ZSXM_DM = B.ZSXM_DM

       AND B.SFBZ_DM = '1'

       AND (A.SBRQ > A.SBQX OR SBRQ IS NULL)),

 

这是该语句现有的执行计划:

                                            

 

该语句现有执行时间为5分58秒,逻辑读为386750,物理读为130678 。

 

上述执行计划中Oracle对表LDMT02_YE_SBXX表是按SBQX字段索引进行访问,而DI_ZSXM是一个代码表,该表进行全表扫描非常正常。进一步,我发现LDMT02_YE_SBXX表按SBRQ(申报日期)字段按月进行了分区,而SBQX(申报期限)字段索引是按月进行Global Range Partition分区的分区索引。另外,对这两个表的访问Oracle均采用了并行处理技术。

 

该语句现有执行计划似乎很正常,也就是好像没什么优化空间。查询2006年1月1日至2016年3月31日3个月的交易数据,需要将近6分钟在业务上似乎也很正常。

 

2全表扫描居然比索引访问快!
 

尽管看似正常,但我还是想尝试优化的可能性。首先,我发现针对这种查询3个月交易数据的典型的大批量数据访问语句,开发人员强制使用并行处理的策略是非常正确的。但是,开发人员的Hint: /*+ PARALLEL(4) */ 编写方式得有点另类,那就是为什么只写并行度(DOP)为4,而不写表名呢?于是我尝试将该语句Hint修改为:/*+ PARALLEL(a,4) */。再观察执行计划,奇怪的事情发生了:

 

 

原来,Oracle优化器此时对LDMT02_YE_SBXX表的访问不再走SBQX字段索引,而改走全表扫描了!语句实际执行效果呢?执行时间为2分52秒,速度提高1倍!但是逻辑读为651387,物理读为620747。也就是说,尽管资源开销更大,但针对数据仓库应用,Oracle全表扫描速度比按索引访问更快!

 

3进一步优化空间
 

尽管执行时间更快了,但针对几千万、甚至几亿条记录的交易明细表进行全表扫描总不是一种好策略,而且资源消耗的确更大了,毕竟该语句只是查询3个月的数据。

 

假设将该表调整为按SBQX(申报期限)字段进行月分区,Oracle将采用分区裁剪功能,也就是该语句只查询3个月的分区数据,性能一定能大幅度提升!不仅预计响应速度在1分钟之内,甚至秒级,而且逻辑读、物理读都将大幅度下降。

 

可惜我在现场工作时间有限,也无法在生产或测试环境进行这种调整分区策略的大动干戈的验证,但略有经验的数据库设计和开发人员都知道这种优化策略的效果是显而易见的。

 

4原来还是对分区技术了解不够
 

当我向应用开发人员提出上述分区表改造建议时,他们也说出了他们的设计初衷:原来他们是考虑应用主要是通过SBRQ(申报日期)字段进行查询,因此就考虑按SBRQ(申报日期)字段进行分区设计了。同时,他们也对我的优化方案表示了担忧:如果改成按SBQX(申报期限)字段进行分区,那按SBRQ(申报日期)字段进行查询的SQL语句是不是就性能下降了?这就是很多数据库设计和应用开发人员的一个误区:以为SQL语句按哪个字段做条件多,就一定要按这个字段进行分区。

 

究其根源,设计者和开发人员还是对Oracle的分区技术,尤其是分区索引技术的了解不深入。回到该问题,尽管表按SBQX(申报期限)进行分区了,SBRQ(申报日期)字段仍然可以建成分区索引,包括Global Range Partition索引或者Local non-prefixed Partition索引,依然可以提高按SBRQ(申报日期)字段进行访问的效率。这就是这两种索引结合该案例的示意图:

 

 

 

在Global Range Partition索引情况下,Oracle优化器能保证只访问一个索引子分区,访问效率仍然会有提升。但是,一旦出现分区管理操作(drop、split、merge等),将导致Global Range Partition索引失效,也就是导致业务连续性下降。

 

在Local non-prefixed Partition索引情况下,不会出现索引失效情况,也就是业务连续性尚可,但除非在SQL语句中增加分区字段SBQX(申报期限)的条件,否则,很可能导致该索引全扫描,性能反而会下降。

 

综合利弊,尤其是考虑SBQX(申报期限)和SBRQ(申报日期)两个字段本身的业务逻辑,SBQX(申报期限)保存的是月数据,是更宏观的字段,而SBRQ(申报日期)保存的是天数据,是更微观的字段。显然,将J1_LDM.LDMT02_YE_SBXX表按SBQX(申报期限)字段进行按月范围分区,更符合业务逻辑。

 

5感悟1:数据仓库应用技术运用的针对性和合理性
 

通过上述这个并非复杂的SQL语句的深入剖析,我想首先感悟的就是:广大应用开发人员在开发数据仓库应用时技术运用一定要有针对性,同时也要保证技术运用的合理性。

 

众所周知,IT系统总体上可分为联机交易系统(OLTP)和联机分析系统(OLAP)两类,OLAP系统也可称之为数据仓库系统。这两类系统无论在业务特征还是适用的技术方面都迥然不同。对OLTP应用,具有并发量大、单笔事务处理的数据量小等特点,应以系统的响应速度作为首要的优化目标。而OLAP应用则具有大批量数据处理、并发事务低特点,应该以系统整体吞吐量作为优化目标。OLAP系统在具体技术运用策略方面,应该贯彻大批量、并行处理思路,并合理运用Oracle并行处理、分区操作、HASH-JOIN、MERGE语句、数据仓库函数、外部表、位图索引、物化视图等典型技术。

 

索引技术其实更适合于OLTP系统中的小事务处理,而针对该案例查询3个月数据的大批量数据处理,全表或全分区扫描加并行处理则是更适合的技术策略了。回到该案例,当我告诉应用开发人员全表扫描加并行处理比索引访问更快时,令他们十分惊讶。

 

再者,分区技术既适合于OLTP系统,也适合于OLAP系统,但是针对这两类系统,分区技术运用策略和实现目标是不同的。我们应该深入分析应用需求特点,结合Oracle丰富的分区技术,有针对性地选择分区字段,并合理设计分区索引。例如,若只考虑性能,则将SBRQ(申报日期)设计为Global Range Partition索引是合理的。若作为数据仓库系统,未来需要按SBQX(申报期限)按月进行分区的历史数据管理操作,则SBRQ(申报日期)设计为Local non-prefixed Partition索引,将不会出现索引失效情况,也就是业务连续性将更高。同时,针对数据仓库应用具有按时间进行访问和分析的特点,尽量建议在语句中增加分区字段SBQX(申报期限)条件,这样性能也能得到保证。若这样设计就更全面、更合理了。

 

6感悟2:业务与技术结合的重要性
 

通过该案例,大家也一定能感悟到这一点,那就是业务与技术结合的重要性。我想这也正是国内IT行业多年来一直存在的一个痼疾:一方面,业务人员包括应用开发人员尽管非常熟悉应用逻辑,但对相关底层技术的确缺乏深入、细致的了解,在技术运用方面的确存在明显的误区和不足,例如本案例中应用开发人员就是对Oracle的各种分区技术,尤其是各种分区索引技术的原理、适用场景等缺乏全面、深入的理解。而另一方面,熟悉底层技术的人员,又不愿意去关注业务逻辑和实际需求。总之,这就是典型的业务和技术的脱节问题。

 

这种局面的存在导致什么后果呢?这就是IT系统普遍存在的粗糙、性能低下、资源消耗过大等问题。继续回到该案例,无论是客户,还是技术人员一定只是感受到应用响应速度慢、I/O资源消耗太大等表面现象,进而简单地认为是存储系统效率太低,甚至感觉是Oracle对海量数据仓库系统处理能力有限,需要给系统瘦身,进行历史数据迁移,从而进一步加剧整个系统架构的复杂性。设计开发人员甚至以现在流行分库设计为理由,欲将该数据仓库大卸八块,进行拆库操作,殊不知都是数据库设计和应用开发本身的问题,把库拆小了,只是缓解了问题,并没有从根本上解决问题。

 

业务与技术的有机结合是何等重要!


时间: 2024-11-09 05:51:34

一次SQL语句优化的反思:技术和业务的脱节如何解决?的相关文章

秋色园QBlog技术原理解析:性能优化篇:打印页面SQL,全局的SQL语句优化(十三)

文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文件的作用 2: 秋色园QBlog技术原理解析:认识整站处理流程(二) --介绍秋色园业务处理流程 3: 秋色园QBlog技术原理解析:UrlRewrite之无后缀URL原理(三) --介绍如何实现无后缀URL 4: 秋色园QBlog技术原理解析:UrlRewrite之URL重定向体系(四) --介绍URL如何定位到处理程序 5: 秋色园QBlog技术原理解析:Module之页面基类设计(五) --介绍创建

sql语句优化之SQL Server(详细整理)_MsSql

MS SQL Server查询优化方法 查询速度慢的原因很多,常见如下几种 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 5.网络速度慢 6.查询出的数据量过大(可以采用多次查询,其他的方法降低数据量) 7.锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷) 8.sp_lock,sp_who,活动的用户查看,原因是读写竞争资源. 9.返回了不必要的行和列 10.查询语句不好,

sql语句优化,很着急,在线等,sql如下

问题描述 sql语句优化,很着急,在线等,sql如下 SELECT t1.user_id AS user_id, t1.user_name AS user_name, t1.serv_num AS serv_num, t1.create_date AS create_date, t1.connect_type AS connect_type, t1.login_device AS login_device, t1.login_time AS login_time, ( SELECT timest

sql优化-mysql数据库sql语句优化,求大神!!!!

问题描述 mysql数据库sql语句优化,求大神!!!! SELECT DISTINCT uid, level,username,ansnum FROM test WHERE level=100 GROUP BY uid ORDER BY ansnum DESC LIMIT 12; uid.ansnum均已建索引,主要是GROUP BY uid导致特别慢,如何提速??? 解决方案 MySQL数据库SQL语句优化原则 解决方案二: 根据你的查询需求,没有特别好的优化办法.注意group by 和o

SQL语句优化

前一段时间一直在优化系统,看了一些关于SQL语句优化的东西,在这里分享一下. 1.统一SQL语句的写法 对于以下两句SQL语句,程序员认为是相同的,数据库查询优化器认为是不同的. select*from dual select*From dual 其实就是大小写不同,查询分析器就认为是两句不同的SQL语句,必须进行两次解析.生成2个执行计划.所以作为程序员,应该保证相同的查询语句在任何地方都一致,多一个空格都不行! 2.使用"临时表"暂存中间结果 简化SQL语句的重要方法就是采用临时表

信息-跪等大神——SQL语句优化

问题描述 跪等大神--SQL语句优化 这个语句如何优化跪等: select A0188,A0190,A0101,cast(dp.dept_code as int) as a01depcode,A0177,case when A0107='男' then 2 else 3 end as sex,A0117,A0159,A0148,MobileTel,A01069,A01071,GW_NAME,case when A0191='在职人员' then 9 else 7 end as a0191 fro

sql语句优化分享

sql语句优化分享 这是查询学生数据的逻辑,逻辑比较有点乱,这个查询跑30分钟也不会出结果,一执行CPU立马100%,虽然是个虚似机,但也不至于这种查询也对付不了,肯定有优化的地方.     SELECT  *        FROM 学生表 WITH(NOLOCK) WHERE          (FromSys IS NULL OR          (             (FromSys<>'A' AND FromSys<>'B' AND FromSys<>

[20151221]sql语句优化.txt

[20151221]sql语句优化.txt --自从发现开发乱用distinct以后,链接http://blog.itpub.net/267265/viewspace-1871989/ --我看sql语句特别注意连接多个表,但是显示仅仅一个表的情况,上个星期五,发现一条: sql_id=dpdk3xfd6cvky SELECT EMR_DJMX.ZSFL     FROM MS_YJ01, L_LIS_SQDMX, EMR_DJMX    WHERE     MS_YJ01.YJXH IN (

[20131204]sql语句优化.txt

[20131204]sql语句优化.txt 昨天优化sql语句,遇到一些细节问题,做一个记录: SCOTT@test> @verBANNER--------------------------------------------------------------------------------Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production create table t (id numb