走向DBA[MSSQL篇] 针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串

原文:走向DBA[MSSQL篇] 针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串

测试的结果在此处 本篇详解一下原理



设计背景

由于历史原因,线上库环境数据量及其庞大,很多千万级以上甚至过亿的表。目标是让N张互相关联的表 按照一张源表为基表,数据搬移归档 这里我们举例N为50 每张表数据5000W



最差性能sql进化客串

2表KeyName 字段意义 名称等相同 从bug01 表中取出前500条不在bug02 表中的数据

最差性能:

SELECT TOP 500 a.KeyName FROM bug01 a LEFT JOIN bug02 b on a.KeyName = b.KeyName
WHERE (a.KeyName not in (select distinct b.KeyName From bug02))
ORDER BY a.KeyName asc

 进化体在篇尾揭晓



详细设计

问题点:性能 安全 容错

流程篇 为何如此设计 在下文中会解释

step.1 源表数据过滤

这部分没什么好说的 根据大家自己的业务场景设定不同的过滤规则

step.2 源表数据副本

程序的入口点肯定是源表了,扩展表中的内容都是以源表为Key来展开。那么这个展开的过程如何来做。

首先确定一些概念,这50表中的层级关系如何。可能直接和源表key键关联的表只有10张。

例如我统计市内所有图书馆详细信息,那么我们以图书馆为源表。图书馆关联书架、地址、会员信息。那么这3中信息我们分为一级别表。

书架关联图书类别,地址关联街道信息,会员关联用户借阅信息,那么后面3者我们继续分为二级表,......按照场景继续扩展。

方案1:使用游标 循环源表 根据源表key值 处理和key相关的数据  假设我们没批次处理500跳源表数据

    也就是根据图书馆ID,遍历所有节点。假设我们不分二级三级表,都是一级表 我们的insert操作次数是500*50。select操作同数据量

    这个给谁肯定都不大乐意,而且如果再遍历2级表3级更难想象。

方案2:对源表key数据进行集合,存进变量,然后用in表达式。貌似可行。直接减少到1/500的操作次数。但是这里有个最恐怖的问题。

    变量都有长度,例如varchar 最大长度不能超过65535。

方案3:将源表Key做成一个查询过滤池(相对于一级表 底层的sql where条件语句 下面会详细介绍一下) 相对于第二种方案,我们这种似乎又将操作数提高了。

    不考虑层级的情况下,insert操作50。select操作50*2可以接受.

方案3扩展: 对于一张大表来说 操作50次也不是什么可以乐观的数字,并且这个50还有可能变成500,5000,50000。

      更有一个问题就是,当你操作这500条的时候,可能会有数据干扰,你1秒前取得的这500条可不一定是1秒后的内容。

      所以采取临时表策略。

       CREATE TABLE #p
	(
		OrderID varchar(50),
		primary key (OrderID)
	);
	SET @temp_text = 'INSERT INTO #p '+@KeyText
	--PRINT @temp_text
	EXEC (@temp_text)	

	SET @KeyText = 'SELECT OrderID FROM #p'
	--如果一级表关联的操作次数比较多那么可以访源表操作 以临时表取代物理表
	SET @SubKeyText = 'select 一级表_A_被关联键 From 一级表_A with(nolock) where 一级表_A_关联源表键 in (' + @KeyText+')'

	CREATE TABLE #q
	(
		OrderID varchar(50),
		primary key (OrderID)
	);
	SET @temp_text = 'INSERT INTO #q '+@SubKeyText
	EXEC (@temp_text)
	SET @SubKeyText ='SELECT OrderID FROM #q'

	--如果一级表关联的操作次数不多可以直接生成数据过滤池
	SET @SubKeyTextforA ='select 一级表_B_被二级关联键 From 一级表_B with(nolock) where 一级表_B_关联源表键 in (' + @KeyText+')'
	SET @SubKeyTextforB ='select 一级表_C_被二级关联键 From 一级表_C with(nolock) where 一级表_C_关联源表键 in (' + @KeyText+')'

	--如果存在更多层操作在此处可以继续关联资源过滤池 Demo只做到三层
SET @THKeyTextforA ='select 二级表_A_被三级关联键 From 二级表_A with(nolock) where 二级表_A_关联一级表键 in (' + @SubKeyTextforA+')'

 --step.3 分表归档操作

这个环节的问题是安全 事务如何控制 事务的大小如何衡量 如何容错 以及如何将程序做得可扩展 可维护

大家根据业务场景 区分自己的批次范围 拿虫子这篇demo来说 50张千万级大表 如果是批次5000条以上 事务要放在内层处理 如果是5000条以下 可以放在最外层

事务的大小直接影响性能的波动

容错的方案大家也可以自己设计 虫子的程序员采用第三类表 异常表来重置 失败了就插入 下一个批次直接就过滤

--将错误的批次订单号入异常表
	Insert into 异常表(@ExTable) SELECT OrderID FROM #p
--@ExTable用来存放异常数据 如果当期批次出错 则将本次批次订单信息入库@ExTable下一批次则过滤这些数据再执行
	SET @KeyText = 'SELECT TOP '+CAST(@SynSize AS VARCHAR(10))+' '+@Base_Key+' FROM +
