本文是對整個Undo生命周期過程的闡述,代碼分析基于當前最新的MySQL5.7版本。本文也可以作為了解整個Undo模塊的代碼導讀。由于涉及到的模塊眾多,因此部分細節并未深入。
## 前言
Undo log是InnoDB MVCC事務特性的重要組成部分。當我們對記錄做了變更操作時就會產生undo記錄,Undo記錄默認被記錄到系統表空間(ibdata)中,但從5.6開始,也可以使用獨立的Undo 表空間。
Undo記錄中存儲的是老版本數據,當一個舊的事務需要讀取數據時,為了能讀取到老版本的數據,需要順著undo鏈找到滿足其可見性的記錄。當版本鏈很長時,通常可以認為這是個比較耗時的操作(例如[bug#69812](http://bugs.mysql.com/bug.php?id=69812 "the server stalls at function row_vers_build_for_consistent_read"))。
大多數對數據的變更操作包括INSERT/DELETE/UPDATE,其中INSERT操作在事務提交前只對當前事務可見,因此產生的Undo日志可以在事務提交后直接刪除(誰會對剛插入的數據有可見性需求呢!!),而對于UPDATE/DELETE則需要維護多版本信息,在InnoDB里,UPDATE和DELETE操作產生的Undo日志被歸成一類,即update_undo。
## 基本文件結構
為了保證事務并發操作時,在寫各自的undo log時不產生沖突,InnoDB采用回滾段的方式來維護undo log的并發寫入和持久化。回滾段實際上是一種 Undo 文件組織方式,每個回滾段又有多個undo log slot。具體的文件組織方式如下圖所示:

上圖展示了基本的Undo回滾段布局結構,其中:
1. rseg0預留在系統表空間ibdata中;
2. rseg 1~rseg 32這32個回滾段存放于臨時表的系統表空間中;
3. rseg33~ 則根據配置存放到獨立undo表空間中(如果沒有打開獨立Undo表空間,則存放于ibdata中)
如果我們使用獨立Undo tablespace,則總是從第一個Undo space開始輪詢分配undo 回滾段。大多數情況下這是OK的,但假設我們將回滾段的個數從33開始依次遞增配置到128,就可能導致所有的回滾段都存放在同一個undo space中。(參考函數trx_sys_create_rsegs 以及?[bug#74471](http://bugs.mysql.com/bug.php?id=74471 "most rollback segments may always be created in first undo tablespace"))
每個回滾段維護了一個段頭頁,在該page中又劃分了1024個slot(TRX_RSEG_N_SLOTS),每個slot又對應到一個undo log對象,因此理論上InnoDB最多支持 96 * 1024個普通事務。
## 關鍵結構體
為了便于管理和使用undo記錄,在內存中維持了如下關鍵結構體對象:
1. 所有回滾段都記錄在`trx_sys->rseg_array`,數組大小為128,分別對應不同的回滾段;
2. rseg_array數組類型為trx_rseg_t,用于維護回滾段相關信息;
3. 每個回滾段對象trx_rseg_t還要管理undo log信息,對應結構體為trx_undo_t,使用多個鏈表來維護trx_undo_t信息;
4. 事務開啟時,會專門給他指定一個回滾段,以后該事務用到的undo log頁,就從該回滾段上分配;
5. 事務提交后,需要purge的回滾段會被放到purge隊列上(`purge_sys->purge_queue`)。
各個結構體之間的聯系如下:

## 分配回滾段
當開啟一個讀寫事務時(或者從只讀事務轉換為讀寫事務),我們需要預先為事務分配一個回滾段:
對于只讀事務,如果產生對臨時表的寫入,則需要為其分配回滾段,使用臨時表回滾段(第1~32號回滾段),函數入口:`trx_assign_rseg -->trx_assign_rseg_low-->get_next_noredo_rseg`。
在MySQL5.7中事務默認以只讀事務開啟,當隨后判定為讀寫事務時,則轉換成讀寫模式,并為其分配事務ID和回滾段,調用函數:`trx_set_rw_mode -->trx_assign_rseg_low --> get_next_redo_rseg`。
普通回滾段的分配方式如下:
1. 采用round-robin的輪詢方式來賦予回滾段給事務,如果回滾段被標記為skip_allocation(這個undo tablespace太大了,purge線程需要對其進行truncate操作),則跳到下一個;
2. 選擇一個回滾段給事務后,會將該回滾段的`rseg->trx_ref_count`遞增,這樣該回滾段所在的undo tablespace文件就不可以被truncate掉;
3. 臨時表回滾段被賦予`trx->rsegs->m_noredo`,普通讀寫操作的回滾段被賦予`trx->rsegs->m_redo`;如果事務在只讀階段使用到臨時表,隨后轉換成讀寫事務,那么會為該事務分配兩個回滾段。
## 使用回滾段
當產生數據變更時,我們需要使用Undo log記錄下變更前的數據以維護多版本信息。insert 和 delete/update 分開記錄undo,因此需要從回滾段單獨分配Undo slot。
入口函數:`trx_undo_report_row_operation`
流程如下:
1. 判斷當前變更的是否是臨時表,如果是臨時表,則采用臨時表回滾段來分配,否則采用普通的回滾段;
2. 臨時表操作記錄undo時不寫redo log;
3. 操作類型為TRX_UNDO_INSERT_OP,且未分配insert undo slot時,調用函數`trx_undo_assign_undo`進行分配;
4. 操作類型為TRX_UNDO_MODIFY_OP,且未分配Update undo slot時,調用函數`trx_undo_assign_undo`進行分配。
我們來看看函數trx_undo_assign_undo的流程:
1. 首先總是從cahced list上分配trx_undo_t (函數`trx_undo_reuse_cached`,當滿足某些條件時,事務提交時會將其擁有的trx_undo_t放到cached list上,這樣新的事務可以重用這些undo 對象,而無需去掃描回滾段,尋找可用的slot,在后面的事務提交一節會介紹到);
* 對于INSERT,從`trx_rseg_t::insert_undo_cached`上獲取,并修改頭部重用信息(trx_undo_insert_header_reuse)及預留XID空間(trx_undo_header_add_space_for_xid)
* 對于DELETE/UPDATE,從`trx_rseg_t::update_undo_cached`上獲取, 并在undo log hdr page上創建新的Undo log header(trx_undo_header_create),及預留XID存儲空間(trx_undo_header_add_space_for_xid)
* 獲取到trx_undo_t對象后,會從cached list上移除掉。并初始化trx_undo_t相關信息(trx_undo_mem_init_for_reuse),將`trx_undo_t::state`設置為TRX_UNDO_ACTIVE
2. 如果沒有cache的trx_undo_t,則需要從回滾段上分配一個空閑的undo slot(trx_undo_create),并創建對應的undo頁,進行初始化;
一個回滾段可以支持1024個事務并發,如果不幸回滾段都用完了(通常這幾乎不會發生),會返回錯誤DB_TOO_MANY_CONCURRENT_TRXS
每一個Undo log segment實際上對應一個獨立的段,段頭的起始位置在UNDO 頭page的TRX_UNDO_SEG_HDR+TRX_UNDO_FSEG_HEADER偏移位置(見下圖)
3. 已分配給事務的trx_undo_t會加入到鏈表`trx_rseg_t::insert_undo_list`或者`trx_rseg_t::update_undo_list上`;
4. 如果是數據詞典操作(DDL)產生的undo,主要是表級別操作,例如創建或刪除表,還需要記錄操作的table id到undo log header中(TRX_UNDO_TABLE_ID),同時將TRX_UNDO_DICT_TRANS設置為TRUE。(trx_undo_mark_as_dict_operation)。
總的來說,undo header page主要包括如下信息:

## 如何寫入undo日志
入口函數:`trx_undo_report_row_operation`
當分配了一個undo slot,同時初始化完可用的空閑區域后,就可以向其中寫入undo記錄了。寫入的page no取自`undo->last_page_no`,初始情況下和hdr_page_no相同。
對于INSERT_UNDO,調用函數trx_undo_page_report_insert進行插入,記錄格式大致如下圖所示:

對于UPDATE_UNDO,調用函數`trx_undo_page_report_modify`進行插入,UPDATE UNDO的記錄格式大概如下圖所示:

在寫入的過程中,可能出現單頁面空間不足的情況,導致寫入失敗,我們需要將剛剛寫入的區域清空重置(trx_undo_erase_page_end),同時申請一個新的page(trx_undo_add_page) 加入到undo log段上,同時將`undo->last_page_no`指向新分配的page,然后重試。
完成Undo log寫入后,構建新的回滾段指針并返回(trx_undo_build_roll_ptr),回滾段指針包括undo log所在的回滾段id、日志所在的page no、以及page內的偏移量,需要記錄到聚集索引記錄中。
## 事務Prepare階段
入口函數:trx_prepare_low
當事務完成需要提交時,為了和BINLOG做XA,InnoDB的commit被劃分成了兩個階段:prepare階段和commit階段,本小節主要討論下prepare階段undo相關的邏輯。
為了在崩潰重啟時知道事務狀態,需要將事務設置為Prepare,MySQL 5.7對臨時表undo和普通表undo分別做了處理,前者在寫undo日志時總是不需要記錄redo,后者則需要記錄。
分別設置insert undo 和 update undo的狀態為prepare,調用函數trx_undo_set_state_at_prepare,過程也比較簡單,找到undo log slot對應的頭頁面(trx_undo_t::hdr_page_no),將頁面段頭的TRX_UNDO_STATE設置為TRX_UNDO_PREPARED,同時修改其他對應字段,如下圖所示(對于外部顯式XA所產生的XID,這里不做討論):

Tips:InnoDB層的XID是如何獲取的呢? 當Innodb的參數innodb_support_xa打開時,在執行事務的第一條SQL時,就會去注冊XA,根據第一條SQL的query id拼湊XID數據,然后存儲在事務對象中。參考函數`trans_register_ha`。
## 事務Commit
當事務commit時,需要將事務狀態設置為COMMIT狀態,這里同樣通過Undo來實現的。
入口函數:`trx_commit_low-->trx_write_serialisation_history`
在該函數中,需要將該事務包含的Undo都設置為完成狀態,先設置insert undo,再設置update undo(trx_undo_set_state_at_finish),完成狀態包含三種:
* 如果當前的undo log只占一個page,且占用的header page大小使用不足其3/4時(TRX_UNDO_PAGE_REUSE_LIMIT),則狀態設置為TRX_UNDO_CACHED,該undo對象會隨后加入到undo cache list上;
* 如果是Insert_undo(undo類型為TRX_UNDO_INSERT),則狀態設置為TRX_UNDO_TO_FREE;
* 如果不滿足a和b,則表明該undo可能需要Purge線程去執行清理操作,狀態設置為TRX_UNDO_TO_PURGE。
在確認狀態信息后,寫入undo header page的TRX_UNDO_STATE中。
如果當前事務包含update undo,并且undo所在回滾段不在purge隊列時,還需要將當前undo所在的回滾段(及當前最大的事務號)加入Purge線程的Purge隊列(purge_sys->purge_queue)中(參考函數`trx_serialisation_number_get`)。
對于undate undo需要調用`trx_undo_update_cleanup`進行清理操作,清理的過程包括:
1. 將undo log加入到history list上,調用`trx_purge_add_update_undo_to_history`:
* 如果該undo log不滿足cache的條件(狀態為TRX_UNDO_CACHED,如上述),則將其占用的slot設置為FIL_NULL,意為slot空閑,同時更新回滾段頭的TRX_RSEG_HISTORY_SIZE值,將當前undo占用的page數累加上去;
* 將當前undo加入到回滾段的TRX_RSEG_HISTORY鏈表上,作為鏈表頭節點,節點指針為UNDO頭的TRX_UNDO_HISTORY_NODE;
* 更新`trx_sys->rseg_history_len`(也就是show engine innodb status看到的history list),如果只有普通的update_undo,則加1,如果還有臨時表的update_undo,則加2,然后喚醒purge線程;
* 將當前事務的`trx_t::no`寫入undo頭的TRX_UNDO_TRX_NO段;
* 如果不是delete-mark操作,將undo頭的TRX_UNDO_DEL_MARKS更新為false;
* 如果undo所在回滾段的`rseg->last_page_no`為FIL_NULL,表示該回滾段的舊的清理已經完成,進行如下賦值,記錄這個回滾段上第一個需要purge的undo記錄信息:
~~~
rseg->last_page_no = undo->hdr_page_no;
rseg->last_offset = undo->hdr_offset;
rseg->last_trx_no = trx->no;
rseg->last_del_marks = undo->del_marks;
~~~
2. 如果undo需要cache,將undo對象放到回滾段的update_undo_cached鏈表上;否則釋放undo對象(trx_undo_mem_free)。
注意上面只清理了update_undo,insert_undo直到事務釋放記錄鎖、從讀寫事務鏈表清除、以及關閉read view后才進行,調用函數trx_undo_insert_cleanup:
1. 如果Undo狀態為TRX_UNDO_CACHED,則加入到回滾段的insert_undo_cached鏈表上;
2. 否則,將該undo所占的segment及其所占用的回滾段的slot全部釋放掉(trx_undo_seg_free),修改當前回滾段的大小(rseg->curr_size),并釋放undo對象所占的內存(trx_undo_mem_free),和Update_undo不同,insert_undo并未放到History list上。
事務完成提交后,需要將其使用的回滾段引用計數rseg->trx_ref_count減1;
## 事務回滾
如果事務因為異常或者被顯式的回滾了,那么所有數據變更都要改回去。這里就要借助回滾日志中的數據來進行恢復了。
入口函數為:`row_undo_step --> row_undo`
操作也比較簡單,析取老版本記錄,做逆向操作即可:對于標記刪除的記錄清理標記刪除標記;對于in-place更新,將數據回滾到最老版本;對于插入操作,直接刪除聚集索引和二級索引記錄(row_undo_ins)。
具體的操作中,先回滾二級索引記錄(row_undo_mod_del_mark_sec、row_undo_mod_upd_exist_sec、row_undo_mod_upd_del_sec),再回滾聚集索引記錄(row_undo_mod_clust)。這里不展開描述,可以參閱對應的函數。
## 多版本控制
InnoDB的多版本使用undo來構建,這很好理解,undo記錄中包含了記錄更改前的鏡像,如果更改數據的事務未提交,對于隔離級別大于等于read commit的事務而言,它不應該看到已修改的數據,而是應該給它返回老版本的數據。
入口函數:?`row_vers_build_for_consistent_read`
由于在修改聚集索引記錄時,總是存儲了回滾段指針和事務id,可以通過該指針找到對應的undo 記錄,通過事務Id來判斷記錄的可見性。當舊版本記錄中的事務id對當前事務而言是不可見時,則繼續向前構建,直到找到一個可見的記錄或者到達版本鏈尾部。(關于事務可見性及read view,可以參閱我們[之前的月報](http://mysql.taobao.org/index.php?title=MySQL%E5%86%85%E6%A0%B8%E6%9C%88%E6%8A%A5_2014.12#MySQL.C2.B7.E3.80.80.E6.80.A7.E8.83.BD.E4.BC.98.E5.8C.96.C2.B75.7_Innodb.E4.BA.8B.E5.8A.A1.E7.B3.BB.E7.BB.9F "MySQL· 性能優化·5.7 Innodb事務系統"))
Tips 1:構建老版本記錄(`trx_undo_prev_version_build`)需要持有page latch,因此如果Undo鏈太長的話,其他請求該page的線程可能等待時間過長導致crash,最典型的就是備庫備份場景:
當備庫使用innodb表存儲復制位點信息時(relay_log_info_repository=TABLE),邏輯備份顯式開啟一個read view并且執行了長時間的備份時,這中間都無法對slave_relay_log_info表做purge操作,導致版本鏈極其長;當開始備份slave_relay_log_info表時,就需要去花很長的時間構建老版本;復制線程由于需要更新slave_relay_log_info表,因此會陷入等待Page latch的場景,最終有可能導致信號量等待超時,實例自殺。 ([bug#74003](http://bugs.mysql.com/bug.php?id=74003 "The server was crashed because of long semaphore wait"))
Tips 2:在構建老版本的過程中,總是需要創建heap來存儲舊版本記錄,實際上這個heap是可以重用的,無需總是重復構建([bug#69812](http://bugs.mysql.com/bug.php?id=69812 "the server stalls at function row_vers_build_for_consistent_read"))
Tips 3:如果回滾段類型是INSERT,就完全沒有必要去看Undo日志了,因為一個未提交事務的新插入記錄,對其他事務而言總是不可見的。
Tips 4: 對于聚集索引我們知道其記錄中存有修改該記錄的事務id,我們可以直接判斷是否需要構建老版本(`lock_clust_rec_cons_read_sees`),但對于二級索引記錄,并未存儲事務id,而是每次更新記錄時,同時更新記錄所在的page上的事務id(PAGE_MAX_TRX_ID),如果該事務id對當前事務是可見的,那么就無需去構建老版本了,否則就需要去回表查詢對應的聚集索引記錄,然后判斷可見性(`lock_sec_rec_cons_read_sees`)。
## Purge清理操作
從上面的分析我們可以知道:update_undo產生的日志會放到history list中,當這些舊版本無人訪問時,需要進行清理操作;另外頁內標記刪除的操作也需要從物理上清理掉。后臺Purge線程負責這些工作。
入口函數:`srv_do_purge --> trx_purge`
1. 確認可見性
在開始嘗試purge前,purge線程會先克隆一個最老的活躍視圖(`trx_sys->mvcc->clone_oldest_view`),所有在readview開啟之前提交的事務所做的事務變更都是可以清理的。
2. 獲取需要purge的undo記錄(`trx_purge_attach_undo_recs`)
從history list上讀取多個Undo記錄,并分配到多個purge線程的工作隊列上(`(purge_node_t*) thr->child->undo_recs`),默認一次最多取300個undo記錄,可通過參數innodb_purge_batch_size參數調整。
3. Purge工作線程
當完成任務的分發后,各個工作線程(包括協調線程)開始進行purge操作
入口函數: row_purge_step -> row_purge -> row_purge_record_func
主要包括兩種:一種是記錄直接被標記刪除了,這時候需要物理清理所有的聚集索引和二級索引記錄(`row_purge_record_func`);另一種是聚集索引in-place更新了,但二級索引上的記錄順序可能發生變化,而二級索引的更新總是標記刪除 + 插入,因此需要根據回滾段記錄去檢查二級索引記錄序是否發生變化,并執行清理操作(`row_purge_upd_exist_or_extern`)。
4. 清理history list
從前面的分析我們知道,insert undo在事務提交后,Undo segment 就釋放了。而update undo則加入了history list,為了將這些文件空間回收重用,需要對其進行truncate操作;默認每處理128輪Purge循環后,Purge協調線程需要執行一次purge history List操作。
入口函數:`trx_purge_truncate --> trx_purge_truncate_history`
從回滾段的HISTORY 文件鏈表上開始遍歷釋放Undo log segment,由于history 鏈表是按照trx no有序的,因此遍歷truncate直到完全清除,或者遇到一個還未purge的undo log(trx no比當前purge到的位置更大)時才停止。
關于Purge操作的邏輯實際上還算是比較復雜的代碼模塊,這里只是簡單的介紹了下,以后有時間再展開描述。
## 崩潰恢復
當實例從崩潰中恢復時,需要將活躍的事務從undo中提取出來,對于ACTIVE狀態的事務直接回滾,對于Prepare狀態的事務,如果該事務對應的binlog已經記錄,則提交,否則回滾事務。
實現的流程也比較簡單,首先先做redo (recv_recovery_from_checkpoint_start),undo是受redo 保護的,因此可以從redo中恢復(臨時表undo除外,臨時表undo是不記錄redo的)。
在redo日志應用完成后,初始化完成數據詞典子系統(dict_boot),隨后開始初始化事務子系統(trx_sys_init_at_db_start),undo 段的初始化即在這一步完成。
在初始化undo段時(`trx_sys_init_at_db_start -> trx_rseg_array_init -> ... -> trx_undo_lists_init`),會根據每個回滾段page中的slot是否被使用來恢復對應的undo log,讀取其狀態信息和類型等信息,創建內存結構,并存放到每個回滾段的undo list上。
當初始化完成undo內存對象后,就要據此來恢復崩潰前的事務鏈表了(trx_lists_init_at_db_start),根據每個回滾段的insert_undo_list來恢復插入操作的事務(trx_resurrect_insert),根據update_undo_list來恢復更新事務(tex_resurrect_update),如果既存在插入又存在更新,則只恢復一個事務對象。另外除了恢復事務對象外,還要恢復表鎖及讀寫事務鏈表,從而恢復到崩潰之前的事務場景。
當從Undo恢復崩潰前活躍的事務對象后,會去開啟一個后臺線程來做事務回滾和清理操作(recv_recovery_rollback_active -> trx_rollback_or_clean_all_recovered),對于處于ACTIVE狀態的事務直接回滾,對于既不ACTIVE也非PREPARE狀態的事務,直接則認為其是提交的,直接釋放事務對象。但完成這一步后,理論上事務鏈表上只存在PREPARE狀態的事務。
隨后很快我們進入XA Recover階段,MySQL使用內部XA,即通過Binlog和InnoDB做XA恢復。在初始化完成引擎后,Server層會開始掃描最后一個Binlog文件,搜集其中記錄的XID(MYSQL_BIN_LOG::recover),然后和InnoDB層的事務XID做對比。如果XID已經存在于binlog中了,對應的事務需要提交;否則需要回滾事務。
Tips:為何只需要掃描最后一個binlog文件就可以了? 因為在每次rotate到一個新的binlog文件之前,總是要保證前一個binlog文件中對應的事務都提交并且sync redo到磁盤了,也就是說,前一個binlog文件中的事務在崩潰恢復時肯定是出于提交狀態的。
- 數據庫內核月報目錄
- 數據庫內核月報 - 2016/09
- MySQL · 社區貢獻 · AliSQL那些事兒
- PetaData · 架構體系 · PetaData第二代低成本存儲體系
- MySQL · 社區動態 · MariaDB 10.2 前瞻
- MySQL · 特性分析 · 執行計劃緩存設計與實現
- PgSQL · 最佳實踐 · pg_rman源碼淺析與使用
- MySQL · 捉蟲狀態 · bug分析兩例
- PgSQL · 源碼分析 · PG優化器淺析
- MongoDB · 特性分析· Sharding原理與應用
- PgSQL · 源碼分析 · PG中的無鎖算法和原子操作應用一則
- SQLServer · 最佳實踐 · TEMPDB的設計
- 數據庫內核月報 - 2016/08
- MySQL · 特性分析 ·MySQL 5.7新特性系列四
- PgSQL · PostgreSQL 邏輯流復制技術的秘密
- MySQL · 特性分析 · MyRocks簡介
- GPDB · 特性分析· Greenplum 備份架構
- SQLServer · 最佳實踐 · RDS for SQLServer 2012權限限制提升與改善
- TokuDB · 引擎特性 · REPLACE 語句優化
- MySQL · 專家投稿 · InnoDB物理行中null值的存儲的推斷與驗證
- PgSQL · 實戰經驗 · 旋轉門壓縮算法在PostgreSQL中的實現
- MySQL · 源碼分析 · Query Cache并發處理
- PgSQL · 源碼分析· pg_dump分析
- 數據庫內核月報 - 2016/07
- MySQL · 特性分析 ·MySQL 5.7新特性系列三
- MySQL · 特性分析 · 5.7 代價模型淺析
- PgSQL · 實戰經驗 · 分組TOP性能提升44倍
- MySQL · 源碼分析 · 網絡通信模塊淺析
- MongoDB · 特性分析 · 索引原理
- SQLServer · 特性分析 · XML與JSON應用比較
- MySQL · 最佳實戰 · 審計日志實用案例分析
- MySQL · 性能優化 · 條件下推到物化表
- MySQL · 源碼分析 · Query Cache內部剖析
- MySQL · 捉蟲動態 · 備庫1206錯誤問題說明
- 數據庫內核月報 - 2016/06
- MySQL · 特性分析 · innodb 鎖分裂繼承與遷移
- MySQL · 特性分析 ·MySQL 5.7新特性系列二
- PgSQL · 實戰經驗 · 如何預測Freeze IO風暴
- GPDB · 特性分析· Filespace和Tablespace
- MariaDB · 新特性 · 窗口函數
- MySQL · TokuDB · checkpoint過程
- MySQL · 特性分析 · 內部臨時表
- MySQL · 最佳實踐 · 空間優化
- SQLServer · 最佳實踐 · 數據庫實現大容量插入的幾種方式
- 數據庫內核月報 - 2016/05
- MySQL · 引擎特性 · 基于InnoDB的物理復制實現
- MySQL · 特性分析 · MySQL 5.7新特性系列一
- PostgreSQL · 特性分析 · 邏輯結構和權限體系
- MySQL · 特性分析 · innodb buffer pool相關特性
- PG&GP · 特性分析 · 外部數據導入接口實現分析
- SQLServer · 最佳實踐 · 透明數據加密在SQLServer的應用
- MySQL · TokuDB · 日志子系統和崩潰恢復過程
- MongoDB · 特性分析 · Sharded cluster架構原理
- PostgreSQL · 特性分析 · 統計信息計算方法
- MySQL · 捉蟲動態 · left-join多表導致crash
- 數據庫內核月報 - 2016/04
- MySQL · 參數故事 · innodb_additional_mem_pool_size
- GPDB · 特性分析 · Segment事務一致性與異常處理
- GPDB · 特性分析 · Segment 修復指南
- MySQL · 捉蟲動態 · 并行復制外鍵約束問題二
- PgSQL · 性能優化 · 如何瀟灑的處理每天上百TB的數據增量
- Memcached · 最佳實踐 · 熱點 Key 問題解決方案
- MongoDB · 最佳實踐 · 短連接Auth性能優化
- MySQL · 最佳實踐 · RDS 只讀實例延遲分析
- MySQL · TokuDB · TokuDB索引結構--Fractal Tree
- MySQL · TokuDB · Savepoint漫談
- 數據庫內核月報 - 2016/03
- MySQL · TokuDB · 事務子系統和 MVCC 實現
- MongoDB · 特性分析 · MMAPv1 存儲引擎原理
- PgSQL · 源碼分析 · 優化器邏輯推理
- SQLServer · BUG分析 · Agent 鏈接泄露分析
- Redis · 特性分析 · AOF Rewrite 分析
- MySQL · BUG分析 · Rename table 死鎖分析
- MySQL · 物理備份 · Percona XtraBackup 備份原理
- GPDB · 特性分析· GreenPlum FTS 機制
- MySQL · 答疑解惑 · 備庫Seconds_Behind_Master計算
- MySQL · 答疑解惑 · MySQL 鎖問題最佳實踐
- 數據庫內核月報 - 2016/02
- MySQL · 引擎特性 · InnoDB 文件系統之文件物理結構
- MySQL · 引擎特性 · InnoDB 文件系統之IO系統和內存管理
- MySQL · 特性分析 · InnoDB transaction history
- PgSQL · 會議見聞 · PgConf.Russia 2016 大會總結
- PgSQL · 答疑解惑 · PostgreSQL 9.6 并行查詢實現分析
- MySQL · TokuDB · TokuDB之黑科技工具
- PgSQL · 性能優化 · PostgreSQL TPC-C極限優化玩法
- MariaDB · 版本特性 · MariaDB 的 GTID 介紹
- MySQL · 特性分析 · 線程池
- MySQL · 答疑解惑 · mysqldump tips 兩則
- 數據庫內核月報 - 2016/01
- MySQL · 引擎特性 · InnoDB 事務鎖系統簡介
- GPDB · 特性分析· GreenPlum Primary/Mirror 同步機制
- MySQL · 專家投稿 · MySQL5.7 的 JSON 實現
- MySQL · 特性分析 · 優化器 MRR & BKA
- MySQL · 答疑解惑 · 物理備份死鎖分析
- MySQL · TokuDB · Cachetable 的工作線程和線程池
- MySQL · 特性分析 · drop table的優化
- MySQL · 答疑解惑 · GTID不一致分析
- PgSQL · 特性分析 · Plan Hint
- MariaDB · 社區動態 · MariaDB on Power8 (下)
- 數據庫內核月報 - 2015/12
- MySQL · 引擎特性 · InnoDB 事務子系統介紹
- PgSQL · 特性介紹 · 全文搜索介紹
- MongoDB · 捉蟲動態 · Kill Hang問題排查記錄
- MySQL · 參數優化 ·RDS MySQL參數調優最佳實踐
- PgSQL · 特性分析 · 備庫激活過程分析
- MySQL · TokuDB · 讓Hot Backup更完美
- PgSQL · 答疑解惑 · 表膨脹
- MySQL · 特性分析 · Index Condition Pushdown (ICP)
- MariaDB · 社區動態 · MariaDB on Power8
- MySQL · 特性分析 · 企業版特性一覽
- 數據庫內核月報 - 2015/11
- MySQL · 社區見聞 · OOW 2015 總結 MySQL 篇
- MySQL · 特性分析 · Statement Digest
- PgSQL · 答疑解惑 · PostgreSQL 用戶組權限管理
- MySQL · 特性分析 · MDL 實現分析
- PgSQL · 特性分析 · full page write 機制
- MySQL · 捉蟲動態 · MySQL 外鍵異常分析
- MySQL · 答疑解惑 · MySQL 優化器 range 的代價計算
- MySQL · 捉蟲動態 · ORDER/GROUP BY 導致 mysqld crash
- MySQL · TokuDB · TokuDB 中的行鎖
- MySQL · 捉蟲動態 · order by limit 造成優化器選擇索引錯誤
- 數據庫內核月報 - 2015/10
- MySQL · 引擎特性 · InnoDB 全文索引簡介
- MySQL · 特性分析 · 跟蹤Metadata lock
- MySQL · 答疑解惑 · 索引過濾性太差引起CPU飆高分析
- PgSQL · 特性分析 · PG主備流復制機制
- MySQL · 捉蟲動態 · start slave crash 診斷分析
- MySQL · 捉蟲動態 · 刪除索引導致表無法打開
- PgSQL · 特性分析 · PostgreSQL Aurora方案與DEMO
- TokuDB · 捉蟲動態 · CREATE DATABASE 導致crash問題
- PgSQL · 特性分析 · pg_receivexlog工具解析
- MySQL · 特性分析 · MySQL權限存儲與管理
- 數據庫內核月報 - 2015/09
- MySQL · 引擎特性 · InnoDB Adaptive hash index介紹
- PgSQL · 特性分析 · clog異步提交一致性、原子操作與fsync
- MySQL · 捉蟲動態 · BUG 幾例
- PgSQL · 答疑解惑 · 詭異的函數返回值
- MySQL · 捉蟲動態 · 建表過程中crash造成重建表失敗
- PgSQL · 特性分析 · 談談checkpoint的調度
- MySQL · 特性分析 · 5.6 并行復制恢復實現
- MySQL · 備庫優化 · relay fetch 備庫優化
- MySQL · 特性分析 · 5.6并行復制事件分發機制
- MySQL · TokuDB · 文件目錄談
- 數據庫內核月報 - 2015/08
- MySQL · 社區動態 · InnoDB Page Compression
- PgSQL · 答疑解惑 · RDS中的PostgreSQL備庫延遲原因分析
- MySQL · 社區動態 · MySQL5.6.26 Release Note解讀
- PgSQL · 捉蟲動態 · 執行大SQL語句提示無效的內存申請大小
- MySQL · 社區動態 · MariaDB InnoDB表空間碎片整理
- PgSQL · 答疑解惑 · 歸檔進程cp命令的core文件追查
- MySQL · 答疑解惑 · open file limits
- MySQL · TokuDB · 瘋狂的 filenum++
- MySQL · 功能分析 · 5.6 并行復制實現分析
- MySQL · 功能分析 · MySQL表定義緩存
- 數據庫內核月報 - 2015/07
- MySQL · 引擎特性 · Innodb change buffer介紹
- MySQL · TokuDB · TokuDB Checkpoint機制
- PgSQL · 特性分析 · 時間線解析
- PgSQL · 功能分析 · PostGIS 在 O2O應用中的優勢
- MySQL · 引擎特性 · InnoDB index lock前世今生
- MySQL · 社區動態 · MySQL內存分配支持NUMA
- MySQL · 答疑解惑 · 外鍵刪除bug分析
- MySQL · 引擎特性 · MySQL logical read-ahead
- MySQL · 功能介紹 · binlog拉取速度的控制
- MySQL · 答疑解惑 · 浮點型的顯示問題
- 數據庫內核月報 - 2015/06
- MySQL · 引擎特性 · InnoDB 崩潰恢復過程
- MySQL · 捉蟲動態 · 唯一鍵約束失效
- MySQL · 捉蟲動態 · ALTER IGNORE TABLE導致主備不一致
- MySQL · 答疑解惑 · MySQL Sort 分頁
- MySQL · 答疑解惑 · binlog event 中的 error code
- PgSQL · 功能分析 · Listen/Notify 功能
- MySQL · 捉蟲動態 · 任性的 normal shutdown
- PgSQL · 追根究底 · WAL日志空間的意外增長
- MySQL · 社區動態 · MariaDB Role 體系
- MySQL · TokuDB · TokuDB數據文件大小計算
- 數據庫內核月報 - 2015/05
- MySQL · 引擎特性 · InnoDB redo log漫游
- MySQL · 專家投稿 · MySQL數據庫SYS CPU高的可能性分析
- MySQL · 捉蟲動態 · 5.6 與 5.5 InnoDB 不兼容導致 crash
- MySQL · 答疑解惑 · InnoDB 預讀 VS Oracle 多塊讀
- PgSQL · 社區動態 · 9.5 新功能BRIN索引
- MySQL · 捉蟲動態 · MySQL DDL BUG
- MySQL · 答疑解惑 · set names 都做了什么
- MySQL · 捉蟲動態 · 臨時表操作導致主備不一致
- TokuDB · 引擎特性 · zstd壓縮算法
- MySQL · 答疑解惑 · binlog 位點刷新策略
- 數據庫內核月報 - 2015/04
- MySQL · 引擎特性 · InnoDB undo log 漫游
- TokuDB · 產品新聞 · RDS TokuDB小手冊
- PgSQL · 社區動態 · 說一說PgSQL 9.4.1中的那些安全補丁
- MySQL · 捉蟲動態 · 連接斷開導致XA事務丟失
- MySQL · 捉蟲動態 · GTID下slave_net_timeout值太小問題
- MySQL · 捉蟲動態 · Relay log 中 GTID group 完整性檢測
- MySQL · 答疑釋惑 · UPDATE交換列單表和多表的區別
- MySQL · 捉蟲動態 · 刪被引用索引導致crash
- MySQL · 答疑釋惑 · GTID下auto_position=0時數據不一致
- 數據庫內核月報 - 2015/03
- MySQL · 答疑釋惑· 并發Replace into導致的死鎖分析
- MySQL · 性能優化· 5.7.6 InnoDB page flush 優化
- MySQL · 捉蟲動態· pid file丟失問題分析
- MySQL · 答疑釋惑· using filesort VS using temporary
- MySQL · 優化限制· MySQL index_condition_pushdown
- MySQL · 捉蟲動態·DROP DATABASE外鍵約束的GTID BUG
- MySQL · 答疑釋惑· lower_case_table_names 使用問題
- PgSQL · 特性分析· Logical Decoding探索
- PgSQL · 特性分析· jsonb類型解析
- TokuDB ·引擎機制· TokuDB線程池
- 數據庫內核月報 - 2015/02
- MySQL · 性能優化· InnoDB buffer pool flush策略漫談
- MySQL · 社區動態· 5.6.23 InnoDB相關Bugfix
- PgSQL · 特性分析· Replication Slot
- PgSQL · 特性分析· pg_prewarm
- MySQL · 答疑釋惑· InnoDB丟失自增值
- MySQL · 答疑釋惑· 5.5 和 5.6 時間類型兼容問題
- MySQL · 捉蟲動態· 變量修改導致binlog錯誤
- MariaDB · 特性分析· 表/表空間加密
- MariaDB · 特性分析· Per-query variables
- TokuDB · 特性分析· 日志詳解
- 數據庫內核月報 - 2015/01
- MySQL · 性能優化· Group Commit優化
- MySQL · 新增特性· DDL fast fail
- MySQL · 性能優化· 啟用GTID場景的性能問題及優化
- MySQL · 捉蟲動態· InnoDB自增列重復值問題
- MySQL · 優化改進· 復制性能改進過程
- MySQL · 談古論今· key分區算法演變分析
- MySQL · 捉蟲動態· mysql client crash一例
- MySQL · 捉蟲動態· 設置 gtid_purged 破壞AUTO_POSITION復制協議
- MySQL · 捉蟲動態· replicate filter 和 GTID 一起使用的問題
- TokuDB·特性分析· Optimize Table
- 數據庫內核月報 - 2014/12
- MySQL· 性能優化·5.7 Innodb事務系統
- MySQL· 踩過的坑·5.6 GTID 和存儲引擎那會事
- MySQL· 性能優化·thread pool 原理分析
- MySQL· 性能優化·并行復制外建約束問題
- MySQL· 答疑釋惑·binlog event有序性
- MySQL· 答疑釋惑·server_id為0的Rotate
- MySQL· 性能優化·Bulk Load for CREATE INDEX
- MySQL· 捉蟲動態·Opened tables block read only
- MySQL· 優化改進· GTID啟動優化
- TokuDB· Binary Log Group Commit with TokuDB
- 數據庫內核月報 - 2014/11
- MySQL· 捉蟲動態·OPTIMIZE 不存在的表
- MySQL· 捉蟲動態·SIGHUP 導致 binlog 寫錯
- MySQL· 5.7改進·Recovery改進
- MySQL· 5.7特性·高可用支持
- MySQL· 5.7優化·Metadata Lock子系統的優化
- MySQL· 5.7特性·在線Truncate undo log 表空間
- MySQL· 性能優化·hash_scan 算法的實現解析
- TokuDB· 版本優化· 7.5.0
- TokuDB· 引擎特性· FAST UPDATES
- MariaDB· 性能優化·filesort with small LIMIT optimization
- 數據庫內核月報 - 2014/10
- MySQL· 5.7重構·Optimizer Cost Model
- MySQL· 系統限制·text字段數
- MySQL· 捉蟲動態·binlog重放失敗
- MySQL· 捉蟲動態·從庫OOM
- MySQL· 捉蟲動態·崩潰恢復失敗
- MySQL· 功能改進·InnoDB Warmup特性
- MySQL· 文件結構·告別frm文件
- MariaDB· 新鮮特性·ANALYZE statement 語法
- TokuDB· 主備復制·Read Free Replication
- TokuDB· 引擎特性·壓縮
- 數據庫內核月報 - 2014/09
- MySQL· 捉蟲動態·GTID 和 DELAYED
- MySQL· 限制改進·GTID和升級
- MySQL· 捉蟲動態·GTID 和 binlog_checksum
- MySQL· 引擎差異·create_time in status
- MySQL· 參數故事·thread_concurrency
- MySQL· 捉蟲動態·auto_increment
- MariaDB· 性能優化·Extended Keys
- MariaDB·主備復制·CREATE OR REPLACE
- TokuDB· 參數故事·數據安全和性能
- TokuDB· HA方案·TokuDB熱備
- 數據庫內核月報 - 2014/08
- MySQL· 參數故事·timed_mutexes
- MySQL· 參數故事·innodb_flush_log_at_trx_commit
- MySQL· 捉蟲動態·Count(Distinct) ERROR
- MySQL· 捉蟲動態·mysqldump BUFFER OVERFLOW
- MySQL· 捉蟲動態·long semaphore waits
- MariaDB·分支特性·支持大于16K的InnoDB Page Size
- MariaDB·分支特性·FusionIO特性支持
- TokuDB· 性能優化·Bulk Fetch
- TokuDB· 數據結構·Fractal-Trees與LSM-Trees對比
- TokuDB·社區八卦·TokuDB團隊