一条sql就能把oracle搞挂了,真的是不敢相信啊,前几天生产上确实出现了这样一个故障,我们来一起做一个事件回放。

系统介绍

系统架构见下图:

file

application1和application2是一个分布式系统中的2个应用,application1连接的数据库是database1,application2连接的数据库是database2,application2生产的数据要给application1做跑批使用。

application1要获取database2的数据,并不是通过接口来获取的,而是直连database2来获取,因此application1也具有database2库的读权限。

database2中有1张表table_b,里面保存的数据是application1跑批需要的数据。application1查找到table_b的数据后,先保存到database1的数据库表table_a中,等跑批时取出来用。

table_a和table_b的表结构如下:

file

2个表的主键都是字段a,application1查询出table_b的数据后,会根据主键a来判断这条数据是否存在,如果数据存在,就更新,否则,就插入。

application1使用的orm框架是mybatis,为了减少应用和数据库的交互,使用了oracle的merge语句。

注意:mybatis相关的文件有5个:

TableAMapper.java

TableBMapper.java

TableAMapper.xml

TableBMapper.xml

TableAEntity.java

熟悉mybatis的同学应该都知道,前两个java类是sql操作接口类,第3、4两个文件是存放sql的xml文件,跟前两个文件对应,最后一个java文件是do类。

事故现场

TableBMapper中有一个方法selectForPage,用来按页查询table_b中数据,每页1万条数据,之后把这个list结果merge到table_a,看一下代码:

//从table_b按每页1万条来查询数据

List list = tableBMapper.selectForPage(startPage, 10000);

//把查到的数据一次性merge到table_a中

tableAMapper.mergeFromTableB(list);

我们再看一下TableAMapper.xml中的mergeFromTableB方法,代码如下:

