走向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
本篇就讲到此处 欢迎大家讨论
走向DBA[MSSQL篇] 针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串的更多相关文章
- 走向DBA[MSSQL篇] 详解游标
原文:走向DBA[MSSQL篇] 详解游标 前篇回顾:上一篇虫子介绍了一些不常用的数据过滤方式,本篇详细介绍下游标. 概念 简单点说游标的作用就是存储一个结果集,并根据语法将这个结果集的数据逐条处理. ...
- 走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串
原文:走向DBA[MSSQL篇] 面试官最喜欢的问题 ----索引+C#面试题客串 对大量数据进行查询时,可以应用到索引技术.索引是一种特殊类型的数据库对象,它保存着数据表中一列或者多列的排序结果,有 ...
- 走向DBA[MSSQL篇] 从SQL语句的角度 提高数据库的访问性能
原文:走向DBA[MSSQL篇] 从SQL语句的角度 提高数据库的访问性能 最近公司来一个非常虎的dba 10几年的经验 这里就称之为蔡老师吧 在征得我们蔡老同意的前提下 我们来分享一下蔡老给我们 ...
- 走向DBA[MSSQL篇] 积跬步行千里
原文:走向DBA[MSSQL篇] 积跬步行千里 不知道大家对SQL系列的感不感兴趣 先在这里探个路 本文针对的读者为SQL菜鸟 欢迎大牛驳论或者补充 既然是探路篇 就先说下数据过滤中的偏门匹配 希望能 ...
- 走向DBA[MSSQL篇] - 从SQL语句的角度提高数据库的访问性能(转)
最近公司来一个非常虎的DBA,10几年的经验,这里就称之为蔡老师吧,在征得我们蔡老同意的前提下 ,我们来分享一下蔡老给我们带来的宝贵财富,欢迎其他的DBA来拍砖. 目录 1.什么是执行计划?执行计划 ...
- mysql大表设计以及优化
MYSQL千万级数据量的优化方法积累https://m.toutiao.com/group/6583260372269007374/?iid=6583260372269007374 MySQL 千万级 ...
- 打开黑盒:从 MySQL架构设计出发,看它是如何执行一条 SQL语句的
1.把MySQL当个黑盒子一样执行SQL语句 我们的系统采用数据库连接池的方式去并发访问数据库,然后数据库自己其实也会维护一个连接池,其中管理了各种系统跟这台数据库服务器建立的所有连接 当我们的系统只 ...
- “取出数据表中第10条到第20条记录”的sql语句+selecttop用法
1.首先,select top用法: 参考问题 select top n * from和select * from的区别 select * from table -- 取所有数据,返回无序集合 sel ...
- “取出数据表中第10条到第20条记录”的sql语句+select top 使用方法
1.首先.select top使用方法: 參考问题 select top n * from和select * from的差别 select * from table -- 取全部数据.返回无序集合 ...
随机推荐
- TCP header
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3Vzc2VyNDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...
- iOS Dev (55) 获得本年度、月、日本和其他信息
iOS Dev (55) 获得本年度.月.日本和其他信息 作者:大锐哥 博客:http://prevention.iteye.com - NSDate *now = [NSDate date]; NS ...
- veridata实验例(3)验证veridata发现insert操作不会导致同步
veridata实验例(3)验证veridata发现insert操作不会导致同步 续接:<veridata实验举例(2)验证表BONUS与表SALGRADE两节点同步情况>,地址:点击打开 ...
- java打印各种菱形
/** * 类说明 * * @author 曾修建 * @version 创建时间:2014-7-23 上午09:50:46 */ public class Diamond { public stat ...
- [改变自己wordpress.2]至wordpress再加上简单的debug sql调试.
或者说,同事. 需要帮助她打印出来sql 调试输出到页面sql 在这里,我们使用插件或一个的方式来启动配置文件wordpress的debug 在插件文件夹 wordpress/wp-content/p ...
- Quartz.NET学习系列
Quartz.NET它是一个开源的任务调度引擎,对于周期性任务,持久性任务提供了很好的支持,并且支持持久性.集群等功能. 这是什么对我来说Quartz.NET学习记录: 源代码下载http://yun ...
- [cocos2dx笔记008]cocos2d 用luabridge手动绑定类
基于cocos2dx 2.2.2版本号.这几天使用了cocostudio实现了,动画.骨骼动画.UI编辑.粒子效果,尽管有些不足,但已经算是很好了.今天尝试用lua.这个很easy.创建的时候.设置语 ...
- 【原创】纯OO:从设计到编码写一个FlappyBird (六)
第五部分请看这里 终于到了最后一个部分了! 这里使用SimpleJudge类来实现Judge接口. 首先是SimpleJudge需要的实例变量: 0.final LinkedList<Pilla ...
- 使用DbUtils实现CRUD
commons-dbutils 这是 Apache 提供了一个开源组织 JDBC工具库,它是JDBC简包,学习成本低,和使用dbutils可以大大简化jdbc的工作量,同一时候也不会影响程序的性能.因 ...
- 让你提前认识软件开发(17):makefile文件的书写及应用
第1部分 又一次认识C语言 makefile文件的书写及应用 [文章摘要] makefile用于Linux下整个project的编译.对于Linux下的C/C++语言的编译是至关重要的. 本文以实际的 ...