针对大表 设计高效的存储过程【原理篇】 附最差性能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秒后的内容。
所以采取临时表策略。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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 处理细节
游标循环临时表 针对每一张表操作一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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
熬夜的虫子:http://www.cnblogs.com/dubing/archive/2011/11/11/2245836.html
针对大表 设计高效的存储过程【原理篇】 附最差性能sql语句进化过程客串相关推荐
- 数据可视化大屏设计经验分享 【进阶篇】
前言 说起数据可视化设计,如今绝对是热门的设计之一,而真正懂数据可视化设计的设计师却不多,随着大数据产业的蓬勃发展,很多企业都开始应用数据可视化.很多UI设计师突然会接到公司数据可视化设计的需求,如果 ...
- 【Apache Kylin 】 大数据下的OLAP解决方案(原理篇)
前言 在现在的大数据时代,Hadoop已经成为大数据事实上的标准规范,一大批工具陆陆续续围绕Hadoop平台来构建,用来解决不同场景下的需求. 让我们来想想有哪些业务需求呢? 比如Hive是基于Had ...
- mysql 取出20条数据_“取出数据表中第10条到第20条记录”的sql语句+select top 使用方法...
1.首先.select top使用方法: select * from table -- 取全部数据.返回无序集合 select top n * from table -- 依据表内数据存储顺序取前n ...
- mysql查询第10到第20条记录_“取出数据表中第10条到第20条记录”的sql语句+selecttop用法...
1.首先,select top用法: 参考问题 select top n * from和select * from的区别 select * from table -- 取所有数据,返回无序集合 sel ...
- oracle无会话锁表,深入浅出oracle锁 原理篇 停止无反应的sql会话
在现代的多用户多任务系统中,必然会出现多个用户同时访问共享的某个对象,这个对象可能是表,行,或者内存结构,为了解决多个用户并发性访问带来的数据的安全性,完整性及一致性问题,必须要有一种机制,来使对这些 ...
- kettle分批处理大表数据_Kettle大量数据快速导出的解决方案(利用SQL导出百万级数据,挺快的)...
org.apache.commons commons-vfs2 2.0 org.scannotation scannotation 1.0.3 dom4j dom4j 1.6.1 pentaho-ke ...
- mysql取最大一条数据,mysql取出表中,某字段值最大的一条纪录,sql语句
一个为交易表,有唯一id,别名trade_id 一个为交易状态详情表,记录trade_id的多个状态. 进行了两个表查询.查询某个trade_id的交易情况及最新状态. 直接粘贴此sql,执行即知. ...
- mysql存储过程是不是不能穿sql语句_mysql存储过程能不能直接执行拼接的sql语句...
展开全部 当然可以,就是在mysql存储过程中使用动态sql,就可以拼接sql,然62616964757a686964616fe58685e5aeb931333361323562后执行了. 给你复制一 ...
- mysql存储过程语法错误1064_mysql,dos下执行SQL语句创建存储过程出错ERROR 1064 (42000):...
update1.sql的内容为 DROP PROCEDURE IF EXISTS pcName; CREATE PROCEDURE pcName() BEGIN select 'a'; END; do ...
最新文章
- 【摘录】GestureDector使用
- 在PHP中实现StringBuilder类
- vs2019 MFC 中 cannot open include file 'afxres.h' 问题解决方法
- Linux shell编程学习笔记-----第十四章
- GlusterFS配置管理(五)
- Json.NET Deserialize时如何忽略$id等特殊属性
- 桌面怎么设置 计算机 网络,Win10 10130桌面电脑网络图标怎么设置?
- 图书管理系统可行性分析报告范例_会做可行性分析报告贺州专家团队*金
- 在国外当程序员有多爽
- 【转】VBScript-RegExp对象的详细用法
- 童鞋们,颜色采色器,实用工具
- c语言的数据结构,c语言中数据结构是什么?常见数据结构有哪些?
- linux界面美化 简书,ubuntu美化
- 【Orientation】详解Android中的屏幕方向
- 计算机中央的处理器由什么构成,计算机中央处理器CPU的由什么组成
- NC57中间表数据源的设置流程
- python手撕分水岭算法
- 我所知道的张小龙 by和菜头
- solidworks中工程图标注三面焊符号
- 如何做一个win10系统盘
热门文章
- C++smallest circle 获取外接给定点集的最小圆的中心和半径算法(附完整源码)
- c++Data Member的绑定
- vscode只有utf8_基于VSCode搭建LaTeX写作环境
- 11_Eclipse中演示Git版本的创建,历史版本的修改,创建分支,合并历史版本和当前版本
- Maven项目中获取classpath和资源文件的路径
- 开发人员需要熟知的常用Linux命令Version、Kernel查看
- SQLite 语法(http://www.w3cschool.cc/sqlite/sqlite-syntax.html)
- 调用webservice 设置超时时间
- 深度学习TensorFlow取名由来,张量的理解
- 怎么调整字段长短_木北造型:空气刘海卷发筒怎么用 木北造型告诉您