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;
更多Oracle相關資訊見 Oracle 專題頁面 https://www.linuxidc.com/topicnews.aspx?tid=12
Linux公社的RSS地址 :https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址:https://www.linuxidc.com/Linux/2019-01/156490.htm