oracle大數據量更新引發的死鎖問題解決方法及oracle分區和存儲過程的思考


前言

前幾天上午在對數據庫的一張表進行操作的時候,由於這張表是按照時間的一張統計表,正好到那天沒有測試數據了,於是我想將表中所有的時間,統一更新到后一個月,於是對80w條數據的更新開始了。整個過程曲折的一批。同時學到了很多知識,在此進行記錄。希望對大家有幫助。

首先是大批量更新,由於數據已經進行了分區,開始對分區進行分析,然后大批量操作死鎖,對死鎖的解決,最后存儲過程來解決數據的大批量插入。

曲折的過程開始

由於測試數據到21號就沒了,21號一上去,發現開發的功能,都沒有數據了,圖表也都空了。查詢原因發現測試數據沒了。於是打算開始造數據。此時數據庫已經有80多w的數據,當時想着將所有數據的collect_time時間字段向后推遲一個月,即可。當時也沒想優化問題。於是寫下sql。

update i_people_collect set collect_time = collect_time+30

此sql將表的所有時間向后推遲一個月。於是開始執行。

此時報錯:ORA-14402: 更新分區關鍵字列將導致分區的更改。

於是發現此表的collect_time列進行了分區處理。

我們可以先開啟表的行移動來允許對分區字段的update 操作。sql如下

alter table xxx enable row movement;

之后再執行update發現可以執行,執行完畢后,記得關閉行移動。

alter table xxx disable row movement;

回到剛才我們執行update語句,預計會慢,但是發現執行了20分鍾還沒有結束。於是懷疑報錯了。就強行終止。但是此時終止也不好使了。。大概是占用資源太多,不好釋放。

於是強行關掉pl/sql。重新登錄。這里我們先分析一下,執行update操作為什么會這么慢。

分區表某一行更新時,如果更新的是分區列,並且更新后的列值不屬於原來的這個分區,如果開啟了這個選項,就會把這行從這個分區中delete掉,並加到更 新后所屬的分區。相當於一個隱式的delete+insert,但是不會觸發insert/delete觸發器。如果沒有開啟這個選項,就會在更新時報錯 ORA-14402;

這一操作產生影響的特殊之處在於這是個DML操作,是和online transaction密切相關。對於這樣一個UPDATE,實際上分為3步:先從原有分區將數據刪除;將原數據轉移到新分區上;更新數據。

其影響就在於以下幾個方面:

一 個UPDATE被分解為DELET、INSERT、UPDATE三個操作,增加了性能負擔。其中,DELETE的查詢條件與原UPDATE的查詢條件相 同,新的UPDATE的查詢條件是基於INSERT生成的新的ROWID,相應的Redo Log、Undo Log會增加;

如果Update語句還涉及到了Local Index的字段的話,新、舊2個分區上的Local Index都要被更新。

由於我們更新的是collect_time列。collect_time列又正好是分區列。於是就產生了上面的這種情況。造成執行速度十分的緩慢。

原因分析完畢。繼續說接下來發生的問題。

重新連接到PL/Sql后,對剛才的表進行查詢,發現一直執行sql,並不返回結果。於是考慮剛才的sql還在執行的問題。

通過pl/sql的工具,會話,發現剛才的會話仍然存在,沒有斷開連接。這就坑爹了啊。通過會話來對連接強制結束。發現還是不能操作剛才的表。於是考慮了一下,可能是表發生了死鎖。

於是執行查詢哪些表產生了死鎖的sql,如下

select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object a,dba_objects b where b.object_id = a.object_id;

通過結果發現,剛才的表果然已經被鎖定了。

繼續向下看是哪個用戶的哪個進程造成的死鎖

--查看那個用戶那個進程照成死鎖 select b.username,b.sid,b.serial#,logon_time from v$locked_object a,v$session b where a.session_id = b.sid order by b.logon_time;
--查看連接的進程 SELECT sid, serial#, username, osuser FROM v$session;
--查出鎖定表的sid, serial#,os_user_name, machine_name, terminal,鎖的type,mode SELECT s.sid, s.serial#, s.username, s.schemaname, s.osuser, s.process, s.machine, s.terminal, s.logon_time, l.type FROM v$session s, v$lock l WHERE s.sid = l.sid AND s.username IS NOT NULL ORDER BY sid;

此時通過這些查詢,我們已經能夠定位是哪個進程導致了鎖表的產生。同時獲取到了進程的sid以及serial。

執行中斷進程的sql,

alter system kill session'210,11562';

講道理,此時已經進行了進程的結束,但是發現表還是在鎖着的。於是可能是查看一下造成死鎖的這一進程的狀態。

select saddr, sid, serial#, paddr, username, status, machine from v$session where username is not null

通過status發現鎖定的進程的狀態已經改變為KILLED,這種狀態可能導致長時間的未釋放資源,PMON並沒有對其進行清除,等了很久仍然是鎖表狀態。

於是可能需要操作系統級別的對進程進行清除。

我們查詢出會話進程在操作系統中的進程id。

select a.spid,b.sid,b.serial#,b.username,b.status from v$process a,v$session b where a.addr=b.paddr ; 

我們進入linux后台。通過kill -9 spid,此時執行后,發現表已經解鎖了,死鎖結束。呼~不容易。

接下來問題又來了,我們如何繼續更新數據呢。最終決定實用存儲過程來進行增加數據。

create or replace procedure aaa(startdate in date, days in number) as --生成的數據包含startdate當天 i number; begin i := 0; while i < days loop insert into aaa1 select sec_pkid.nextval,startdate + i, '字段名','字段名','字段名','字段名' from aaa2 t where collect_time = to_date('2018-11-09','yyyy-mm-dd'); i := i+1; commit; end loop; end aaa;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM