走向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秒后的内容。

      所以采取临时表策略。

?
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
29
    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+ ')'
--SET @THKeyTextforB ='select 二级表_B_被三级关联键 From 二级表_B with(nolock) where 二级表_B_关联一级表键 in (' + @SubKeyTextforBank+')'

 --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


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



本文转自 熬夜的虫子  51CTO博客,原文链接:http://blog.51cto.com/dubing/712446

相关文章
|
8月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
11月前
|
SQL 安全 关系型数据库
SQL注入之万能密码:原理、实践与防御全解析
本文深入解析了“万能密码”攻击的运行机制及其危险性,通过实例展示了SQL注入的基本原理与变种形式。文章还提供了企业级防御方案,包括参数化查询、输入验证、权限控制及WAF规则配置等深度防御策略。同时,探讨了二阶注入和布尔盲注等新型攻击方式,并给出开发者自查清单。最后强调安全防护需持续改进,无绝对安全,建议使用成熟ORM框架并定期审计。技术内容仅供学习参考,严禁非法用途。
1718 0
|
10月前
|
SQL 存储 自然语言处理
SQL的解析和优化的原理:一条sql 执行过程是什么?
SQL的解析和优化的原理:一条sql 执行过程是什么?
SQL的解析和优化的原理:一条sql 执行过程是什么?
|
11月前
|
SQL 人工智能 自然语言处理
Text2SQL圣经:从0到1精通Text2Sql(Chat2Sql)的原理,以及Text2Sql开源项目的使用
Text2SQL圣经:从0到1精通Text2Sql(Chat2Sql)的原理,以及Text2Sql开源项目的使用
Text2SQL圣经:从0到1精通Text2Sql(Chat2Sql)的原理,以及Text2Sql开源项目的使用
|
SQL 缓存 Java
框架源码私享笔记(02)Mybatis核心框架原理 | 一条SQL透析核心组件功能特性
本文详细解构了MyBatis的工作机制,包括解析配置、创建连接、执行SQL、结果封装和关闭连接等步骤。文章还介绍了MyBatis的五大核心功能特性:支持动态SQL、缓存机制(一级和二级缓存)、插件扩展、延迟加载和SQL注解,帮助读者深入了解其高效灵活的设计理念。
|
SQL 存储 开发框架
ASPX+MSSQL注如;SQL盲注
在ASPX与MSSQL环境下,SQL注入和SQL盲注是常见且危险的攻击方式。通过参数化查询、输入验证、最小权限原则以及使用WAF等防御措施,可以有效防止此类攻击的发生。了解和掌握这些技术,对于提升应用程序的安全性至关重要。希望本文能为您提供有价值的信息和指导。
290 23
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
4442 11
|
SQL 存储 关系型数据库
MySQL进阶突击系列(01)一条简单SQL搞懂MySQL架构原理 | 含实用命令参数集
本文从MySQL的架构原理出发,详细介绍其SQL查询的全过程,涵盖客户端发起SQL查询、服务端SQL接口、解析器、优化器、存储引擎及日志数据等内容。同时提供了MySQL常用的管理命令参数集,帮助读者深入了解MySQL的技术细节和优化方法。
|
SQL 关系型数据库 数据库
SQL数据库:核心原理与应用实践
随着信息技术的飞速发展,数据库管理系统已成为各类组织和企业中不可或缺的核心组件。在众多数据库管理系统中,SQL(结构化查询语言)数据库以其强大的数据管理能力和灵活性,广泛应用于各类业务场景。本文将深入探讨SQL数据库的基本原理、核心特性以及实际应用。一、SQL数据库概述SQL数据库是一种关系型数据库
522 6
|
SQL 监控 安全
SQL注入公鸡分类及原理
SQL注入公鸡分类及原理