Oracle高人寫的Oracle執行原理性文章(zt)
我們從一個使用者請求開始講,ORACLE的完整的工作機制是怎樣的,首先一個使用者程式發出一個連線請求,如果使用的是主機命名或者是本地服務命中的主機名使用的是機器名(非IP地址),那麼這個請求都會透過DNS伺服器或HOST檔案的服務名解析然後傳送到ORACLE監聽程式,監聽程式接收到使用者請求後會採取兩種方式來處理這個使用者請求,下面我們分專用伺服器和共享伺服器分別採用這兩種方式時的情況來講:
專用伺服器模式下:一種方式是監聽程式接收到使用者程式請求後,產生一個新的專用伺服器程式,並且將對使用者程式的所有控制資訊傳給此伺服器程式,也就是說新建的伺服器程式繼承了監聽程式的資訊,然後伺服器程式給使用者程式發一個RESEND包,通知使用者程式可以開始給它發資訊了,使用者程式給這個新建的伺服器程式發一個CONNECT包,伺服器程式再以ACCEPT包回應使用者程式,致此,使用者程式正式與伺服器程式確定連線。我們把這種連線叫做HAND-OFF連線,也叫轉換連線。另一種方式是監聽程式接收到使用者程式的請求後產生一個新的專用伺服器程式,這個伺服器程式選用一個TCP/IP埠來控制與使用者程式的互動,然後將此資訊回傳給監聽程式,監聽程式再將此資訊傳給使用者程式,使用者程式使用這個埠給伺服器程式傳送一個CONNECT包,伺服器程式再給使用者程式傳送一個ACCEPT包,致此,使用者程式可以正式向伺服器程式傳送資訊了。這種方式我們叫做重定向連線。HAND-OFF連線需要系統平臺具有程式繼承的能力,為了使WINDOWS NT/2000支援HAND-OFF必須在HKEY_LOCAL_MACHINE>SOFTWARE>ORACLE>HOMEX中設定USE_SHARED_SOCKET。 共享伺服器模式下:只有重定向連線的方式,工作方式是監聽程式接收到使用者程式的請求後產生一個新的排程程式,這個排程程式選用一個TCP/IP埠來控制與使用者程式的互動,然後將此資訊回傳給監聽程式,監聽程式再將此資訊傳給使用者程式,使用者程式使用這個埠給排程程式傳送一個CONNECT包,排程程式再給使用者程式傳送一個ACCEPT包,致此,使用者程式可以正式向排程程式傳送資訊了。可以透過設定MAX_DISPIATCHERS這個引數來確定排程程式的最大數目,如果排程程式的個數已經達到了最大,或者已有的排程程式不是滿負荷,監聽程式將不再建立新的排程程式,而是讓其中一個排程程式選用一個TCP/IP埠來與此使用者程式互動。排程程式每接收一個使用者程式請求都會在監聽程式處作一個登記,以便監聽程式能夠均衡每個排程程式的負荷,所有的使用者程式請求將分別在有限的排程程式中排隊,所有排程程式再順序的把各自佇列中的部分使用者程式請求放入同一個請求佇列,等候多個ORACLE的共享伺服器程式進行處理(可以透過SHARED_SERVERS引數設定共享伺服器程式的個數),也就是說所有的排程程式共享同一個請求佇列,共享伺服器模式下一個例項只有一個請求佇列,共享伺服器程式處理完使用者程式的請求後將根據使用者程式請求取自不同的排程程式將返回結果放入不同的響應佇列,也就是說有多少排程程式就有多少響應佇列,然後各個排程程式從各自的響應佇列中將結果取出再返回給使用者程式。 以上我們講完了使用者與ORACLE的連線方式,下面我們要講ORACLE伺服器程式如可處理使用者程式的請求,當一個使用者程式發出了一條SQL語名:UPDATE TABBLEA SET SALARY=SALARY*2;首先,伺服器程式把這條語句的字元轉換成ASCII等效數字碼,接著這個ASCII碼被傳遞給一個HASH函式,並返回一個HASH值,伺服器程式將到SHARED POOL 的共享PL/SQL區去查詢是否存在同樣的HASH值,如果存在,伺服器程式將使用這條語句已快取記憶體在SHARED POOL中的已分析過的版本來執行,如果不存在,伺服器程式將對該語句進行語法分析,首先檢查該語句的語法的正確性,接著對語句中涉及的表、索引、檢視等物件進行解析,並對照資料字典檢查這些物件的名稱以及相關結構,並根據ORACLE選用的最佳化模式以及資料字典中是否存在相應物件的統計資料和是否使用了儲存大綱來生成一個執行計劃或從儲存大綱中選用一個執行計劃,然後再用資料字典核對此使用者對相應物件的執行許可權,最後生成一個編譯程式碼。ORACLE將這條語名的本身實際文字、HASH值、編譯程式碼、與此語名相關聯的任何統計資料和該語句的執行計劃快取在SHARED POOL的共享PL/SQL區。伺服器程式透過SHARED POOL 鎖存器來申請可以向哪些共享PL/SQL區中快取這此內容,也就是說被SHARED POOL鎖存器鎖定的PL/SQL區中的塊不可被覆蓋,因為這些塊可能被其它程式所使用。在SQL分析階段將用到LIBRARY CACHE,從資料字典中核對錶、檢視等結構的時候,需要將資料字典從磁碟讀入LIBRARY CACHE,因此,在讀入之前也要使用LIBRARY CACHE鎖存器來申請用於快取資料字典。 生成編譯程式碼之後,接著下一步伺服器程式要準備開始更新資料,伺服器程式將到DB BUFFER中查詢是否有相關物件的快取資料,下面分兩個可能進行解釋: 如果沒有,伺服器程式將在表頭部請求一些行鎖,如果成功加鎖,伺服器程式將從資料檔案中讀這些行所在的資料塊放入DB BUFFER中空閒的區域或者覆蓋已被擠出LRU列表的非髒資料塊緩衝區,並且排列在LRU列表的頭部,如果這些非髒資料緩衝區寫完也不能滿足新資料的請求時,會立即觸發DBWN程式將髒資料列表中指向的緩衝塊寫入資料檔案,並且清洗掉這些緩衝區,來騰出空間緩衝新讀入的資料,也就是在放入DB BUFFER之前也是要先申請DB BUFFER中的鎖存器,成功鎖定後,再寫入DB BUFFER,然後伺服器程將該語句影響的被讀入DB BUFFER塊中的這些行的ROWID及將要更新的原值和新值及SCN等資訊逐條的寫入REDO LOG BUFFER,在寫入REDO LOG BUFFER之前也是先請求REDO LOG BUFFER塊的鎖存器,成功鎖定之後才開始寫入,當寫入達到REDO LOG BUFFER大小的三分之一或寫入量達到1M或超過三秒後或發生檢查點時或者DBWN之前發生,LGWR將把REDO LOG BUFFER中的資料寫入磁碟上的重做日誌檔案,已被寫入重做日誌檔案的REDO LOG BUFFER中的塊上的鎖存器被釋放,並可被後來寫入的資訊所覆蓋,REDO LOG BUFFER以迴圈的方式工作。當一個重做日誌檔案寫滿後,LGWR將切換到下一個重做日誌檔案,如果是歸檔模式,歸檔程式還將前一個寫滿的重做日誌程式寫入歸檔日誌檔案,重做日誌檔案也是迴圈工作方式。寫完所有的REDO LOG BUFFER之後,伺服器程式開始改寫這個DB BUFFER塊頭部的事務列表並寫入SCN,然後COPY包含這個塊的頭部事務列表及SCN資訊的資料副本放入回滾段中,我們將回滾段中的副本稱為資料塊的“前映像”。(回滾段可以儲存在專門的回滾表空間中,這個表空間由一個或多個物理檔案組成,並專用於回滾表空間,回滾段也可在其它表空間中的資料檔案中開闢。)然後改寫這個DB BUFFER塊的資料,並在其頭部寫入對應的回滾段地址,如果對一行資料多次UPDATE而不COMMIT則在回滾段中將會有多個“前映像”,除第一個“前映像”含有SCN資訊外,其它的每個“前映像”的頭部還含有SCN資訊和“前前映像”的回滾段地址。一次UPDATE操作只對應一個SCN。然後伺服器程式在髒資料列表中建立一條指向此緩衝塊的指標。接著伺服器程式會從資料檔案讀入第二個塊重複以上讀入,記日誌,建立回滾段,修改,放入髒列表的動作,當髒資料列表達到一定長度時,DBWN程式將髒資料列表中指向的緩衝塊全部寫入資料檔案,也就是釋放加在這些DB BUFER 塊上的鎖存器。其實ORACLE可以一次從資料檔案中讀入幾個塊放入DB BUFFER,可以透過引數DB_FILE_MULTIBLOCK_READ_COUNT來設定一次讀入的塊的個數。 如果要查詢的資料已快取,則根據使用者的SQL操作型別決定如何操作,如果是SELECT 則檢視DB BUFFER塊的頭部是否有事務,如果有,將從回滾段讀取,如果沒有則比較SELECT 的SCN與DB BUFFER塊頭部的SCN如果比自己大,仍然從回滾段讀取,如果比自己小則認這是一個非髒快取,可以直接從這個DB BUFFER塊中讀取。如果是UPDATE則即使在DB BUFFER中找到一個沒有事務,而且SCN比自己小的非髒快取資料塊,伺服器程式仍然要到表的頭部對這條記錄申請加鎖,加鎖成功則進行後續動作,如果不成功,則要等待前面的程式解鎖後才能進行動作。 只有當SQL語句影響的所有行所在的最後一個塊被讀入DB BUFFER並且重做資訊被寫入REDO LOG BUFFER(僅是指重做日誌緩衝,而非重做日誌檔案)之後,使用者才可以發出COMMIT,COMMIT觸發LGRW,但並不強制立即DBWN來釋放所有相應的DB BUFFER塊上的鎖,也就是說有可能出現已COMMIT,但在隨後的一段時間內DBWN還在寫這條語句涉及的資料塊的情形,表頭部的行鎖,並不是在COMMIT一發出就馬上釋放,實際上要等到相應的DBWN程式結束才會釋放。一個使用者請求鎖定另一個使用者已COMMIT的資源不成功的機會是存在的,從COMMIT到DBWN程式結束之間的時間很短,如果恰巧在這個時間斷電,由於COMMIT已觸發LGWR程式,所以這些未來得及寫入資料檔案的改變會在例項重啟後由SMON程式根據重做日誌檔案來前滾。如果未COMMIT就斷電,由於DBWN之前觸發LGWR,所有DBWN在資料檔案上的修改都會被先一步記入重做日誌檔案,例項重啟後,SMON程式再根據重做日誌檔案來回滾。 如果使用者ROOLBACK,則伺服器程式會根據資料檔案塊和DB BUFFER中塊的頭部的事務列表和SCN以及回滾段地址找到回滾段中相應的修改前的副本,並且用這些原值來還原當前資料檔案中已修改但未提交的改變。如果有多個“前映像”,伺服器程式會在一個“前映像”的頭部找到“前前映像”的回滾段地址,一直找到同一事務下的最早的一個“前映像”為止。一旦發出了COMMIT,使用者就不能ROOLBACK,這使得COMMIT後DBWN程式還沒有全部完成的後續動作得到了保障。 下面我們要提到檢查點的作用,當一個全部檢查點發生的時候,首先讓LGWR程式將REDO LOG BUFFER中的所有緩衝(包含未提交的重做資訊)寫入重做日誌檔案,然後讓DBWN程式將DB BUFFER中所有已提交的緩衝寫入資料檔案(不強制寫未提交的)。然後更新控制檔案和資料檔案頭部的SCN,表明當前資料庫是一致的,如果在發生檢點之前斷電,並且當時有一個未提交的改變正在進行,例項重啟之後,SMON程式將從上一個檢查點開始核對這個檢查點之後記錄在重做日誌檔案中已提交的和未提交改變,因為DBWN之前會觸發LGWR,所以DBWN對資料檔案的修改一定會被先記錄在重做日誌檔案中。因此,斷電前被DBWN寫進資料檔案的改變將透過重做日誌檔案中的記錄進行還原,叫做回滾,如果斷電時有一個已提交,但DBWN動作還沒有完全完成的改變存在,因為已經提交,提交會觸發LGWR程式,所以不管DBWN動作是否已完成,該語句將要影響的行及其產生的結果一定已經記錄在重做日誌檔案中了,則例項重啟後,SMON程式根據重做日誌檔案進行前滾。由此可見,例項失敗後用於恢復的時間由兩個檢查點之間的間隔大小來決定,我們可以通個四個引數設定檢查點執行的頻率,LOG_CHECKPOINT_IMTERVAL決定了兩個檢查點之間寫入重做日誌檔案的系統物理塊的大小,LOG_CHECKPOINT_TIMEOUT決定了兩個檢查點之間的時間長度,FAST_START_IO_TARGET決定了用於恢復時需要處理的塊的大小,FAST_START_MTTR_TARGET直接決定了用於恢復的時間的長短。SMON程式執行的前滾和回滾與使用者的回滾是不同的,SMON是根據重做日誌檔案進行前滾或回滾,而使用者的回滾一定是根據回滾段的內容進行回滾的。在這裡我們要說一下回滾段儲存的資料,假如是delete操作,則回滾段將會記錄整個行的資料,假如是update,則回滾段只記錄被修改了的欄位的變化前的資料(前映像),也就是沒有被修改的欄位是不會被記錄的,假如是insert,則回滾段只記錄插入記錄的rowid。這樣假如事務提交,那回滾段中簡單標記該事務已經提交;假如是回退,則如果操作是是delete,回退的時候把回滾段中資料重新寫回資料塊,操作如果是update,則把變化前資料修改回去,操作如果是insert,則根據記錄的rowid 把該記錄刪除。 下面我們要講DBWN如何來寫資料檔案,在寫資料檔案前首先要找到可寫的空閒資料塊,ORACLE中空閒資料塊可以透過FREELIST或BITMAP來維護,它們位於一個段的頭部用來標識當前段中哪些資料塊可以進行INSERT。在本地管理表空間中ORACLE自動管理分配給段的區的大小,區的分配資訊儲存在組成表空間的資料檔案的頭部,而資料字典管理的表空間使用者可以在建立時決定區的大小,並且區的分配資訊是儲存在資料字典中的,只在本地管理的表空間中才能選用段自動管理,採用自動段空間管理的本地管理表空間中的段中的空閒資料塊的資訊就存放在段的頭部並且使用點陣圖來管理,採用手動管理的本地管理表空間中的段和資料字典管理的表空間中的段中的空閒資料塊的管理都使用位於段頭部的空閒列表來管理,空閒列表的工作方式:首先一個空的資料塊被加入空閒列表,當其中空閒空間小於PCTFREE設定的值之後,這個塊從空閒列表刪除,當這個塊中的內容降至PCTUSED設定的值之下後,這個資料塊被再次加入空閒列表,位於空閒列表中的資料塊都是可以向其中INSERT的塊,當一個塊移出了空閒列表,但只要其中還有保留空間就可以進行UPDATE,當對其中一行UPDATE一個大資料時,如果當前塊不能完全放下整個行,只會把整個行遷移到一個新的資料塊,並在原塊位置留下一個指向新塊的指標,這叫行遷移。如果一個資料塊可以INSERT,當插入一個當前塊裝不下的行時,這個行會溢位到兩個或兩個几上的塊中,這叫行連結。如果使用者的動作是INSERT 則伺服器程式會先鎖定FREELIST,然後找到空閒塊的地址,再釋放FREELIST,當多個伺服器程式同時想要鎖定FREELIST時即發生FREELIST的爭用,可以在非採用自動段空間管理的表空間中建立表時指定FREELIST的個數,預設為1,如果是在採用自動段空間管理的表空間中建立表,即使指定了FREELIST也會被忽略,因為此時將使用BITMAP而不是FREELIST來管理段中的空閒空間。如果使用者動作是UPDATE伺服器程式將不會使用到FREELIST和BITMAP,因為不要去尋找一個空閒塊,而使用鎖的佇列。 下面來講一下ORACLE鎖的機制,ORACLE分鎖存器和鎖兩種。鎖存器是用來保護對記憶體結構的訪問,比如對DB BUFFER中塊的鎖存器申請,只有在DBWN完成後,這些DB BUFFER塊被解鎖。然後用於其它的申請。鎖存器不可以在程式間共享,鎖存器的申請要麼成功要麼失敗,沒有鎖存器申請佇列。主要的鎖存器有SHARED POOL鎖存器,LIBRARY CACHE鎖存器,CACHE BUFFERS LRU CHAIN鎖存器,CACHE BUFFERS CHAINS 鎖存器,REDO ALLOCATION 鎖存器,REDO COPY 鎖存器。ORACLE的鎖是用來保護資料訪問的,鎖的限制比鎖存器要更寬鬆,比如,多個使用者在修改同一表的不同行時,可以共享一個表上的一個鎖,鎖的申請可以按照被申請的順序來排隊等候,然後依次應用,這種排隊機制叫做佇列(ENPUEUE),如果兩個伺服器程式試圖對同一表的同一行進行加鎖,則都進入鎖的申請佇列,先進的加鎖成功,後面的程式要等待,直到前一個程式解鎖才可以加鎖,這叫做鎖的爭用,而且一旦加鎖成功,這個鎖將一直保持到使用者發出COMMIT或ROOLBACK命令為止。如果兩個使用者鎖定各自的一行並請求對方鎖定的行的時候將發生無限期等待即死鎖,死鎖的發生都是由於鎖的爭用而不是鎖存器的爭用引起的,ORACLE在遇到死鎖時,自動釋放其中一個使用者的鎖並回滾此使用者的改變。正常情況下發生鎖的爭用時,資料的最終儲存結果由SCN來決定哪個程式的更改被最終儲存。兩個使用者的伺服器程式在申請同一表的多個行的鎖的時候是可以交錯進入鎖的申請佇列的。只有其中發生爭用才會進行等待。建立表時指定的MAXTRANS引數決了,表中的一個資料塊最多可以被幾個事務同時鎖定。 下面是幾個關於回滾段和死鎖的事例: 有表:Test (id number(10)) 有記錄1000000條 一,大SELECT,小UPDATE A會話----Select * from test;----設scn=101----執行時間09:10:11 B會話-----Update test set id=9999999 where id=1000000----設scn=102-----執行時間09:10:12 我們會發現B會話會在A會話前完成,A會話中顯示的ID=100000是從回滾段中讀取的,因為A會話在讀到ID=1000000所在的BLOCK時發現BLOCK上有事務資訊,因此要從回滾段中讀,如果UPDATE在SELECT讀到此BLOCK之前已經COMMIT,則SELECT 讀到此BLOCK時發現其BLOCK上沒有事務資訊,但是會發現其BLICK的SCN比SELECT自己的SCN大,因此也會從回滾段中讀取。因此是否從回滾段讀一是看是否有事務資訊二是比較SCN大小。如果B會話在A會話結束前連續多次對同一條記錄UPDATE並COMMIT ,那麼在回滾段中將記錄多個“前映像”,而每個“前映像”中不但包括了原BLOCK的資料和SCN也記錄了“前前映像”的回滾段地址,因此A會話在查詢到被UPDATE過的BLOCK時,會根據BLOCK記錄的回滾段的地址,找到回滾段中的“前映像”,發現這個“前映像”的SCN也比自己的大,因此將根據這個“前映像”中記錄的“前前映像”的回滾段地址,在回滾段中找到“前前映像”,再與這個“前前映像”比較SCN,如果比自己小就讀取,如果還比自己大,則重複以上步驟,直到找到比自己SCN小的“前…前映像”為止,如果找不到,就會報ORA-01555快照太舊這個錯誤。 二、大UPDATE,小SELECT A會話----Update test set id=1;----設scn=101----執行時間09:10:11 B會話-----select * from test where id=1000000----設scn=102-----執行時間09:10:12 我們會發現B會話會在A會話前完成,B會話中顯示的ID=1000000是從BLOCK中直接讀取的,因為B會話在讀到ID=1000000所在的BLOCK時,A會話還沒有來得及對其鎖定,因此B會話既不會發現BLOCK上有事務資訊,也不會發現BLOCK上的SCN比SELECT的大,因此會從BLOCK中直接讀取,如果SELECT在UPDATE鎖定此BLOCK後才發出,B會話讀到此BLOCK時發現其BLOCK上有事務資訊,因此會從回滾段中讀取。 三、大UPDATE,小UPDATE A會話----Update test set id=1;----設scn=101----執行時間09:10:11 B會話1-----Update test set id=999999 where id=1000000----設scn=102-----執行時間09:10:12 B會話2----- select * from test where id=2----設scn=103-----執行時間09:10:14 B會話3----- update test set id=3 where id=2----設scn=104-----執行時間09:10:15 我們會發現B會話1會完成,A會話將一直等待,因為B會話1會先於A會話鎖定ID=1000000所在的BLOCK,並改寫頭部的事務資訊,A會話在試圖鎖定此BLOCK時,發現其上有事務資訊,將會一直等待B會話1事務結束後再行鎖定, B會話2查詢到的ID=2是從回滾段中讀取的而不是從BLOCK中直接讀出來的。因為A會話已將ID=2的BLOCK鎖定,並寫入了回滾段,從B會話3可以證明這一點,B會話3發出後,B會話3會收到死鎖的資訊,死鎖的原因是A會話在等待B會話對ID=1000000所在的BLOCK解鎖,現在B會話又在等待A會話對ID=2所在的BLOCK解鎖,因此形成死鎖,因此證明ID=2所在的BLOCK已被A會話鎖定,然後A會話也會收到死鎖的資訊 分類: () :: 評論 (5) :: 靜態連結網址 :: 引用 (0) 很不錯! [回覆] 這篇文章確實很不錯,不知道作者是哪位?我找了下,老兄是不是從這裡轉過來的:?作者對ORACLE的概念理解得非常清晰。 blue_prince | 28/06/2005, 13:17 呵呵 [回覆] 忘了從哪裡轉來的了 yaanzy | 01/07/2005, 10:28 我來挑點刺哈 [回覆] 不得不承認,作者對oracle的理解是深刻的,也是頗費了工夫去思考的。只是可能由於缺乏和人的共同探討 或者 碰巧有點小錯誤引出一些推導錯誤。 如果要查詢的資料已快取,則根據使用者的SQL操作型別決定如何操作,如果是SELECT 則檢視DB BUFFER塊的頭部是否有事務,如果有,將從回滾段讀取,如果沒有則比較SELECT 的SCN與DB BUFFER塊頭部的SCN如果比自己大,仍然從回滾段讀取,如果比自己小則認這是一個非髒快取,可以直接從這個DB BUFFER塊中讀取。如果是UPDATE則即使在DB BUFFER中找到一個沒有事務,而且SCN比自己小的非髒快取資料塊,伺服器程式仍然要到表的頭部對這條記錄申請加鎖,加鎖成功則進行後續動作,如果不成功,則要等待前面的程式解鎖後才能進行動作。 只有當SQL語句影響的所有行所在的最後一個塊被讀入DB BUFFER並且重做資訊被寫入REDO LOG BUFFER(僅是指重做日誌緩衝,而非重做日誌檔案)之後,使用者才可以發出COMMIT,COMMIT觸發LGRW,但並不強制立即DBWN來釋放所有相應的DB BUFFER塊上的鎖,也就是說有可能出現已COMMIT,但在隨後的一段時間內DBWN還在寫這條語句涉及的資料塊的情形,表頭部的行鎖,並不是在COMMIT一發出就馬上釋放,實際上要等到相應的DBWN程式結束才會釋放。 由上面兩段話可以看出點 瑕疵,作者對 block cleanout 概念不瞭解,所以在這兩個地方描述有遺漏,第二段更是因為這個概念不清楚而引出一些猜測 :) 下面我們要提到檢查點的作用,當一個全部檢查點發生的時候,首先讓LGWR程式將REDO LOG BUFFER中的所有緩衝(包含未提交的重做資訊)寫入重做日誌檔案,然後讓DBWN程式將DB BUFFER中所有已提交的緩衝寫入資料檔案(不強制寫未提交的)。 由上面這段話可以看出多檢查點理解有誤,dbwr寫的時候不會管是否提交的,由此也許可以理解為是對 恢復 的過程理解不是很深刻,但是也可以和 block cleanout 套上關係,因為參考更前面兩段話,作者一直以為是dbwr 來處理 block cleanout的。 下面我們要講DBWN如何來寫資料檔案,在寫資料檔案前首先要找到可寫的空閒資料塊,ORACLE中空閒資料塊可以透過FREELIST或BITMAP來維護,它們位於一個段的頭部用來標識當前段中哪些資料塊可以進行INSERT 由這段話也有錯誤,這不是寫資料檔案,這是在記憶體中寫資料,讀block進buffer和寫buffer 是 使用者程式做的時候,透過freelist去找空閒資料塊 也是使用者程式做的事情,作者在這裡一概認為是dbwr做的。參考我所陳述的上面所有內容可以看出作者 擴大了 dbwr 的功能,將使用者程式做的很多事情以為是dbwr做的,所以會有這一系列的相關錯誤。而作者應該是一個邏輯嚴密的人,有一點小東西沒理解清楚的時候,用了好幾個猜測去支援(只是碰巧猜測錯了 :) )。 所以如果作者能有人點一下這個問題,所有相關的疑惑 迎刃而解。偶當初也這麼走過來的,有時一個小地方的錯誤理解,可能會導致自己推論出一系列錯誤來。 biti_rainy | 20/07/2005, 09:41 biti說得正確 [回覆] 毛病挑得很對,不過我認為在記憶體中寫資料,讀block進buffer和寫buffer應該是使用者的影子程式做的 yaanzy | 04/08/2005, 11:12 幾點看法 [回覆] 1. 文章思路不錯。 2. 索引沒有談到。 3. 文章最後有“畫蛇添足”之嫌。 4. 文章各部分“深淺不一”,有拼湊之嫌。
專用伺服器模式下:一種方式是監聽程式接收到使用者程式請求後,產生一個新的專用伺服器程式,並且將對使用者程式的所有控制資訊傳給此伺服器程式,也就是說新建的伺服器程式繼承了監聽程式的資訊,然後伺服器程式給使用者程式發一個RESEND包,通知使用者程式可以開始給它發資訊了,使用者程式給這個新建的伺服器程式發一個CONNECT包,伺服器程式再以ACCEPT包回應使用者程式,致此,使用者程式正式與伺服器程式確定連線。我們把這種連線叫做HAND-OFF連線,也叫轉換連線。另一種方式是監聽程式接收到使用者程式的請求後產生一個新的專用伺服器程式,這個伺服器程式選用一個TCP/IP埠來控制與使用者程式的互動,然後將此資訊回傳給監聽程式,監聽程式再將此資訊傳給使用者程式,使用者程式使用這個埠給伺服器程式傳送一個CONNECT包,伺服器程式再給使用者程式傳送一個ACCEPT包,致此,使用者程式可以正式向伺服器程式傳送資訊了。這種方式我們叫做重定向連線。HAND-OFF連線需要系統平臺具有程式繼承的能力,為了使WINDOWS NT/2000支援HAND-OFF必須在HKEY_LOCAL_MACHINE>SOFTWARE>ORACLE>HOMEX中設定USE_SHARED_SOCKET。 共享伺服器模式下:只有重定向連線的方式,工作方式是監聽程式接收到使用者程式的請求後產生一個新的排程程式,這個排程程式選用一個TCP/IP埠來控制與使用者程式的互動,然後將此資訊回傳給監聽程式,監聽程式再將此資訊傳給使用者程式,使用者程式使用這個埠給排程程式傳送一個CONNECT包,排程程式再給使用者程式傳送一個ACCEPT包,致此,使用者程式可以正式向排程程式傳送資訊了。可以透過設定MAX_DISPIATCHERS這個引數來確定排程程式的最大數目,如果排程程式的個數已經達到了最大,或者已有的排程程式不是滿負荷,監聽程式將不再建立新的排程程式,而是讓其中一個排程程式選用一個TCP/IP埠來與此使用者程式互動。排程程式每接收一個使用者程式請求都會在監聽程式處作一個登記,以便監聽程式能夠均衡每個排程程式的負荷,所有的使用者程式請求將分別在有限的排程程式中排隊,所有排程程式再順序的把各自佇列中的部分使用者程式請求放入同一個請求佇列,等候多個ORACLE的共享伺服器程式進行處理(可以透過SHARED_SERVERS引數設定共享伺服器程式的個數),也就是說所有的排程程式共享同一個請求佇列,共享伺服器模式下一個例項只有一個請求佇列,共享伺服器程式處理完使用者程式的請求後將根據使用者程式請求取自不同的排程程式將返回結果放入不同的響應佇列,也就是說有多少排程程式就有多少響應佇列,然後各個排程程式從各自的響應佇列中將結果取出再返回給使用者程式。 以上我們講完了使用者與ORACLE的連線方式,下面我們要講ORACLE伺服器程式如可處理使用者程式的請求,當一個使用者程式發出了一條SQL語名:UPDATE TABBLEA SET SALARY=SALARY*2;首先,伺服器程式把這條語句的字元轉換成ASCII等效數字碼,接著這個ASCII碼被傳遞給一個HASH函式,並返回一個HASH值,伺服器程式將到SHARED POOL 的共享PL/SQL區去查詢是否存在同樣的HASH值,如果存在,伺服器程式將使用這條語句已快取記憶體在SHARED POOL中的已分析過的版本來執行,如果不存在,伺服器程式將對該語句進行語法分析,首先檢查該語句的語法的正確性,接著對語句中涉及的表、索引、檢視等物件進行解析,並對照資料字典檢查這些物件的名稱以及相關結構,並根據ORACLE選用的最佳化模式以及資料字典中是否存在相應物件的統計資料和是否使用了儲存大綱來生成一個執行計劃或從儲存大綱中選用一個執行計劃,然後再用資料字典核對此使用者對相應物件的執行許可權,最後生成一個編譯程式碼。ORACLE將這條語名的本身實際文字、HASH值、編譯程式碼、與此語名相關聯的任何統計資料和該語句的執行計劃快取在SHARED POOL的共享PL/SQL區。伺服器程式透過SHARED POOL 鎖存器來申請可以向哪些共享PL/SQL區中快取這此內容,也就是說被SHARED POOL鎖存器鎖定的PL/SQL區中的塊不可被覆蓋,因為這些塊可能被其它程式所使用。在SQL分析階段將用到LIBRARY CACHE,從資料字典中核對錶、檢視等結構的時候,需要將資料字典從磁碟讀入LIBRARY CACHE,因此,在讀入之前也要使用LIBRARY CACHE鎖存器來申請用於快取資料字典。 生成編譯程式碼之後,接著下一步伺服器程式要準備開始更新資料,伺服器程式將到DB BUFFER中查詢是否有相關物件的快取資料,下面分兩個可能進行解釋: 如果沒有,伺服器程式將在表頭部請求一些行鎖,如果成功加鎖,伺服器程式將從資料檔案中讀這些行所在的資料塊放入DB BUFFER中空閒的區域或者覆蓋已被擠出LRU列表的非髒資料塊緩衝區,並且排列在LRU列表的頭部,如果這些非髒資料緩衝區寫完也不能滿足新資料的請求時,會立即觸發DBWN程式將髒資料列表中指向的緩衝塊寫入資料檔案,並且清洗掉這些緩衝區,來騰出空間緩衝新讀入的資料,也就是在放入DB BUFFER之前也是要先申請DB BUFFER中的鎖存器,成功鎖定後,再寫入DB BUFFER,然後伺服器程將該語句影響的被讀入DB BUFFER塊中的這些行的ROWID及將要更新的原值和新值及SCN等資訊逐條的寫入REDO LOG BUFFER,在寫入REDO LOG BUFFER之前也是先請求REDO LOG BUFFER塊的鎖存器,成功鎖定之後才開始寫入,當寫入達到REDO LOG BUFFER大小的三分之一或寫入量達到1M或超過三秒後或發生檢查點時或者DBWN之前發生,LGWR將把REDO LOG BUFFER中的資料寫入磁碟上的重做日誌檔案,已被寫入重做日誌檔案的REDO LOG BUFFER中的塊上的鎖存器被釋放,並可被後來寫入的資訊所覆蓋,REDO LOG BUFFER以迴圈的方式工作。當一個重做日誌檔案寫滿後,LGWR將切換到下一個重做日誌檔案,如果是歸檔模式,歸檔程式還將前一個寫滿的重做日誌程式寫入歸檔日誌檔案,重做日誌檔案也是迴圈工作方式。寫完所有的REDO LOG BUFFER之後,伺服器程式開始改寫這個DB BUFFER塊頭部的事務列表並寫入SCN,然後COPY包含這個塊的頭部事務列表及SCN資訊的資料副本放入回滾段中,我們將回滾段中的副本稱為資料塊的“前映像”。(回滾段可以儲存在專門的回滾表空間中,這個表空間由一個或多個物理檔案組成,並專用於回滾表空間,回滾段也可在其它表空間中的資料檔案中開闢。)然後改寫這個DB BUFFER塊的資料,並在其頭部寫入對應的回滾段地址,如果對一行資料多次UPDATE而不COMMIT則在回滾段中將會有多個“前映像”,除第一個“前映像”含有SCN資訊外,其它的每個“前映像”的頭部還含有SCN資訊和“前前映像”的回滾段地址。一次UPDATE操作只對應一個SCN。然後伺服器程式在髒資料列表中建立一條指向此緩衝塊的指標。接著伺服器程式會從資料檔案讀入第二個塊重複以上讀入,記日誌,建立回滾段,修改,放入髒列表的動作,當髒資料列表達到一定長度時,DBWN程式將髒資料列表中指向的緩衝塊全部寫入資料檔案,也就是釋放加在這些DB BUFER 塊上的鎖存器。其實ORACLE可以一次從資料檔案中讀入幾個塊放入DB BUFFER,可以透過引數DB_FILE_MULTIBLOCK_READ_COUNT來設定一次讀入的塊的個數。 如果要查詢的資料已快取,則根據使用者的SQL操作型別決定如何操作,如果是SELECT 則檢視DB BUFFER塊的頭部是否有事務,如果有,將從回滾段讀取,如果沒有則比較SELECT 的SCN與DB BUFFER塊頭部的SCN如果比自己大,仍然從回滾段讀取,如果比自己小則認這是一個非髒快取,可以直接從這個DB BUFFER塊中讀取。如果是UPDATE則即使在DB BUFFER中找到一個沒有事務,而且SCN比自己小的非髒快取資料塊,伺服器程式仍然要到表的頭部對這條記錄申請加鎖,加鎖成功則進行後續動作,如果不成功,則要等待前面的程式解鎖後才能進行動作。 只有當SQL語句影響的所有行所在的最後一個塊被讀入DB BUFFER並且重做資訊被寫入REDO LOG BUFFER(僅是指重做日誌緩衝,而非重做日誌檔案)之後,使用者才可以發出COMMIT,COMMIT觸發LGRW,但並不強制立即DBWN來釋放所有相應的DB BUFFER塊上的鎖,也就是說有可能出現已COMMIT,但在隨後的一段時間內DBWN還在寫這條語句涉及的資料塊的情形,表頭部的行鎖,並不是在COMMIT一發出就馬上釋放,實際上要等到相應的DBWN程式結束才會釋放。一個使用者請求鎖定另一個使用者已COMMIT的資源不成功的機會是存在的,從COMMIT到DBWN程式結束之間的時間很短,如果恰巧在這個時間斷電,由於COMMIT已觸發LGWR程式,所以這些未來得及寫入資料檔案的改變會在例項重啟後由SMON程式根據重做日誌檔案來前滾。如果未COMMIT就斷電,由於DBWN之前觸發LGWR,所有DBWN在資料檔案上的修改都會被先一步記入重做日誌檔案,例項重啟後,SMON程式再根據重做日誌檔案來回滾。 如果使用者ROOLBACK,則伺服器程式會根據資料檔案塊和DB BUFFER中塊的頭部的事務列表和SCN以及回滾段地址找到回滾段中相應的修改前的副本,並且用這些原值來還原當前資料檔案中已修改但未提交的改變。如果有多個“前映像”,伺服器程式會在一個“前映像”的頭部找到“前前映像”的回滾段地址,一直找到同一事務下的最早的一個“前映像”為止。一旦發出了COMMIT,使用者就不能ROOLBACK,這使得COMMIT後DBWN程式還沒有全部完成的後續動作得到了保障。 下面我們要提到檢查點的作用,當一個全部檢查點發生的時候,首先讓LGWR程式將REDO LOG BUFFER中的所有緩衝(包含未提交的重做資訊)寫入重做日誌檔案,然後讓DBWN程式將DB BUFFER中所有已提交的緩衝寫入資料檔案(不強制寫未提交的)。然後更新控制檔案和資料檔案頭部的SCN,表明當前資料庫是一致的,如果在發生檢點之前斷電,並且當時有一個未提交的改變正在進行,例項重啟之後,SMON程式將從上一個檢查點開始核對這個檢查點之後記錄在重做日誌檔案中已提交的和未提交改變,因為DBWN之前會觸發LGWR,所以DBWN對資料檔案的修改一定會被先記錄在重做日誌檔案中。因此,斷電前被DBWN寫進資料檔案的改變將透過重做日誌檔案中的記錄進行還原,叫做回滾,如果斷電時有一個已提交,但DBWN動作還沒有完全完成的改變存在,因為已經提交,提交會觸發LGWR程式,所以不管DBWN動作是否已完成,該語句將要影響的行及其產生的結果一定已經記錄在重做日誌檔案中了,則例項重啟後,SMON程式根據重做日誌檔案進行前滾。由此可見,例項失敗後用於恢復的時間由兩個檢查點之間的間隔大小來決定,我們可以通個四個引數設定檢查點執行的頻率,LOG_CHECKPOINT_IMTERVAL決定了兩個檢查點之間寫入重做日誌檔案的系統物理塊的大小,LOG_CHECKPOINT_TIMEOUT決定了兩個檢查點之間的時間長度,FAST_START_IO_TARGET決定了用於恢復時需要處理的塊的大小,FAST_START_MTTR_TARGET直接決定了用於恢復的時間的長短。SMON程式執行的前滾和回滾與使用者的回滾是不同的,SMON是根據重做日誌檔案進行前滾或回滾,而使用者的回滾一定是根據回滾段的內容進行回滾的。在這裡我們要說一下回滾段儲存的資料,假如是delete操作,則回滾段將會記錄整個行的資料,假如是update,則回滾段只記錄被修改了的欄位的變化前的資料(前映像),也就是沒有被修改的欄位是不會被記錄的,假如是insert,則回滾段只記錄插入記錄的rowid。這樣假如事務提交,那回滾段中簡單標記該事務已經提交;假如是回退,則如果操作是是delete,回退的時候把回滾段中資料重新寫回資料塊,操作如果是update,則把變化前資料修改回去,操作如果是insert,則根據記錄的rowid 把該記錄刪除。 下面我們要講DBWN如何來寫資料檔案,在寫資料檔案前首先要找到可寫的空閒資料塊,ORACLE中空閒資料塊可以透過FREELIST或BITMAP來維護,它們位於一個段的頭部用來標識當前段中哪些資料塊可以進行INSERT。在本地管理表空間中ORACLE自動管理分配給段的區的大小,區的分配資訊儲存在組成表空間的資料檔案的頭部,而資料字典管理的表空間使用者可以在建立時決定區的大小,並且區的分配資訊是儲存在資料字典中的,只在本地管理的表空間中才能選用段自動管理,採用自動段空間管理的本地管理表空間中的段中的空閒資料塊的資訊就存放在段的頭部並且使用點陣圖來管理,採用手動管理的本地管理表空間中的段和資料字典管理的表空間中的段中的空閒資料塊的管理都使用位於段頭部的空閒列表來管理,空閒列表的工作方式:首先一個空的資料塊被加入空閒列表,當其中空閒空間小於PCTFREE設定的值之後,這個塊從空閒列表刪除,當這個塊中的內容降至PCTUSED設定的值之下後,這個資料塊被再次加入空閒列表,位於空閒列表中的資料塊都是可以向其中INSERT的塊,當一個塊移出了空閒列表,但只要其中還有保留空間就可以進行UPDATE,當對其中一行UPDATE一個大資料時,如果當前塊不能完全放下整個行,只會把整個行遷移到一個新的資料塊,並在原塊位置留下一個指向新塊的指標,這叫行遷移。如果一個資料塊可以INSERT,當插入一個當前塊裝不下的行時,這個行會溢位到兩個或兩個几上的塊中,這叫行連結。如果使用者的動作是INSERT 則伺服器程式會先鎖定FREELIST,然後找到空閒塊的地址,再釋放FREELIST,當多個伺服器程式同時想要鎖定FREELIST時即發生FREELIST的爭用,可以在非採用自動段空間管理的表空間中建立表時指定FREELIST的個數,預設為1,如果是在採用自動段空間管理的表空間中建立表,即使指定了FREELIST也會被忽略,因為此時將使用BITMAP而不是FREELIST來管理段中的空閒空間。如果使用者動作是UPDATE伺服器程式將不會使用到FREELIST和BITMAP,因為不要去尋找一個空閒塊,而使用鎖的佇列。 下面來講一下ORACLE鎖的機制,ORACLE分鎖存器和鎖兩種。鎖存器是用來保護對記憶體結構的訪問,比如對DB BUFFER中塊的鎖存器申請,只有在DBWN完成後,這些DB BUFFER塊被解鎖。然後用於其它的申請。鎖存器不可以在程式間共享,鎖存器的申請要麼成功要麼失敗,沒有鎖存器申請佇列。主要的鎖存器有SHARED POOL鎖存器,LIBRARY CACHE鎖存器,CACHE BUFFERS LRU CHAIN鎖存器,CACHE BUFFERS CHAINS 鎖存器,REDO ALLOCATION 鎖存器,REDO COPY 鎖存器。ORACLE的鎖是用來保護資料訪問的,鎖的限制比鎖存器要更寬鬆,比如,多個使用者在修改同一表的不同行時,可以共享一個表上的一個鎖,鎖的申請可以按照被申請的順序來排隊等候,然後依次應用,這種排隊機制叫做佇列(ENPUEUE),如果兩個伺服器程式試圖對同一表的同一行進行加鎖,則都進入鎖的申請佇列,先進的加鎖成功,後面的程式要等待,直到前一個程式解鎖才可以加鎖,這叫做鎖的爭用,而且一旦加鎖成功,這個鎖將一直保持到使用者發出COMMIT或ROOLBACK命令為止。如果兩個使用者鎖定各自的一行並請求對方鎖定的行的時候將發生無限期等待即死鎖,死鎖的發生都是由於鎖的爭用而不是鎖存器的爭用引起的,ORACLE在遇到死鎖時,自動釋放其中一個使用者的鎖並回滾此使用者的改變。正常情況下發生鎖的爭用時,資料的最終儲存結果由SCN來決定哪個程式的更改被最終儲存。兩個使用者的伺服器程式在申請同一表的多個行的鎖的時候是可以交錯進入鎖的申請佇列的。只有其中發生爭用才會進行等待。建立表時指定的MAXTRANS引數決了,表中的一個資料塊最多可以被幾個事務同時鎖定。 下面是幾個關於回滾段和死鎖的事例: 有表:Test (id number(10)) 有記錄1000000條 一,大SELECT,小UPDATE A會話----Select * from test;----設scn=101----執行時間09:10:11 B會話-----Update test set id=9999999 where id=1000000----設scn=102-----執行時間09:10:12 我們會發現B會話會在A會話前完成,A會話中顯示的ID=100000是從回滾段中讀取的,因為A會話在讀到ID=1000000所在的BLOCK時發現BLOCK上有事務資訊,因此要從回滾段中讀,如果UPDATE在SELECT讀到此BLOCK之前已經COMMIT,則SELECT 讀到此BLOCK時發現其BLOCK上沒有事務資訊,但是會發現其BLICK的SCN比SELECT自己的SCN大,因此也會從回滾段中讀取。因此是否從回滾段讀一是看是否有事務資訊二是比較SCN大小。如果B會話在A會話結束前連續多次對同一條記錄UPDATE並COMMIT ,那麼在回滾段中將記錄多個“前映像”,而每個“前映像”中不但包括了原BLOCK的資料和SCN也記錄了“前前映像”的回滾段地址,因此A會話在查詢到被UPDATE過的BLOCK時,會根據BLOCK記錄的回滾段的地址,找到回滾段中的“前映像”,發現這個“前映像”的SCN也比自己的大,因此將根據這個“前映像”中記錄的“前前映像”的回滾段地址,在回滾段中找到“前前映像”,再與這個“前前映像”比較SCN,如果比自己小就讀取,如果還比自己大,則重複以上步驟,直到找到比自己SCN小的“前…前映像”為止,如果找不到,就會報ORA-01555快照太舊這個錯誤。 二、大UPDATE,小SELECT A會話----Update test set id=1;----設scn=101----執行時間09:10:11 B會話-----select * from test where id=1000000----設scn=102-----執行時間09:10:12 我們會發現B會話會在A會話前完成,B會話中顯示的ID=1000000是從BLOCK中直接讀取的,因為B會話在讀到ID=1000000所在的BLOCK時,A會話還沒有來得及對其鎖定,因此B會話既不會發現BLOCK上有事務資訊,也不會發現BLOCK上的SCN比SELECT的大,因此會從BLOCK中直接讀取,如果SELECT在UPDATE鎖定此BLOCK後才發出,B會話讀到此BLOCK時發現其BLOCK上有事務資訊,因此會從回滾段中讀取。 三、大UPDATE,小UPDATE A會話----Update test set id=1;----設scn=101----執行時間09:10:11 B會話1-----Update test set id=999999 where id=1000000----設scn=102-----執行時間09:10:12 B會話2----- select * from test where id=2----設scn=103-----執行時間09:10:14 B會話3----- update test set id=3 where id=2----設scn=104-----執行時間09:10:15 我們會發現B會話1會完成,A會話將一直等待,因為B會話1會先於A會話鎖定ID=1000000所在的BLOCK,並改寫頭部的事務資訊,A會話在試圖鎖定此BLOCK時,發現其上有事務資訊,將會一直等待B會話1事務結束後再行鎖定, B會話2查詢到的ID=2是從回滾段中讀取的而不是從BLOCK中直接讀出來的。因為A會話已將ID=2的BLOCK鎖定,並寫入了回滾段,從B會話3可以證明這一點,B會話3發出後,B會話3會收到死鎖的資訊,死鎖的原因是A會話在等待B會話對ID=1000000所在的BLOCK解鎖,現在B會話又在等待A會話對ID=2所在的BLOCK解鎖,因此形成死鎖,因此證明ID=2所在的BLOCK已被A會話鎖定,然後A會話也會收到死鎖的資訊 分類: () :: 評論 (5) :: 靜態連結網址 :: 引用 (0) 很不錯! [回覆] 這篇文章確實很不錯,不知道作者是哪位?我找了下,老兄是不是從這裡轉過來的:?作者對ORACLE的概念理解得非常清晰。 blue_prince | 28/06/2005, 13:17 呵呵 [回覆] 忘了從哪裡轉來的了 yaanzy | 01/07/2005, 10:28 我來挑點刺哈 [回覆] 不得不承認,作者對oracle的理解是深刻的,也是頗費了工夫去思考的。只是可能由於缺乏和人的共同探討 或者 碰巧有點小錯誤引出一些推導錯誤。 如果要查詢的資料已快取,則根據使用者的SQL操作型別決定如何操作,如果是SELECT 則檢視DB BUFFER塊的頭部是否有事務,如果有,將從回滾段讀取,如果沒有則比較SELECT 的SCN與DB BUFFER塊頭部的SCN如果比自己大,仍然從回滾段讀取,如果比自己小則認這是一個非髒快取,可以直接從這個DB BUFFER塊中讀取。如果是UPDATE則即使在DB BUFFER中找到一個沒有事務,而且SCN比自己小的非髒快取資料塊,伺服器程式仍然要到表的頭部對這條記錄申請加鎖,加鎖成功則進行後續動作,如果不成功,則要等待前面的程式解鎖後才能進行動作。 只有當SQL語句影響的所有行所在的最後一個塊被讀入DB BUFFER並且重做資訊被寫入REDO LOG BUFFER(僅是指重做日誌緩衝,而非重做日誌檔案)之後,使用者才可以發出COMMIT,COMMIT觸發LGRW,但並不強制立即DBWN來釋放所有相應的DB BUFFER塊上的鎖,也就是說有可能出現已COMMIT,但在隨後的一段時間內DBWN還在寫這條語句涉及的資料塊的情形,表頭部的行鎖,並不是在COMMIT一發出就馬上釋放,實際上要等到相應的DBWN程式結束才會釋放。 由上面兩段話可以看出點 瑕疵,作者對 block cleanout 概念不瞭解,所以在這兩個地方描述有遺漏,第二段更是因為這個概念不清楚而引出一些猜測 :) 下面我們要提到檢查點的作用,當一個全部檢查點發生的時候,首先讓LGWR程式將REDO LOG BUFFER中的所有緩衝(包含未提交的重做資訊)寫入重做日誌檔案,然後讓DBWN程式將DB BUFFER中所有已提交的緩衝寫入資料檔案(不強制寫未提交的)。 由上面這段話可以看出多檢查點理解有誤,dbwr寫的時候不會管是否提交的,由此也許可以理解為是對 恢復 的過程理解不是很深刻,但是也可以和 block cleanout 套上關係,因為參考更前面兩段話,作者一直以為是dbwr 來處理 block cleanout的。 下面我們要講DBWN如何來寫資料檔案,在寫資料檔案前首先要找到可寫的空閒資料塊,ORACLE中空閒資料塊可以透過FREELIST或BITMAP來維護,它們位於一個段的頭部用來標識當前段中哪些資料塊可以進行INSERT 由這段話也有錯誤,這不是寫資料檔案,這是在記憶體中寫資料,讀block進buffer和寫buffer 是 使用者程式做的時候,透過freelist去找空閒資料塊 也是使用者程式做的事情,作者在這裡一概認為是dbwr做的。參考我所陳述的上面所有內容可以看出作者 擴大了 dbwr 的功能,將使用者程式做的很多事情以為是dbwr做的,所以會有這一系列的相關錯誤。而作者應該是一個邏輯嚴密的人,有一點小東西沒理解清楚的時候,用了好幾個猜測去支援(只是碰巧猜測錯了 :) )。 所以如果作者能有人點一下這個問題,所有相關的疑惑 迎刃而解。偶當初也這麼走過來的,有時一個小地方的錯誤理解,可能會導致自己推論出一系列錯誤來。 biti_rainy | 20/07/2005, 09:41 biti說得正確 [回覆] 毛病挑得很對,不過我認為在記憶體中寫資料,讀block進buffer和寫buffer應該是使用者的影子程式做的 yaanzy | 04/08/2005, 11:12 幾點看法 [回覆] 1. 文章思路不錯。 2. 索引沒有談到。 3. 文章最後有“畫蛇添足”之嫌。 4. 文章各部分“深淺不一”,有拼湊之嫌。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/35489/viewspace-84389/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- oracle特性 yangtingkun BLOG文章索引 [zt]Oracle索引
- 在Oracle中進行大小寫不敏感的查詢[zt]Oracle
- 使用Oracle Hint提示來更改執行計劃 ZTOracle
- 不錯的關於Oracle 全文索引的文章(zt)Oracle索引
- ORACLE的動態註冊行為 ztOracle
- Oracle的MTS (ZT)Oracle
- shell 指令碼訪問oracle 寫法 (ZT)指令碼Oracle
- ORACLE鎖的管理(zt)Oracle
- oracle enqueue(zt)OracleENQ
- [ORACLE] SQL執行OracleSQL
- 文章主題: 在Oracle中查詢剛才執行過的SQL語句OracleSQL
- ORACLE MTS的介紹(zt)Oracle
- 理解 oracle 的 “lsnrctl status”(ZT)Oracle
- [zt] Oracle Undo的學習Oracle
- ORACLE LARGE MEMORY(zt)Oracle
- oracle job管理(zt)Oracle
- oracle event 2 (zt)Oracle
- [zt] Oracle LogMinerOracle
- zt_oracle indexOracleIndex
- Oracle RAC TAF [zt]Oracle
- oracle block 格式 (zt)OracleBloC
- ORACLE ROWID (zt)Oracle
- [zt] ORACLE RAC原理Oracle
- jboss oracle rac (zt)Oracle
- ORACLE執行計劃Oracle
- 如何取得Oracle並行執行的traceOracle並行
- [zt] 利用 SQL Server 中的 Linked Server 執行 Oracle 中之預儲函式、程式SQLServerOracle函式
- zt_oracle11g sql baseline與sql execution plan執行計劃OracleSQL
- Oracle中檢視已執行sql的執行計劃OracleSQL
- [zt] Oracle的邏輯結構Oracle
- Oracle DBA的日常工作(zt)Oracle
- Oracle的隱含引數(zt)Oracle
- 看懂Oracle中的執行計劃Oracle
- ORACLE執行計劃的介紹Oracle
- ORACLE執行計劃的檢視Oracle
- oracle執行計劃的使用(EXPLAIN)OracleAI
- Oracle中表連線的執行原理Oracle
- oracle 正在執行的物件查詢Oracle物件