我们有一个重要的旧系统,最近夜维出现了一些问题,夜间执行5小时未完成,为了不影响业务,只能早上高峰期之前,DBA手工kill夜维进程。

这一个夜维程序采用了PLSQL写的存储过程,通过数据库job定时启动执行。存储过程我很少使用,借着这次机会,补习了下,这个存储过程中的逻辑比较简单,依次删除若干张业务表,每张表删除的逻辑相同,为了便于说明,模拟了下删除一张表的逻辑,示例如下,

TBL_CUSS表三个字段,第一个字段是NUMBER类型,第二个字段是VARCHAR2类型,第三个字段是DATE类型,

这个存储过程接受一个参数,表示删除几天前的数据,删除DELETE语句按照一个时间字段和这个参数比较得出符合条件的记录集,同时使用rownum限制每次执行DELETE-COMMIT事务的条数,循环执行,直至出现ORA-1403无记录的提示,退出此逻辑。这个存储过程最优的地方,是使用了批量提交,不是执行一次DELETE删除所有记录COMMIT,如果这么执行,可能会占用大量的UNDO回滚段,进而可能出现回滚段空间不足的报错,也可能出现ORA-1555的错误。毕竟UNDO中记录的是SQL语句的逆向,对于DELETE语句,逆向就是INSERT,即会存储删除的整条记录。

可能有朋友一眼就看出这个存储过程的逻辑有一些问题,比如对于这种批量删除,未使用游标,相当于每次要检索tbl_cuss表符合insert_time < trunc(SYSDATE)-:1条件的记录,可每次仅删除其中的rownum限制的条数,如果使用游标,检索只需要一次执行,不考虑是否有索引,执行语句次数的降低,可以带来性能的一定提升。

针对此问题,写了第二个存储过程,

接受删除天数的参数,使用了游标,执行一次SELECT,读取出的则是符合insert_time < trunc(SYSDATE)-:1条件的所有结果集记录的rowid信息,遍历游标的时候使用BULK批量的方式,设置了一次性执行的条数限制MAX_ROW_SIZE,并且删除语句是根据上面游标获取的rowid为条件进行的DELETE,如果各位了解rowid,可以知道他代表了这条记录的物理位置,通过换算可以得出这条记录存储于的文件、块和行上,即可以快速定位这条记录的物理位置,在RBO模式下,他的成本优先级是最优的,高于索引。

继续写了第三个存储过程,

这和第二个存储过程,基本一致,唯一不同就是第二个存储过程中使用了for循环,第三个存储过程则用forall循环。for循环会执行其中的每条SQL语句,forall则会将其中所有SQL批量发送SQL引擎执行。当然可能有其他的写法,比如使用游标,但不使用BULK,按照rowid删除,这种写法执行SQL语句的次数和结果集数据量一致,效率可能还不如原始procedure。

从原理上说,使用BULK比单条语句执行,减少PLSQL和SQL引擎之间的切换频率,也可以减少redo和undo的产生量。针对循环内执行的DELETE,适合于使用集合,放入forall。

delete和insert都可以从forall上面得到巨大的性能提升。但是对于update来说opcode没有相关操作,提升应该不会那么明显。

接下来我们会对这三个存储过程进行一些比对实验,通过一些数据,说明各自的适用场景,首先创建测试数据集,制造了1300万测试数据,

每天50万数据,一共26天,

第一个存储过程名称为clear_without_fetch。

第二个存储过程名称为clear_all_fetch。

第三个存储过程名称为clear_fetch。

实验有以下几类,(以下执行基本采用第二次执行的结果避免第一次刷缓存)

(1) 一次性删除1万条记录,insert_time不是索引,删除两天的数据(即100万),

clear_without_fetch用时01:16.31

clear_all_fetch用时00:40.50

clear_fetch用时00:21.73

clear_fetch胜出,clear_without_fetch最慢,说明TABLE ACCESS FULL下的SQL语句,一次性删除1万条记录,使用游标和BULK效率要高些,使用forall比for效率要高些。

(2) 一次性删除5万条记录,insert_time不是索引,删除两天的数据(即100万),

clear_without_fetch用时00:26.98

clear_all_fetch用时00:39.80

clear_fetch用时00:22.24

clear_fetch胜出,但一次性删除5万条记录,TABLE ACCESS FULL下的SQL语句由于执行次数减小为原来的5倍,效率上有提升,clear_all_fetch基本一致,是因为无论什么方式,其执行SQL语句的次数,和结果集一致,即100万次SQL。

(3) 一次性删除100万条记录,insert_time不是索引,删除两天的数据(即100万),

clear_without_fetch用时00:21.92

clear_all_fetch用时01:22.00

clear_fetch用时00:25.95

clear_without_fetch胜出,TABLE ACCESS FULL下的SQL语句执行一次,clear_fetch虽然仍是批量一次发送SQL,性能上的优势不很明显。