MERGE INTO table_a ta USING(select #{item.a} as a,#{item.b} as b,#{item.c} as c, #{item.d} as c from dual) tb

on (ta.a = tb.a)

WHEN MATCHED THEN UPDATE set

ta.b=tb.b,

ta.c=tb.c,

ta.d=tb.d

WHEN NOT MATCHED THEN insert(

a,

b,

c,

d

)

values (

tb.a,

tb.b,

tb.c,

tb.d

)

注意:为了文章排版,我对表结构做了简化,真实案例中table_a这张表有60多个字段。

这条sql执行后,我截取部分oracle的日志,如下:

file

图中可以看到oracle报了ORA-07445错误。

分析日志后发现,sql绑定变量达到了了79010个,而oracle是不允许超过65535个的。

解决方案

前面的分析确定了导致oracle挂掉的原因是绑定变量超过了65535个,那对症下药,解决的方案有3个:

业务系统方案

1.循环单条执行merge语句,优点是修改简单,缺点是业务系统跟数据库交互太多,会影响跑批任务执行效率。

2.对mergeFromTableB进行分批调用,比如每1000条调用一次merge方法,改造稍微多一点,但是交互会少很多。

DBA方案

给oracle打一个补丁,这个方案需要停服务。

业务方案2明细有优势,我用这个方案进行了改造,每次1000条,批量merge,代码如下:

for (int i = 0; i < list.size(); i += 1000) {

if (i + 1000 < list.size()) {

tableAMapper.mergeFromTableB(list.subList(i, i + 1000));

} else {

tableAMapper.mergeFromTableB(list.subList(i, list.size()));

}

}

新的问题

按照上面的方案改造完成后,数据库不会奔溃了,但是新的问题出现了。测试的同学发现,每次处理超过1000条数据,非常耗时,有时竟然达到了4分钟,惊呆。

看打印的批量sql,类似于下面的语句:

begin

merge into table_a ta USING(...;

merge into table_a ta USING(...;

end;

分析了一下,虽然放在了一个SQL块中,但还是单条执行,最后一起提交。

再做一次优化,把上面多条merge语句合成1条。

我的优化思路是创建一张临时表,先把list中的数据插入到临时表中,然后用一次merge把临时表的数据merge进table_a这张表。

oracle的临时表有2种,一种是会话级别,一种是事务级别:

会话级别的临时表,数据会在整个会话的生命周期中,会话结束,临时表数据清空;

事务级别的临时表,数据会在真个事务执行过程中,事务结束,临时表数据清空。

下面看具体实施过程。

1.我们创建一张会话临时表,SQL如下:

create global temporary table_a_temp on commit delete rows as select * from table_a;

comment on table_a_temp is 'table_a表临时表';

2.把table_b查询到的数据list插入临时表,需要在 TableAMapper.xml 增加一个方法:

insert all

into table_a_temp

a,

b,

c,

d,

#{item.a},

#{item.b,jdbcType=VARCHAR},

#{item.c,jdbcType=VARCHAR},

#{item.d,jdbcType=VARCHAR},

select 1 from dual

注意:oracle的insert all语句单次插入不能超过1000条。

3.把临时表的数据merge到table_a中,需要在 TableAMapper.xml 增加一个方法:

MERGE INTO table_a ta

USING (select * from table_a_temp) tb

on (ta.a = tb.a)

WHEN MATCHED THEN UPDATE set

ta.b = tb.b,

ta.c = tb.c,

ta.d = tb.d

WHEN NOT MATCHED THEN

insert

(a, b, c, d)

values

(tb.a, tb.b, tb.c, tb.d)

4.最终业务代码修改如下:

//从table_b查询

List list = tableBMapper.selectForPage(startPage, 10000);

//批量插入table_a_temp临时表

for (int i = 0; i < list.size(); i += 1000) {

if (i + 1000 < list.size()) {

tableAMapper.batchInsertTemp(list.subList(i, i + 1000));

} else {

tableAMapper.batchInsertTemp(list.subList(i, list.size()));

}

}

//从table_a_temp把数据merge到table_a

tableAMapper.mergeFromTempData();

总结

在oracle上执行SQL时,如果绑定变量的数量超过了65535,会引发ORA-07445。当然,引发ORA-07445的原因还有其他。

解决这个问题最好的方式是从业务代码层面进行修改。

也可以让DBA可以给oracle打一个补丁,但是oracle必须要停服务。

oracle挂证多少钱一个月_惊呆,一条sql竟然把Oracle搞挂了相关推荐

  1. 亚马逊的vps多少钱一个月_如何查看您在亚马逊上花了多少钱

    亚马逊的vps多少钱一个月 Have you ever wondered how much you've spent at Amazon during your lifetime? Whether y ...

  2. 更改ip地址的软件多少钱一个月_武汉社保代缴多少钱一个月?武汉社保一个月交多少钱?...

    对于相当一部分职场人群而言,有了社保代缴的参保方式更加可以保障自己的社保账户连续性.但是在代缴的过程中,大家往往问题很多,想了解代缴的参保形式.了解多少钱.了解代缴后的重新入职问题,结合此前网友的提问 ...

  3. 惊呆,一条sql竟然让oracle奔溃了

    一条sql就能让oracle挂了,真的是不敢相信啊,前几天生产上确实出现了这样一个故障,我们来一起做一个事件回放. 系统介绍 系统架构见下图: application1和application2是一个 ...

  4. 萍乡电子工程师多少钱一个月_电子工程师月薪是多少?工资待遇及前景怎么样...

    原标题:电子工程师月薪是多少?工资待遇及前景怎么样 又到一年一度的毕业季,很多大学生都开始忙着找工作了.还有很多同学不太了解电子工程师月薪是多少?工资待遇及前景怎么样?下面有途网小编就详细和同学说说. ...

  5. 萍乡电子工程师多少钱一个月_电子工程师工资是多少

    电子工程师 电子工程师指从事电子设备和信息系统研究.教学.产品设计.科技开发.生产和管理等工作的高级工程技术人才.电子工程师一般分为硬件工程师和软件工程师.硬件与软件是不可分离的,硬件需要软件来执行其 ...

  6. 网页设计工资一般多少_理发师工资一般多少钱一个月

    现在社会物价上涨,就连理发的价格也上涨了,但是虽然价格上涨了,质量却未必保证了.理发师在现在生活中可是少不了的,尤其是对女性朋友来说,发型设计可是至关重要的,因此虽然理发的价格上涨,但是依旧阻止不了女 ...

  7. 程序员工资一般多少钱一个月?【推荐】

    其他行业没这么麻烦,一个职位总是有很多人能胜任的,选择多了,公司就有条件把工资降低.但在程序员招聘方面,谈工资的权利在程序员手里,因为对方没有选择.程序员本来就那么稀有,非常适合要求的程序员更是稀有之 ...

  8. 迅雷超级会员和白金会员怎么买最便宜多少钱一年多少钱一个月

    很多同学因为下载资讯需要开通迅雷超级会员或白金会员,但是不知道怎么购买最便宜,也不知道多少钱一年,多少钱一个月购买比较划算.下面就和大家分享迅雷会员怎么买最便宜划算. 迅雷是一款非常不错的下载软件,我 ...

  9. web前端工资一般多少?在北京前端工程师多少钱一个月?

    前端工程师工资多少钱一个月?好程序员告诉大家:平均月薪20K以上,大厂年薪30万起步.web前端工程师在北上广深有巨大的就业需求,所以很多小伙伴选择了学习前端.那么web前端工程师工资一般多少钱呢? ...

最新文章

  1. oracle存储过程的常用语法
  2. 第十七届全国大学生智能车竞赛完全模型组 I 型车模数据
  3. Linux下的静态库、动态库和动态加载库
  4. 我现在的简历以及后面的更改方向
  5. 【归并排序】奶牛的图片(jzoj 1812)
  6. TrueCommand是什么
  7. 1.8 centos7网络排错
  8. Oracle数据导入导出imp/exp sp2-0734:未知的命令开头'imp...解决方法
  9. 使用curl与wget发送get与post请求
  10. Linux下载Java包,Linux环境Java包的安装和环境配置
  11. 全国大学生数学竞赛学习笔记
  12. c++编译时出现discards qualifiers [-fpermissive]的错误
  13. unity PC 、安卓修改数据库文件
  14. Word查找和替换通配符(完全版)
  15. 一款游戏让你成为 Vim 高手!
  16. JBPM4.4业务流程管理框架详细解读
  17. 夜光 :AGV 导航策略总体方案设计
  18. 典型相关分析(Canonical Correlation Analysis, CCA)
  19. windows 键盘记录器(win10下测试成功)
  20. java分布式事务——seata,tcc解决方案总结!

热门文章

  1. 从ASP.NET Core2.2到3.0你可能会遇到这些问题
  2. 使用.NET Core与Google Optimization Tools实现加工车间任务规划
  3. FinTech浪潮已到,五大金融场景将迎变革
  4. 帅呆了!微软即将发布 Visual Studio for Mac 预览版
  5. .NET Core性能测试组件BenchmarkDotNet 支持.NET Framework Mono
  6. 理解 .NET Platform Standard
  7. 在Linux以及Mac OS X启用F#
  8. 云计算产值将超3000亿美元 亚马逊微软谷歌居三甲
  9. 我的未来计算机作文,我的未来作文(精选4篇)
  10. jQuery选择器和选取方法