'+@BaseTable+'+ WHERE '+@Base_Key+' not in (select '+@Base_Key+' From '+@ExTable+') '

 如何让程序变的漂亮 可维护

我们在存储过程中同样可以使用面试对象的思想 只不过存储过程没有类这样的概念给我们 那么我们不妨自己设计

用什么 还是临时表

--一级 直接关联源表主键 或为二级被关联的主表
	INSERT INTO #k VALUES ('一级表_A',@Base_Key,@KeyText,'')					--一级表_A
	INSERT INTO #k VALUES ('一级表_B',@Base_Key,@KeyText,'')					--一级表_B
	INSERT INTO #k VALUES ('一级表_C',@Base_Key,@KeyText,'')					--一级表_C
--二级 规则间接关联
	--@SubKeyText相关
	INSERT INTO #k VALUES ('二级表_A','二级表_A_关联一级键',@SubKeyText,'')				--二级表_A
	INSERT INTO #k VALUES ('二级表_B','二级表_B_关联一级键',@SubKeyText,'')				--二级表_B
	INSERT INTO #k VALUES ('二级表_C','二级表_C_关联一级键',@SubKeyText,'')				--二级表_C
--特殊处理
	--自定义操作
	INSERT INTO #k VALUES ('特殊表','特殊表关联键','自定义数据过滤方式','')			

	--其他 自增列处理
	--修改订单,及其取消修改订单状态历史表
	INSERT INTO #k VALUES ('自增表',@Base_Key,@KeyText,'自定义字段')

 --step.4 处理细节 

 游标循环临时表 针对每一张表操作一次

DECLARE CUR_ORDERHEDER INSENSITIVE CURSOR FOR SELECT TableName,KeyName,temptext,colname FROM #k
	OPEN CUR_ORDERHEDER
	FETCH CUR_ORDERHEDER INTO @Cur_Table,@Cur_Key,@Cur_W,@Cur_K
		WHILE @@FETCH_STATUS = 0
			BEGIN
				 EXECUTE P_Task_Sub_Synchronization
				 @OutParam  = @OutParam OUT, @OutMessage = @OutMessage OUT,
			@KeyText =  @Cur_W,@Table= @Cur_Table,@Extension=@Extension,@IsDelSource=@IsDelSource,@KeyName=@Cur_Key,@ColName=@Cur_K
				 --SET @OutMessage = @OutMessage+@OutMessage
				 --PRINT @OutMessage
				 IF @OutParam <> 0
					 BEGIN
						SET @OutMessage = @OutMessage + @Cur_Table +'操作失败'
						ROLLBACK TRAN
						--将错误的批次订单号入异常表
						Insert into 异常表(@ExTable) SELECT OrderID FROM #p
						DROP TABLE #k
						DROP TABLE #p
						DROP TABLE #q
						RETURN
					 END
				 FETCH CUR_ORDERHEDER INTO @Cur_Table,@Cur_Key,@Cur_W,@Cur_K
			END
	ClOSE CUR_ORDERHEDER
	DEALLOCATE CUR_ORDERHEDER

 --step.5 资源释放

 --step.6 流程处理

 

这2个部分就不详细说了  



最差性能sql进化过程

step.1 not in了 就别再distinc了 distinc和not in都是臭名昭著的角色 not in后+dinstinc画蛇添足而已

改后sql:

SELECT TOP 500 a.KeyName FROM bug01 a LEFT JOIN bug02 b on a.KeyName = b.KeyName
WHERE (a.KeyName not in (select  b.KeyName From bug02))
ORDER BY a.KeyName asc

step.2 别名 别小看别名 用图来说话 原sql计划

改后sql:

 SELECT TOP 500 a.KeyName FROM bug01 a LEFT JOIN bug02 b on a.KeyName = b.KeyName
WHERE (a.KeyName not in (select  c.KeyName From bug02 c))
ORDER BY a.KeyName asc

step.3 何必要用外联 直接过滤不就得了 嘿嘿

改后sql:

SELECT TOP 500 a.KeyName FROM bug01 a
WHERE (a.KeyName not in (select  c.KeyName From bug02 c))
ORDER BY a.KeyName asc

step.4 根据luofer同学的建议 再进化一次 直接EXCEPT

SELECT TOP 500 a.KeyName FROM bug01 a except
SELECT b.KeyName from bug02 b



本篇就讲到此处 欢迎大家讨论

时间: 2024-09-25 18:32:31

走向DBA[MSSQL篇] 针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串的相关文章

走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串

原文:走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串 对大量数据进行查询时,可以应用到索引技术.索引是一种特殊类型的数据库对象,它保存着数据表中一列或者多列的排序结果,有效地使用索引可以提高数据的查询效率.大家面试初级.中级或者高级程序员的时候应该大部分都会被问到这样一些问题,你了解索引吗?你知道索引的分类吗?你知道这些索引的区别吗?你如何去创建有效的索引.本章让大家学会反问面试官 hold住全场. --_____-- 友情客串 最近面试的文章比较火 客串一下 我只

