91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MYSQL INNODB中通用雙向鏈表怎么實現

發布時間:2021-10-27 16:47:09 來源:億速云 閱讀:146 作者:小新 欄目:MySQL數據庫

這篇文章給大家分享的是有關MYSQL INNODB中通用雙向鏈表怎么實現的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。


源碼在Ut0lst.h中
注意:這里我將鏈表中的實際的串聯的數據叫做數據類比如:lock_t、mem_block_t

鏈表作為一種的非常重要的數據結構,在任何地方都會用到,這里簡單解釋一下innodb雙向
鏈表的實現,讓我們來看看innodb鏈表設計的魅力:
經常在某些結構體中看到
UT_LIST_BASE_NODE_T(mem_block_t) base;
UT_LIST_NODE_T(mem_block_t) list; 

作為最為基本的一種的數據結構innodb中實現得比較精妙涉及到重要的4個C++知識點:
1、仿函數
2、類成員指針
3、類模板
4、函數重載

簡單的說仿函數是調用的時候類似函數調用的形式的類,類成員指針并非一個真正意義上的
指針,而是指向特定類對象相對位置的一個偏移量。
比如如下就是一個仿函數類:

點擊(此處)折疊或打開

  1. template <typename T>

  2. class ShowElemt

  3. {

  4. public:

  5.     ShowElemt()

  6.     {

  7.         n = 0;

  8.     }

  9.     void operator()(T &t)

  10.     {

  11.         n++;

  12.         cout << t << " ";

  13.     }

  14.     void printCount()

  15.     {

  16.         cout << n << endl;

  17.     }

  18. public:

  19.     int n;

  20. };

下面是一個簡單的類成員指針使用,他初始化分為2步在最后給出.:


點擊(此處)折疊或打開

  1. #include<iostream>

  2. using namespace std;


  3. class T

  4. {

  5.   public:

  6.   typedef int uint;

  7.   public:

  8.           int a;

  9.           int b;

  10. };


  11. int main21(void)

  12. {

  13.         T t;

  14.         int T::* t1 = &T::b;//1、成員函數指針 初始化為指向類T成員b的一個指針(成員函數指針指向的是偏移量)

  15.         T* t2 = &t;//t2一個指向類變量t的指針

  16.         t.*t1 = 10;//2、初始化t的t1類成員指針指向的內存空間值為10,實際上就是t.b=10

  17.         cout<<t.*t1<<" "<<t2->*t1<<endl;//相同輸出一個采用對象一個采用對象指針

  18.         {

  19.                 T t3;

  20.                 t3.a=300;

  21.                 t.*t1 = t3.a; //他就是擁有實際內存空間的變量了

  22.         }

  23. }

模板和函數重載就沒有什么好說的了。

接下來我們看看UT_LIST_BASE_NODE_T、UT_LIST_NODE_T分別代表了什么
實際上他們都是宏定義:
#define UT_LIST_BASE_NODE_T(t) ut_list_base<t, ut_list_node t::*>
#define UT_LIST_NODE_T(t) ut_list_node
那么他們的類型出來了實際上就是:
ut_list_base和ut_list_node,我們知道在設計鏈表的時候,通常有一個鏈表頭數據結構,用來存儲
比如鏈表中總共多少元素,鏈表頭,鏈表尾等等特征,但是之前還是想來看看ut_list_node鏈表結構體:

點擊(此處)折疊或打開

  1. template <typename Type>

  2. struct ut_list_node {

  3.     Type*        prev;            /*!< pointer to the previous

  4.                         node, NULL if start of list */

  5.     Type*        next;            /*!< pointer to next node,

  6.                         NULL if end of list */


  7.     void reverse()

  8.     {

  9.         Type*    tmp = prev;

  10.         prev = next;

  11.         next = tmp;

  12.     }

  13. };

非常簡單沒有包含任何固定的數據信息,只是包含了鏈表的前后指針,同時包含了一個成員函數reverse,作為
實現鏈表反轉的基礎,這里也注意到由于沒有包含任何數據信息成員,做到了鏈表和具體數據類之間的剝離。
在鏈表設計的時候通常有2種方式:
1、鏈表結構包含數據類
2、數據類包含鏈表結構
這里INNODB使用了后者,讓鏈表的通用更加方便

再來看看ut_list_base 鏈表頭結構體:

