中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

總結一條SQL竟然讓Oracle奔潰了

發布時間:2021-10-22 09:29:58 來源:億速云 閱讀:196 作者:iii 欄目:數據庫

本篇內容介紹了“總結一條SQL竟然讓Oracle奔潰了”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

系統介紹

系統架構見下圖:

總結一條SQL竟然讓Oracle奔潰了

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的表結構如下:

總結一條SQL竟然讓Oracle奔潰了

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<TableAEntity> list = tableBMapper.selectForPage(startPage, 10000); //把查到的數據一次性merge到table_a中 tableAMapper.mergeFromTableB(list);

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

<update id="mergeFromTableB" parameterType="list">   <foreach collection="list" item="item" index="index" separator=";" close=";end;" open="begin">       MERGE INTO table_a ta USING(select #{item.a} as a,#{item.b} as b,#{item.c} as c, #{item.d} as d 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       )     </foreach> </update>

注意:為了文章排版,我對表結構做了簡化,真實案例中table_a這張表有60多個字段。

這條sql執行后,我截取部分oracle的日志,如下:

總結一條SQL竟然讓Oracle奔潰了

圖中可以看到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.會話級別的臨時表,數據會在整個會話的生命周期中,會話結束,臨時表數據清空;

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 id="batchInsertTemp" parameterType="list">   insert all   <foreach collection="list" index="index" item="item">     into table_a_temp     <trim prefix="(" suffix=")" suffixOverrides="," >       a,       <if test="item.b != null" >         b,       </if>       <if test="item.c != null" >         c,       </if>       <if test="item.d != null" >         d,       </if>     </trim>     <trim prefix="values (" suffix=")" suffixOverrides="," >       #{item.a},       <if test="item.b != null" >         #{item.b,jdbcType=VARCHAR},       </if>       <if test="item.c != null" >         #{item.c,jdbcType=VARCHAR},       </if>       <if test="item.d != null" >         #{item.d,jdbcType=VARCHAR},       </if>     </trim>   </foreach>   select 1 from dual </insert>

注意:oracle的insert all語句單次插入不能超過1000條。

3.把臨時表的數據merge到table_a中,需要在 TableAMapper.xml 增加一個方法:

<update id="mergeFromTempData">   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) </update>

4.最終業務代碼修改如下:

//從table_b查詢 List<TableAEntity> 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必須要停服務。

“總結一條SQL竟然讓Oracle奔潰了”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

龙山县| 泰州市| 农安县| 弥渡县| 玉林市| 汉沽区| 广安市| 教育| 郯城县| 阿瓦提县| 敦煌市| 辽阳市| 商丘市| 来凤县| 新邵县| 漳州市| 汝南县| 全椒县| 团风县| 鹤峰县| 肥西县| 车险| 洛南县| 昌平区| 西青区| 白水县| 兴义市| 莒南县| 银川市| 东安县| 罗源县| 麻栗坡县| 腾冲县| 通许县| 邮箱| 永定县| 南木林县| 太仆寺旗| 眉山市| 湟源县| 永吉县|