之前有些 daily job 跑得比較久的時候,偶爾都會來一下 ORA-01555: snapshot too old 的錯誤。筆記一下 root-cause 及個人心得~~
Oracle 有一套叫 MVCC (Multi-Version Concurrency Control,多版本併發控制) 的機制。主要是用來讓「讀取」與「寫入」二個在同時操作時,不會因為時間差互相堵住,然後又能正常完成作業。
舉例來說,當我們的 session 在執行一個 SELECT 查詢動作時,Oracle 會記錄當時這個時間點的 SCN (System Change Number),簡單想像就是一個「版本號」的概念。
這個「版本號」讓我們的查詢在掃描 data block 時,oracle 可以用來比對我們身上的「版本號」跟 data block 身上的「版本號」有沒有差異。
如果一樣的時候,代表這個資料在我們查詢期間沒被動過,可以直接使用。
但如果 data block 較新的時候,代表有被異動過。這時候 data block 一樣會先被讀取出來,接著去 undo tablespace 裡找這二個版本號期間的 undo record(很像操作 log 一樣),然後把 最新的 data block 慢慢的還原到跟我們身上「版本號」時間點相同的資料內容。
而 Snapshot Too Old 就是會在這時候發生。因為 undo tablespace 的空間一定是有限的,所以放在裡面的資料如果 expire 時間還沒到,然後空間已經滿了卻又要再寫入新的 undo record 時,預設會把比較舊但沒 expire 的資料踢掉,然後把空間拿來用。
然後當我們的查詢試著要去抓這些被踢掉的 undo record (因為要把 data block 還原成我們身上的「版本號﹁內容),由於已經不在了,這時就會中 ORA-01555: snapshot too old 錯誤。
基本的起手式確實都可以先分析 long query 的 sql。但第二個方向可以看一下這堆在條件內的資料,為什麼會在查詢時被異動(雖然 undo tablespace 不夠有時是因為別的資料異動),或是這些要被查詢的資料可以等「拍板定案」,也就是不會再變的時候再去抓,是否可行?
或著是抓資料的時候可以選在離峰,也就是較沒有 transaction or job 的時間。
試著從使用資料的角度去切入,也許也能閃掉這些不必要的雷。