點擊(此處)折疊或打開

  1. template <typename Type, typename NodePtr>

  2. struct ut_list_base {

  3.     typedef Type elem_type;

  4.     typedef NodePtr node_ptr;

  5.     typedef ut_list_node<Type> node_type;


  6.     ulint        count;            /*!< count of nodes in list */

  7.     elem_type*    start;            /*!< pointer to list start,

  8.                         NULL if empty */

  9.     elem_type*    end;            /*!< pointer to list end,

  10.                         NULL if empty */

  11.     node_ptr    node;            /*!< Pointer to member field

  12.                         that is used as a link node */

  13. #ifdef UNIV_DEBUG

  14.     ulint        init;            /*!< UT_LIST_INITIALISED if

  15.                         the list was initialised with

  16.                         UT_LIST_INIT() */

  17. #endif /* UNIV_DEBUG */


  18.     void reverse()

  19.     {

  20.         Type*    tmp = start;

  21.         start = end;

  22.         end = tmp;

  23.     }

  24. };

這里再來解釋一下:
在類的內部進行了3種類型的typedef,分別是:
typedef Type elem_type;:具體的類型比如lock_t、mem_block_t。
typedef NodePtr node_ptr; :和模板聲明中的ut_list_node t::* 聯合起來看,實際上他是一個
類成員指針,他指向的會是特定數據類比如lock_t、mem_block_t中特定的一個成員,這個成員一定是
ut_list_node類型的也就是UT_LIST_NODE_T(t)類型的,其中t當然也就是數據類本身,這也是整個
設計中的精髓。
typedef ut_list_node node_type; :和前面的ut_list_node聯合起來看,就能知道
它是一個特定類型的節點類型比如lock_t、mem_block_t。
剩下的就是類成員了:
ulint count;:鏈表中的有多少元素
elem_type* start;:具體數據類元素的起始位置指針
elem_type* end;:具體數據類元素的最后位置指針
node_ptr node;:這里使用了剛才說的typedef NodePtr node_ptr來定義成員node,那么我們可以猜測他是指向
特定數據類對象中鏈表結構元素的成員指針,其實的確如此:
如lock_t

