學習筆記:InnoDB儲存結構及多版本實現

denniswwh發表於2009-07-21
因為InnoDB是多版本化的資料庫儲存引擎, 它必須在表空間中儲存關於舊版本資料行的資訊。這個資訊被存在名為rollback segment(類似於Oracle中的回滾段)的資料結構中。[@more@]

在內部,InnoDB給資料庫中的每一行新增三個域。一個是6位元組的DB_TRX_ID域,用來說明插入或更新該行的最後一個事務的事務識別符。同時,刪除操作也被內部處理為更新操作,其中行中一個特殊的位被設定用來標註該行已刪除。每一行也包含一個稱為回滾指標的7位元組DB_ROLL_PTR域。滾指標指向一個寫在回滾段中的撤銷日誌記錄。如果該行被更新,撤銷日誌記錄包含重建該行被更新之前的內容必需的資訊。還有一個6位元組的DB_ROW_ID域包含在新行被插入時單調增加的Row ID,如果InnoDB自動生成聚集索引,索引中就會包含這個Row ID。否則,這個DB_ROW_ID不會出現在任何索引中。

InnoDB使用在回滾中的資訊來執行事務回滾中需要的撤銷操作。它也使用這個資訊來為一個持續讀構建資料行的早期版本

回滾中的撤銷日誌被分為插入和更新兩種。插入撤銷日誌僅在事務回滾中需要,且只要事務一提交就可以被丟棄。更新撤銷日誌也被用在持續讀中,而且它們僅在沒有被InnoDB分配給一個事務快照之後才能被丟棄,因為這個快照在持續讀中可能會需要撤銷日誌的資訊來建立一個資料行的早期版本。

你必須記得有規律地提交你的事務,包括那些只包含持續讀的事務。否則, InnoDB不能從更新撤銷日誌丟棄資料,並且回滾可能變得太大,填滿你的表空間。

在一個回滾裡,一個撤銷日誌記錄的物理尺寸小於相應的已插入行或已更新行。你可以用這個資訊來計算回滾需要的空間。如果是插入,那麼記錄那個行的id號到回滾段就ok了,如果要刪除,直接透過id號來定位行即可刪除它,實現回滾.如果是一般的更新,那麼直接記錄對應的行id和被更新的欄位即可.但是如果是刪除操作,innodb只需要儲存一條刪除日誌到回滾段中,比如只包含rowid資訊,回滾的時候,只需要把對應的rowid的標記位deleted刪除即可.

在InnoDB多版本化方案中,當你用SQL語句刪除一行時,該行沒有被從資料庫立即物理刪除掉。只有當InnoDB可以丟棄用於刪除操作的撤銷日誌記錄時,InnoDB才從資料庫物理刪除相應行以及它的索引記錄。這個刪除操作被成為淨化(Purge,有點類似於Postgresql中的autovacuum),它執行得很快,通常與做刪除的SQL語句花的時間在一個數量級。(這種方式還是非常先進的,理由有2: a.對於刪除操作,完全可以把刪除的行全部移動到回滾日誌中,而不是標記為刪除.當需要回滾的時候,直接把這些行執行插入操作即可回滾;而需要提供一致讀的時候,直接去回滾段中查詢即可----這顯然浪費了大量的IO,所以innodb不這麼做. b.這種做法還節省了磁碟空間.試想如果把刪除的資訊都儲存到回滾段中,一個delete * from t該要產生多少回滾日誌?浪費多少磁碟空間?而且當事務回滾的時候,把這些東西從回滾段複製到磁碟,又是多麼浪費時間? 當回滾段被重用的時候,innodb會先刪除回滾日誌中的條目,然後把這些回滾日誌指向的被標記為deleted的行也purge掉. oracle的做法是刪除的時候將整行資料放入回滾段,所以oracle中事務回滾的成本是很高的

在某種情景下,使用者以幾乎相同的比率,小批次地在表中插入和刪除行,淨化執行緒可能會滯後,並且表變得越來越大,所有操作都由於磁碟約束變得非常慢。即使表只有10MB有用的資料,它也可能被死行佔據10GB空間。在這種情況下,節流新操作,並分配更多的資源來淨化執行緒會比較好。全域性系統變數innodb_max_purge_lag就是為此而生的。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/124805/viewspace-1024337/,如需轉載,請註明出處,否則將追究法律責任。

相關文章