走向DBA[MSSQL篇] 从SQL语句的角度 提高数据库的访问性能

原文:走向DBA[MSSQL篇] 从SQL语句的角度 提高数据库的访问性能 最近公司来一个非常虎的dba  10几年的经验 这里就称之为蔡老师吧 在征得我们蔡老同意的前提下  我们来分享一下蔡老给我们带来的宝贵财富 欢迎其他的dba来拍砖  目录 1.什么是执行计划?执行计划是依赖于什么信息.2. 统一SQL语句的写法减少解析开销3. 减少SQL语句的嵌套4. 使用"临时表"暂存中间结果5. OLTP系统SQL语句必须采用绑定变量6. 倾斜字段的绑定变量窥测问题7. begin tra

走向DBA[MSSQL篇] 积跬步行千里

原文:走向DBA[MSSQL篇] 积跬步行千里 不知道大家对SQL系列的感不感兴趣 先在这里探个路 本文针对的读者为SQL菜鸟 欢迎大牛驳论或者补充 既然是探路篇 就先说下数据过滤中的偏门匹配 希望能给大家带来收获 create table 虫子的临时表 ( 编号 varchar(30), 名称 varchar(30), 备注 varchar(100) ) insert into 虫子的临时表 values('编号01','name01','这是一个奇迹') insert into 虫子的临时表

MSSQL · 应用案例 · 日志表设计优化与实现

摘要 这篇文章从日志表问题引入.日志表的共有特性.日志表的设计需求.设计思路以及设计详细实现的角度,阐述了在SQL Server数据库中如何最优化设计日志表来降低系统资源的占用和提高系统吞吐量. 问题引入 在平时与客户服务与交流过程中,我们不止一次的被客人问及这样的场景:我们现在面临如何设计SQL Server日志表方案,如何最优化设计数据库日志记录表.因为,日志表设计会面对如下问题: 表记录数大:日志表由于记录了应用程序的很多操作日志,有的业务有很多步骤,甚至每个步骤操作都会被记录到日志表中

数据库表中日期记录为2009-04-23的形式,如何用sql语句查询2009年4月份的所有记录,请帮帮我!!谢谢!!

问题描述 数据库表中日期记录为2009-04-23的形式,如何用sql语句查询2009年4月份的所有记录,请帮帮我!!谢谢!! 解决方案 解决方案二:补充一句我需要HQL语句解决方案三:fromClasswheredatebetweendate'2009-04-01'anddate'2009-04-30'解决方案四:引用2楼endlesspass的回复: fromClasswheredatebetweendate'2009-04-01'anddate'2009-04-30' 就只有这一种思路了吗

解读IBM POWER8:全面开放 针对大数据设计

[天极网服务器频道专稿]一提起云和大数据,大家会不约而同的谈及"开放"的话题,而谈起开放,许多人首先想到的是X86架构.这一方面是因为,X86在短短的数年时间内,发展极其迅速,优点不言而喻-开放的生态系统.更低的成本.较佳的易用性......性能方面也不断追赶小型机,IBM硬件部门在2013年出现亏损,作为UNIX阵营的代表POWER系统遭到了前所未有的压力. 面对Intel阵营的大军压境,ARM的后发制人,IBM在酝酿3年后于2013年年底发布POWER8处理器,扩展Linux市场,

C#控制台做ATM,SQLserver表已经创建好了,在vs2010代码里面写SQL语句时,该怎么写?我思路现在蛮混乱

问题描述 //第一步:指定连接的数据库SqlConnectionconn=newSqlConnection("server=HHY-PC\HHY;uid=sa;pwd=123456;database=YangATM_DB");//第二步:需要执行的SQL语句stringsql="select*fromKaiHuInFowhereKaHao='"+txtkahao.Text+"'andKaMiMa='"+txtmima.Text+"'&q

MSSQL-应用案例-日志表设计优化与实现

title: MSSQL-应用案例-日志表设计优化与实现 author: 风移 摘要 这篇文章从日志表问题引入.日志表的共有特性.日志表的设计需求.设计思路以及设计详细实现的角度,阐述了在SQL Server数据库中如何最优化设计日志表来降低系统资源的占用和提高系统吞吐量. 问题引入 在平时与客户服务与交流过程中,我们不止一次的被客人问及这样的场景:我们现在面临如何设计SQL Server日志表方案,如何最优化设计数据库日志记录表.因为,日志表设计会面对如下问题: 表记录数大:日志表由于记录了

如何应付表数据过大的查询问题?(如何尽量避免大表关联)

原文:如何应付表数据过大的查询问题?(如何尽量避免大表关联)      一般来说,对于做B/S架构的朋友来说,更有机会遇到高并发的数据库访问情况,因为现在WEB的普及速度就像火箭升空,同时就会因为高访问量带来一系列性能问题,而数据库一直是用户与商人之间交流的重要平台.用户是没有耐心忍受一个查询需要用上10秒以上的,或者更少些,如果经常出现服务器死机或者是报查询超时,我想那将是失败的项目.做了几年的WEB工作,不才,一直没有遇到过大访问量或者是海量数据的情况.这里并不是说没有海量数据的项目就不是好