點擊(此處)折疊或打開

  1. /** Lock struct; protected by lock_sys->mutex */

  2. struct lock_t {

  3.     trx_t*        trx;        /*!< transaction owning the

  4.                     lock */

  5.     UT_LIST_NODE_T(lock_t)

  6.             trx_locks;    /*!< list of the locks of the

  7.                     transaction */

  8. ..........


剩下還有一個關鍵的仿函數:

點擊(此處)折疊或打開

  1. template <typename Type> //一元謂詞仿函數

  2. struct GenericGetNode {

  3.     typedef ut_list_node<Type> node_type;

  4.     GenericGetNode(node_type Type::* node) : m_node(node) {}

  5.     node_type& operator() (Type& elem)

  6.     {

  7.         return(elem.*m_node);

  8.     }

  9.     node_type    Type::*m_node;

  10. };

這里解釋一下這個仿函數類:
typedef ut_list_node node_type;和前面的ut_list_node聯合起來看,就能知道
它是一個特定類型的節點類型比如lock_t、mem_block_t。
GenericGetNode(node_type Type::* node) : m_node(node) {} :有參構造函數,通過輸入一個指向特定數據節點的
ut_list_node(UT_LIST_NODE_T(t))成員的值node進行初始化元素m_node。但是這里只是指向了類成員有了偏移量,但是并沒有初始化內存空間,具體的初始化
內存空間在實現函數中。
node_type& operator() (Type& elem) :這里就是仿函數,重載了()運算符,接受一個特定節點類型比如lock_t、mem_block_t
的一個引用輸入,然后返回一個類成員指針的引用,如果是lock_t那么返回的將是trx_locks,那么我們就能夠使用它
trx_locks.prev
在鏈表實現中中包含很多方法大概如下:
UT_LIST_INIT:初始化一個鏈表、是一個宏定義
ut_list_prepend:頭插法插入鏈表
ut_list_append:尾插法插入鏈表
ut_list_insert:將某個元素插入到某個元素之后
ut_list_remove:刪除某個節點
ut_list_reverse:鏈表反向
ut_list_move_to_front:將指定的元素放到頭部
好了到這里我們解釋了關鍵鏈表數據結構下面我們通過一段innodb代碼來分析一下,這里我們
我們只是關注鏈表操作所以如下,這里涉及到初始化和尾插法加入鏈表
UT_LIST_BASE_NODE_T(lock_t) old_locks;
UT_LIST_INIT(old_locks, &lock_t::trx_locks);
lock_t* old_lock = lock_rec_copy(lock, heap);
UT_LIST_ADD_LAST(old_locks, old_lock);

我們來具體解釋一下步驟:
1、UT_LIST_BASE_NODE_T(lock_t) old_locks;定義old_locks為一個鏈表頭對象。
2、UT_LIST_INIT(old_locks, &lock_t::trx_locks);進行初始化,這里UT_LIST_INIT是一個宏

點擊(此處)折疊或打開

  1. #define UT_LIST_INIT(b, pmf)    \

  2. {    \

  3. (b).count = 0;    \

  4. (b).start = 0;    \

  5. (b).end = 0;    \

  6. (b).node = pmf;    \

  7. UT_LIST_INITIALISE(b);    \

  8. }



非常簡單設置全部指針都是NULL,并且初始化node類成員指針指向&lock_t::trx_locks。
3、lock_t* old_lock = lock_rec_copy(lock, heap);我們先不深究這里面,但是他肯定是一種拷貝,完成后他返回一個lock_t*的指針
old_lock
4、接下來就是加入節點,這是一個重頭戲,會應用到前面全部的知識。
UT_LIST_ADD_LAST(old_locks, old_lock);
實際上他是一共宏定義
#define UT_LIST_ADD_LAST(LIST, ELEM) ut_list_append(LIST, ELEM)
在經過函數重載調用后實際上他會調用

點擊(此處)折疊或打開

  1. template <typename List>

  2. void

  3. ut_list_append(

  4. List&    list,

  5. typename List::elem_type*    elem)

  6. {

  7. ut_list_append(

  8. list, elem,

  9. GenericGetNode<typename List::elem_type>(list.node));

  10. }



然后調用:

點擊(此處)折疊或打開

  1. template <typename List, typename Functor>

  2. void

  3. ut_list_append(

  4. List&    list,

  5. typename List::elem_type*    elem,

  6. Functor    get_node)

  7. {

  8. typename List::node_type&    node = get_node(*elem);



  9. UT_LIST_IS_INITIALISED(list);



  10. node.next = 0;

  11. node.prev = list.end;



  12. if (list.end != 0) {

  13. typename List::node_type&    base_node = get_node(*list.end);



  14. ut_ad(list.end != elem);



  15. base_node.next = elem;

  16. }



  17. list.end = elem;



  18. if (list.start == 0) {

  19. list.start = elem;

  20. }

  21. ++list.count;

  22. }

詳細描述一下:
首先看一下:
template
void
ut_list_append(
List& list,
typename List::elem_type* elem)
{
ut_list_append(
list, elem,
GenericGetNode(list.node));
}
這里list就是我們初始化的old_locks類型為UT_LIST_BASE_NODE_T(lock_t),elem就是我們copy出來的一個
指向lock_t*的指針old_lock其類型當然也就是UT_LIST_BASE_NODE_T(lock_t)::elem_type*類型實際上就是
lock_t*類型繞了一大圈。
而GenericGetNode(list.node)雖然很長但是我們可以清楚的明白他是
調用的構造函數,生成一個匿名對象,typename List::elem_type是用到了ut_list_base定義的類型
elem_type就是一個UT_LIST_BASE_NODE_T(lock_t)::elem_type類型其實就是lock_t類型,而list.node
實際上就是node_ptr類型,初始化已經被定義為&lock_t::trx_locks

接下來由于函數重載的函數調用了
ut_list_append(
List& list,
typename List::elem_type* elem,
Functor get_node)
我們來看看。
typename List::node_type& node = get_node(*elem);
將List(old_locks)中的node成員函數指針進行初始化他指向了old_lock這是結構實際鏈表結構的位置。
接下來node.next nodex.prev將是可用的了
node.next = 0;
node.prev = list.end;
將節點的后指針設置為NULL,前指針當然設置為list.end的位置
這里也看到他確實放到末尾
if (list.end != 0) {
typename List::node_type& base_node = get_node(*list.end);
ut_ad(list.end != elem);
base_node.next = elem;
}
如果鏈表不為空,這里再次獲得end節點的位置存放到base_node中,
當然也就要將base_node.next設置為我們新加入的節點的指針。
list.end = elem;
將鏈表頭結構的end指針放到我們新插入的elem中。
if (list.start == 0) {
list.start = elem;
}
如果list的start指針為空代表鏈表為空,那么還需要將start指針指向elem
最后
++list.count;
不解釋了。

從整個鏈表的實現來看仿函數是其中的一個重點,他是一個橋梁其主要分為兩步:
1、初始化指向一個類的成員函數,這是指定他的類型,獲得他的偏移量
2、初始化指向某一個元素,這是獲得他的內存空間地址基地址
有了基地址+偏移量就能夠找到實際的元素了。

感謝各位的閱讀!關于“MYSQL INNODB中通用雙向鏈表怎么實現”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

嫩江县| 隆尧县| 织金县| 平顺县| 伊春市| 琼海市| 金秀| 新津县| 扬中市| 工布江达县| 琼中| 灯塔市| 南溪县| 德阳市| 镇沅| 阿合奇县| 中牟县| 兖州市| 页游| 石泉县| 塘沽区| 广宗县| 彭州市| 山西省| 宝坻区| 涿州市| 乐都县| 镇坪县| 长葛市| 枝江市| 万全县| 临潭县| 荆门市| 浑源县| 南澳县| 沛县| 鄂温| 阳谷县| 石嘴山市| 玉林市| 庄浪县|