(4) 一次性删除1万条记录,insert_time是索引,删除两天的数据(即100万),

clear_without_fetch用时00:31.01

clear_all_fetch用时01:09.02

clear_fetch用时00:37.39

clear_without_fetch胜出,索引扫描执行效率提升,相比TABLE ACCESS FULL要明显一些。

(5) 一次性删除5万条记录,insert_time是索引,删除两天的数据(即100万),

clear_without_fetch用时00:35.35

clear_all_fetch用时01:03.26

clear_fetch用时00:37.40

clear_without_fetch胜出,clear_all_fetch和clear_fetch基本保持和1万一致。

(6) 一次性删除100万条记录,insert_time是索引,删除两天的数据(即100万),

clear_without_fetch用时00:23.33

clear_all_fetch用时01:27.80

clear_fetch用时00:33.68

clear_without_fetch胜出,相比1万和5万,效率提升一些,我理解主要是SQL执行次数从100次(1万)->20次(5万)->1次(100万)。

上面的实验中,数据接近的未必是绝对,和环境因素(例如机器配置、数据质量等)可能有关,因此大体方向上可以参考,不同次的实验可能会略有不同,但方向应该比较接近,毕竟原理摆着。

如下是上面六个实验,三个存储过程SQL,各自执行的10046的trace,从中可以看出一些端倪,

clear_without_fetch存储过程各场景trace,

(1) 一次性删除1万条记录,insert_time不是索引,删除两天的数据(即100万),

可以看出由于使用了绑定变量,解析一次,由于循环逻辑的问题,执行了100+1次。

(2) 一次性删除5万条记录,insert_time不是索引,删除两天的数据(即100万),

(3) 一次性删除100万条记录,insert_time不是索引,删除两天的数据(即100万),

从elapsed以及query可以比较(1)-(3)。

(4) 一次性删除1万条记录,insert_time是索引,删除两天的数据(即100万),

(5) 一次性删除5万条记录,insert_time是索引,删除两天的数据(即100万),

(6) 一次性删除100万条记录,insert_time是索引,删除两天的数据(即100万),

由于需要维护索引,相比TABLE ACCESS FULL,会有些消耗。

clear_all_fetch存储过程各场景trace,

(1) 一次性删除1万条记录,insert_time不是索引,删除两天的数据(即100万),

(2) 一次性删除5万条记录,insert_time不是索引,删除两天的数据(即100万),

(3) 一次性删除100万条记录,insert_time不是索引,删除两天的数据(即100万),

无论(1)、(2)、(3),DELETE语句均执行了100万次,唯一的区别就是SELECT语句和执行的次数。

(4) 一次性删除1万条记录,insert_time是索引,删除两天的数据(即100万),

(5) 一次性删除5万条记录,insert_time是索引,删除两天的数据(即100万),

(6) 一次性删除100万条记录,insert_time是索引,删除两天的数据(即100万),

有索引和无索引相比,有一些需要维护索引的消耗。

clear_fetch存储过程各场景trace,

(SELECT和clear_all_fetch存储过程相近,此处忽略)

(1) 一次性删除1万条记录,insert_time不是索引,删除两天的数据(即100万),

(2) 一次性删除5万条记录,insert_time不是索引,删除两天的数据(即100万),

(3) 一次性删除100万条记录,insert_time不是索引,删除两天的数据(即100万),

(4) 一次性删除1万条记录,insert_time是索引,删除两天的数据(即100万),

(5) 一次性删除5万条记录,insert_time是索引,删除两天的数据(即100万),

(6) 一次性删除100万条记录,insert_time是索引,删除两天的数据(即100万),

可以看见clear_fetch和clear_all_fetch唯一区别就是DELETE语句执行次数,clear_fetch中执行次数和循环次数一样,说明是批量发送的,单条DELETE相同,但执行次数的不同,影响了资源消耗和执行时间。

从实验中可以得出的结论,

(1) SQL使用TABLE ACCESS FULL的执行计划,若SQL执行次数较多时,则BULK+forall的方式,效率较高;若SQL执行次数较少时,很可能使用TABLE ACCESS FULL的执行计划的SQL,效率和BULK+forall接近,甚至有更优的可能。

(2) SQL使用INDEX RANGE SCAN的执行计划,效率会比BULK+forall略高,若SQL执行次数较少时,使用INDEX RANGE SCAN的执行计划的SQL,效率较高;SQL执行次数对于BULK+forall的方式基本一致。

(3) 无论是否用索引,BULK+forall的方式均优于BULK+for。可以使用索引,则用游标和不用游标,效率比较接近,从实验上看,不用游标反而可能略高一些,这和使用游标需要一些解析类的消耗可能有关,但游标可以带来便捷性,比如方便控制结果集,可以更灵活地编辑逻辑,既然效率比较接近,若时间均是可接受范围内,可以根据实际来考虑,选择什么方式。无论什么方式,大表数据的批量删除,这是首要原则。

