MySQL系列:innodb源碼分析之基礎數據結構

近一年來一直在分析關於數據庫相關的源碼,前段時間分析了levelDB的實現和BeansDB的實現,這兩個數據庫網絡上分析的文章很多,也都比較分析的比較深,所以也就沒有太多必要重複勞動。最近開始關注關係數據庫和MYSQL,當然主要還是數據庫存儲引擎,首先我還是從innodb這個最流行的開源關係數據庫引擎着手來逐步分析和理解。我一般分析源碼的時候都是從基礎的數據結構和算法逐步往上分析,遇到不明白的地方,自己按照源碼重新輸入一遍並做對應的單元測試,這樣便於理解。對於Innodb這樣的大項目,也應該如此,以後我會逐步將具體的細節和實現寫到BLOG上。我分析Innodb是以MySQL-3.23爲藍本作爲分析對象,然後再去比較5.6版本的改動來做分析的。這樣做有個好處就是先理解相對基礎的代碼容易,在有了基本概念後再去理解最新的改動。以下是我對innodb基礎的數據結構和算法的理解。

1.vector

innodb的vector是個動態數組的數據結構,和c++的STL用法相似,值得一提的是vector的內存分配可以通過函數指針來指定是從heap內存池堆上分配內存還是用OS自帶的malloc來分配內存。內存分配器的結構爲:
        struct ib_alloc_t {
             ib_mem_alloc_t	mem_malloc; 				//分配器的malloc函數指針
             ib_mem_free_t	mem_release;  				//分配器的free函數指針
             ib_mem_resize_t	mem_resize;  				//分配器的重新定義堆大小指針
             void*	 arg;     					//堆句柄,如果是系統的malloc方式,這個值爲NULL
<span style="white-space:pre">	</span>};
vector內部集成了排序功能函數,其排序的算法是通過qsort(快速)來進行排序。
vector內存結構:
  


2.內存list

innodb的list數據結構是個標準的雙向鏈表結構,ib_list_node_t當中有指向前一個node的prev和指向後一個
node的next,list的內存分配可以通過heap內存堆來分配,也可以通過系統的malloc來分配。就看是採用
ib_list_create_heap來創建list愛是永ib_list_create來創建list。但是內部的ib_list_node_t的內存分配是通過
heap來分配的。

ist的內存結構:


3.FIFO-queue

innodb的FIFO queue是個多線程的消息隊列,可以有多個線程向queue中添加消息,可有多個線程同時讀取queue中的消息並進行處理。queue的mutex是保證同時只有一個線程在操作(讀或者寫)queue的items鏈表,os_event是寫線程完成後通知所有讀線程可以進行queue的讀事件,也就是說,只有向queue寫完成一個消息,纔會發送event信號給讀線程。queue的消息緩衝區是採用ib_list_t來做存儲的,一般寫的時候寫在list的最後,而讀總是讀取list的第一個。queue處理提供一直讀取到消息爲止的方法以外,也提供最長等待讀取消息的方法,這樣讀取線程沒有必要一直等待消息,可以在等待一段時間後去處理其他的任務。其C結構定義如下:
struct ib_wqueue_t
{
	ib_mutex_t	mutex; /*互斥量*/
	ib_list_t*	items; /*用list作爲queue的載體*/
	os_event_t	event; /*信號量*/
};

4.哈希表

innodb中的哈希表的基本構造和傳統的哈希表的構造是相似的,不同的就是innodb的哈希表採用的是自定義鏈式桶結構,而沒有采用每個桶單元用傳統的list來做碰撞管理。由於這個特性,innodb中的哈希表操作採用了一系列操作宏來做操作,這樣做的目的是爲了能泛型的對哈希表做操作,因爲在innodb中,除了操作內存中的數據以外,還會操作隱射硬盤中的數據。以下是innodb的操作宏:
        HASH_INSERT                                    插入操作
        HASH_DELETE                                    刪除操作
        HASH_GET_FIRST                               獲取指定HASH key對應cell的第一個數據單元
        HASH_GET_NEXT                                獲取cell_node對應的下一個單元
        HASH_SEARCH                                   查找對應key的值
        HASH_SEARCH_ALL                            遍歷整個hash table並將每個數據單元爲參數執行ASSERTION操作
        HASH_DELETE_AND_COMPACT        刪除操作並且優化和調整heap堆上的內存分配佈局,使得heap效率更高
        HASH_MIGRATE                                 將OLD_TABLE的數據單元合併到NEW_TABLE當中
這些宏在調用的時候都會指定數據的類型和Next函數名。
innodb的哈希表在多線程併發模式下也提供cell級粒度的鎖,有mutex類型的鎖,也有rw_lock類型的鎖。在hash_create_sync_obj_func函數調用過程中,會創建一個n_sync_obj的鎖數據單元,n_sync_obj必須是2的N次方。也就是說如果n_sync_obj = 8, 哈希表的n_cells = 19,那就至少兩個cell公用一個鎖。這是其他哈希表無法比擬的。
以下是hash table的結構定義:
struct hash_table_t
{
	enum hash_table_sync_t	type;		/*hash table的同步類型*/
	ulint			n_cells;	/*hash桶個數*/
	hash_cell_t*		array;		/*hash桶數組*/
#ifndef UNIV_HOTBACKUP
	ulint			n_sync_obj;
	union{ /*同步鎖*/
		ib_mutex_t*	mutexes;
		rw_lock_t*	rw_locks;
	}sync_obj;
	/*heaps的單元個數和n_sync_obj一樣*/
	mem_heap_t**		heaps;
#endif
	mem_heap_t*		heap;
	ulint			magic_n;	/*校驗魔法字*/
#endif
};

5.小結

Innodb還有其他的一些數據結構,例如最小堆,這些都是通用的封裝,也就不做過多的描述,在可以去看看innodb的源碼相關就可以。innodb在定義數據結構的時候做了特殊的處理,例如對線程併發的控制,對內存分配的控制。這樣做的目的是爲了統一的管理。innodb的代碼是C的,但支持C++。裏面並沒有使用STL這種傳統的數據結構和算法,很大程度上是適合性的問題。據說MYSQL 5.7開始大量使用boost 和STL。個人感覺STL還勉強,使用boost有點步子邁大了的感覺。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章