oracle三种删除的区别,三种批量删除PLSQL写法效率的比对相关推荐

  1. excel删除无尽空白行_Excel2019如何批量删除表格中的空白行?

    在使用Excel2019编辑文档的时候,在表格中有很多的空白行如何批量的把这些空白行给删除掉呢 Excel2019如何批量删除表格中的空白行? 首先在电脑上用Excel2019打开要编辑的表格,可以看 ...

  2. WordPress批量删除文章失效图片_批量删除文章404超链接教程

    WordPress批量删除文章失效图片_批量删除文章404超链接教程 当你的WordPress网站运营了一段时间,比如几年,网站很可能就会存在不少失效链接,文章中404超链接.批量删除文章中失效图片, ...

  3. 2023年最新qq空间说说怎么全部删除_QQ空间说说如何批量删除

    2023年最新QQ空间自动删除说说_2023批量删除QQ空间说说插件小工具_QQ空间如何一次性批量删除说说 一千多条说说怎么删?QQ空间说怎么批量删除_怎样把发的空间说说全删了 使用谷歌浏览器插件,一 ...

  4. 三种批量删除PLSQL写法效率的比对

    我们有一个重要的旧系统,最近夜维出现了一些问题,夜间执行5小时未完成,为了不影响业务,只能早上高峰期之前,DBA手工kill夜维进程. 这一个夜维程序采用了PLSQL写的存储过程,通过数据库job定时 ...

  5. php redis 删除key 通配符,php redis 批量删除keys的方法

    php redis批量删除keys的方法:首先打开命令窗口:然后通过命令"redis-cli keys video* | xargs redis-cli del"实现批量删除key ...

  6. php 批量删除redis缓存,php redis 批量删除keys的方法

    php redis批量删除keys的方法:首先打开命令窗口:然后通过命令"redis-cli keys video* | xargs redis-cli del"实现批量删除key ...

  7. qtableview删除选中行_如何批量删除PPT备注+如何修改模板信息

     今日更新主题   修改文件明显作者信息? 如何批量删除备注? 修改文件属性信息? 为什么模板有去不掉的背景图片? 1.修改文件作者信息? 写这篇文章的原因是之前身边有朋友参加比赛的时候,引用了某网站 ...

  8. ssm框架逻辑删除mysql_ssm框架小总结——批量删除

    jsp layui框架: //头工具栏事件 table.on('toolbar(test)', function(obj){ var checkStatus = table.checkStatus(o ...

  9. mysql批量删除数据sql语句_mysql批量删除数据sql语句详解

    1.like与 in批量删除指定记录 like语法 like主要是用来模糊查询了 sql = "delete from A where field like '%ABC%'" 这个 ...

最新文章

  1. Windows下查找端口被什么占用!
  2. vrep中remoteAPI 编程中遇到的没有预留命令执行时间遇到的问题
  3. MAC使用homeBrew安装Redis
  4. a标签无跳转的死链接
  5. 【redis】Redis中常用的五大数据类型的介绍以及代码的实现
  6. 跨境电商ERP有哪些?
  7. 进程和线程的区别【转】
  8. Java通过IP获取所在地
  9. ubuntu20.04 显卡驱动 cuda cudnn安装
  10. SCI写作Response to reviewers 范例
  11. 如何用QT做串口调试助手Qseriaport类的使用
  12. 织梦微信小程序一键生成插件(资讯案例模板),整合织梦CMS一切数据信息
  13. K8s Pod优雅关闭,没你想象的那么简单!
  14. 蘑菇街收购锐鲨科技,志在押注国货新浪潮?
  15. 图形学课设 塔防游戏
  16. mac电脑为什么设置了ssh还是提示Enter passphrase for /Users/mobvoi/.ssh/id_rsa:
  17. 理解资产定价领域的“均衡模型”:APT、CAPM、马歇尔、瓦尔拉斯、无套利均衡
  18. 2022-2028全球汽车塑料夹和紧固件行业调研及趋势分析报告
  19. 这项AR技术将你活生生地“解剖”给别人看
  20. 那些可以看做是沧海一粟的App

热门文章

  1. 智能可穿戴平台,你更看好谁?
  2. 指纹识别开发包 SourceAFIS
  3. 项目正式环境是双机,对外的访问地址是虚拟地址,在登录门户的时候,会等待15秒,才能进入门户...
  4. HeartBeat?你到底要干什么!
  5. 标准SQL的update语句三种用法
  6. webbrowser设置为相应的IE版本
  7. Web API 实现JSONP或者安装配置Cors跨域
  8. 设置Ext tab的宽度自动适应
  9. CentOS 7/8 安装 oniguruma 和 oniguruma-devel
  10. Linux 安装 luarocks(lua的模块管理工具)