最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL源碼中的查詢邏輯詳解

 更新時間:2025年11月03日 09:14:47   作者:方二華  
MySQL使用InnoDB引擎進行數(shù)據(jù)查詢,通過row_search_mvcc方法實現(xiàn)單條查詢和全表掃描,查詢過程中考慮了鎖的處理、事務(wù)管理和多版本并發(fā)控制(MVCC),接下來通過本文給大家介紹MySQL源碼中的查詢邏輯,感興趣的朋友跟隨小編一起看看吧

mysql的架構(gòu)采用引擎分離的模式,innodb引擎負責最終的數(shù)據(jù)查詢。

mysql解析sql后,調(diào)用innodb進行搜索數(shù)據(jù),這個過程并不是mysql一次性調(diào)用,然后等待innodb返回所有的結(jié)果。

innodb提供了一個查詢方法,每次只查詢一行記錄,然后返回記錄,直到查詢不出結(jié)果。

mysql innodb的核心查詢邏輯

方法位置:

storage/innobase/row/row0sel.cc
row_search_mvcc
單條查詢,每次只查詢一條,即使是全表掃描,也是用這個方法一條一條的讀出來

參數(shù):

buf 最終結(jié)果將存儲到這個buf
mode 查詢模式,B+樹搜索/葉子節(jié)點正向遍歷/葉子節(jié)點逆向遍歷 PAGE_CUR_GE PAGE_CUR_L PAGE_CUR_G
prebuilt 表信息和查詢條件,其中的search_tuple就是要條件索引條件字段和值
prebuilt->search_tuple 就是要條件索引條件字段和值
prebuilt->pcur 指向上一條查詢出的記錄,
match_mode 精確匹配, prefix匹配(mysql的最左匹配原則)
direction 升序或逆序查詢

如果是精確匹配,并且如果是聯(lián)合索引,索引字段不為空。 那么結(jié)果只可能是0或1個

if (match_mode == ROW_SEL_EXACT
    && dict_index_is_unique(index)
    && dtuple_get_n_fields(search_tuple)
    == dict_index_get_n_unique(index)
    && (dict_index_is_clust(index)
	|| !dtuple_contains_null(search_tuple))) {
搜索結(jié)果是唯一的
unique_search = TRUE;

開啟事務(wù)

trx_start_if_not_started(trx, false);
// 如果隔離級別是讀未提交或讀已提交,不用加gap鎖
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
    && prebuilt->select_lock_type != LOCK_NONE
    && trx->mysql_thd != NULL
    && thd_is_select(trx->mysql_thd)) {

	set_also_gap_locks = FALSE;
}

升序降序

direction==0 默認或1(ROW_SEL_NEXT就是ASC)
moves_up = TRUE 設(shè)為升級遍歷
if (direction == 0) {
	if (mode == PAGE_CUR_GE
	    || mode == PAGE_CUR_G
	    || mode >= PAGE_CUR_CONTAIN) {
		moves_up = TRUE;
	}
} else if (direction == ROW_SEL_NEXT) {
	moves_up = TRUE;
}

取第一個索引,第一個索引必定是聚集索引
clust_index = dict_table_get_first_index(index->table);

事務(wù)read_view

若當前查詢不需要加鎖LOCK_NONE,
那就只需要保證一致性讀,也就是最普通的select查詢,
需要生成一個read_view,實現(xiàn)一致性讀

if (prebuilt->select_lock_type == LOCK_NONE) {
	if (!srv_read_only_mode) {
		trx_assign_read_view(trx);
	}
	prebuilt->sql_stat_start = FALSE;
}

S鎖

需要加鎖的情況
這里先加了表鎖, 沒有直接加行鎖
若是S鎖,則加IS鎖,否則加IX鎖

else {
            // 加鎖讀,先加意向表鎖
            // 加表鎖,要么是LOCK_IS,要么是LOCK_IX
	err = lock_table(0, index->table,
			 prebuilt->select_lock_type == LOCK_S
			 ? LOCK_IS : LOCK_IX, thr);
	if (err != DB_SUCCESS) {
		table_lock_waited = TRUE;
		goto lock_table_wait;
	}
	prebuilt->sql_stat_start = FALSE;
}

判斷查詢條件,是否在索引范圍內(nèi)
這決定是否走索引查詢

索引查詢

if (dtuple_get_n_fields(search_tuple) > 0) { … }

利用索引遍歷B+樹
search_tuple就是查詢目標值,根據(jù)參數(shù)查詢到第0層(葉子節(jié)點)

btr_pcur_open_with_no_init(index, search_tuple, mode,
				   BTR_SEARCH_LEAF,
				   pcur, 0, &mtr);
	主要邏輯就是遍歷樹 
	btr_cur_search_to_nth_level 找到第n層
		先找到索引樹的根頁,加載到內(nèi)存 
		page_cur_search_with_match(index,tuple,mode) 在當前頁進行查找(從根頁開始,深度優(yōu)先遍歷)
		node_ptr = page_cur_get_rec(page_cursor) 找到下一個node
		loop 循環(huán)遍歷, 直到找到目標層或第0層,
		page_cur_search_with_match_bytes()  找到目標行

根據(jù)搜索到的指針pcur,讀取找到的行記錄
rec = btr_pcur_get_rec(pcur)
在索引遍歷時,如果是降序遍歷,并且還需要加鎖
就會額外處理,找到當前行的下一行,增加GAP鎖
若當前id=100,下一個110,則會在100和110的間隙加間隙鎖。

if (!moves_up
    && !page_rec_is_supremum(rec)
    && set_also_gap_locks
    && !(srv_locks_unsafe_for_binlog
	 || trx->isolation_level <= TRX_ISO_READ_COMMITTED)
    && prebuilt->select_lock_type != LOCK_NONE
    && !dict_index_is_spatial(index)) {

	const rec_t*	next_rec = page_rec_get_next_const(rec);

	offsets = rec_get_offsets(next_rec, index, offsets,
				  ULINT_UNDEFINED, &heap);
	err = sel_set_rec_lock(pcur,
			       next_rec, index, offsets,
			       prebuilt->select_lock_type,
			       LOCK_GAP, thr, &mtr);

	switch (err) {
	case DB_SUCCESS_LOCKED_REC:
		err = DB_SUCCESS;
	case DB_SUCCESS:
		break;
	default:
		goto lock_wait_or_error;
	}
}

非索引查詢

如果不走索引,那就進行全表掃描, 利用葉子節(jié)點的雙向鏈表遍歷。
PAGE_CUR_G 從B+樹葉子節(jié)點最左側(cè)開始掃描(最小節(jié)點升序遍歷)
PAGE_CUR_L 從B+樹葉子節(jié)點最右側(cè)開始掃描(最大節(jié)點降序遍歷)

else if (mode == PAGE_CUR_G || mode == PAGE_CUR_L) {
    btr_pcur_open_at_index_side(
		mode == PAGE_CUR_G, index, BTR_SEARCH_LEAF,
		pcur, false, 0, &mtr);
}

row_search_mvcc方法內(nèi)容非常多,主要的查詢邏輯就是這些。
還有鎖的處理,事務(wù)的處理,mvcc,將在后面分析。

各種查詢條件對應的查詢邏輯

范圍查詢

小于查詢

select * from t where id<10;

參數(shù)mode為 PAGE_CUR_G
search_tuple為空,取出根頁的最小記錄,查出最小頁,最小記錄。
然后根據(jù)葉子節(jié)點的鏈表,依次取出所有符合條件的記錄。

if(mode==PAGE_CUR_G || mode==PAGE_CUR_L)
	btr_pcur_open_at_index_side() 
btr_pcur_open_at_index_side方法將從葉子節(jié)點的最左或最右取值
mode決定了它從哪邊取值
PAGE_CUR_G從最左側(cè)(最?。┤≈担琍AGE_CUR_L從最右側(cè)(最大)取值。

終結(jié)條件
compare_key(end_range)
找到大于end_range時,符合判斷跳出循環(huán)。

找到符合條件的記錄時,會判斷當前版本是不是readView可見
如果不可見,將從版本鏈的上一級取出。
lock_clust_rec_cons_read_sees(rec,index,offsets,trx_get_read_view(trx))

判斷找到的記錄是不是被刪了
rec_get_deleted_flag(rec, comp)

轉(zhuǎn)格式,存儲引擎是innodb,取出來的數(shù)據(jù)是innodb格式,需要轉(zhuǎn)成mysql
row_sel_store_mysql_rec()

大于查詢

select * from t where id>10;
和小于不同,search_tuple有值,也就是需要先查詢出“id=10"的記錄。
然后從"id=10"的記錄往后遍歷,剩余部分同小于查詢

全表掃描

select * from t;

同小于查詢 mode類型為 PAGE_CUR_G ,只是沒有終結(jié)條件

升序查詢

聚集索引默認就是按升序存儲的,全表掃描就是升序查詢
MOVES_UP = true 升序

降序查詢

mode類型為 PAGE_CUR_L, 從葉子節(jié)點鏈表的最右側(cè)取值
然后從左往右遍歷
MOVES_UP = false 降序

葉子節(jié)點頁存在雙向鏈表指針,從而可以快速取出數(shù)據(jù)。
但記錄與記錄之間只有單向鏈表關(guān)系。
如果是升序取數(shù)據(jù),可以利用記錄的鏈表關(guān)系。
如果是降序,只能通過頁之間的鏈表關(guān)系,頁內(nèi)的記錄就無法通過鏈表依次取出,會比升序要復雜一些。
btr_pcur_move_to_pre()
btr_pcur_move_to_prev_on_page()
btr_pcur_move_to_pre(btr_pcur_get_page_cur(cursor))
頁內(nèi)降序查詢的辦法比較笨,從頁內(nèi)最小記錄開始遍歷,匹配到目標值,回退到上一個,就是反向遍歷。
如存在1-10的一組數(shù)據(jù), 當前值10,從1開始遍歷,找到10,退回到9,返回
再從1開始遍歷,找到9,退回到8,返回

范圍+降序查詢

原本的小于查詢從最小頁開始遍歷,有降序條件后,將從范圍值開始降序遍歷
原本的大于查詢從范圍值開始遍歷,有降序條件后,將從最大頁開始降序遍歷,直到終結(jié)條件。

in查詢

原理頁就是逐個按主鍵取。
但它返回的數(shù)據(jù)是升序的,這說明它在查詢前就把in里面的條件排序過了再逐個查詢。

輔助索引

通用也是走row_search_mvcc 不同的是傳入的index索引是輔助索引,search_tuple為輔助索引值 匹配到輔助索引的記錄后,再進行回表查詢。 回表查詢,因為輔助索引的葉子節(jié)點沒有記錄完整的數(shù)據(jù),只存儲了主鍵id和輔助索引值,如果select的值超出了輔助索引存的值,就需要從聚集索引里面取值,也就是再進行一次或多次主鍵索引查詢,查出完整的數(shù)據(jù)。 如果輔助索引是唯一索引,只會發(fā)生一次回表。 如果輔助索引不是唯一索引,就有可能多次回表。 ·

row_sel_get_clust_rec_for_mysql(prebuilt, index, rec,
					      thr, &clust_rec,
					      &offsets, &heap,
					      need_vrow ? &vrow : NULL,
					      &mtr);
這個方法最終調(diào)用前面用到的遍歷B+樹的方法從聚集索引查詢出數(shù)據(jù)
拿到聚集索引
clust_index = dict_table_get_first_index(sec_index->table);
遍歷聚集索引B+樹
btr_pcur_open_with_no_init

mysql通過bitmap來記錄當前表需要獲取的字段

到此這篇關(guān)于MySQL源碼中的查詢邏輯的文章就介紹到這了,更多相關(guān)mysql查詢邏輯內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysql 8.0.12 快速安裝教程

    mysql 8.0.12 快速安裝教程

    這篇文章主要為大家詳細介紹了mysql 8.0.12的快速安裝教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • 一篇文章帶你了解SQL之CASE WHEN用法詳解

    一篇文章帶你了解SQL之CASE WHEN用法詳解

    本文介紹下,在mysql數(shù)據(jù)庫中,有關(guān)case when語句的用法,介紹了case when語句的基礎(chǔ)知識,并提供了相關(guān)實例,供大家學習參考,有需要的朋友不要錯過
    2021-08-08
  • MySQL部署時提示Table mysql.plugin doesn’t exist的解決方法

    MySQL部署時提示Table mysql.plugin doesn’t exist的解決方法

    這篇文章主要介紹了MySQL部署時Table mysql.plugin doesn't exist的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • mysql優(yōu)化配置參數(shù)

    mysql優(yōu)化配置參數(shù)

    mysql安裝好需要優(yōu)化配置一下,打開c:\windows\my.ini文件
    2011-03-03
  • MYSQL代碼 定期備份Mysql數(shù)據(jù)庫

    MYSQL代碼 定期備份Mysql數(shù)據(jù)庫

    Mysql自動備份腳本供大家參考,實現(xiàn)了定期備份Mysql數(shù)據(jù)庫,并且可以選在在每周的一天做指定目錄下文件的全面?zhèn)浞?,備份文件自動上傳到你指定的FTP上,保證了備份的可靠性。
    2009-04-04
  • 一次mysql遷移至OceanBase操作實戰(zhàn)記錄

    一次mysql遷移至OceanBase操作實戰(zhàn)記錄

    經(jīng)過一段時間的學習,我對OceanBase有了一些基本的了解,這篇文章主要介紹了一次mysql遷移至OceanBase操作實戰(zhàn)記錄,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-12-12
  • ubuntu kylin 14.10下多個mysql 5.7.14安裝教程

    ubuntu kylin 14.10下多個mysql 5.7.14安裝教程

    這篇文章主要為大家分享了ubuntu kylin 14.10下多個mysql 5.7.14安裝教程,感興趣的朋友可以參考一下
    2016-08-08
  • mysql行鎖(for update)解決高并發(fā)問題

    mysql行鎖(for update)解決高并發(fā)問題

    這篇文章主要介紹了mysql行鎖(for update)解決高并發(fā)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • MySql?InnoDB存儲引擎之Buffer?Pool運行原理講解

    MySql?InnoDB存儲引擎之Buffer?Pool運行原理講解

    緩沖池是用于存儲InnoDB表,索引和其他輔助緩沖區(qū)的緩存數(shù)據(jù)的內(nèi)存區(qū)域。緩沖池的大小對于系統(tǒng)性能很重要。更大的緩沖池可以減少磁盤I/O來多次訪問同一表數(shù)據(jù)。在專用數(shù)據(jù)庫服務(wù)器上,可以將緩沖池大小設(shè)置為計算機物理內(nèi)存大小的百分之80
    2023-01-01
  • 老鳥帶你開發(fā)專業(yè)規(guī)范的MySQL啟動腳本

    老鳥帶你開發(fā)專業(yè)規(guī)范的MySQL啟動腳本

    這篇文章主要介紹了老鳥帶你開發(fā)專業(yè)規(guī)范的MySQL啟動腳本,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09

最新評論

彭泽县| 镇雄县| 桃园市| 连山| 南充市| 甘孜| 和林格尔县| 三亚市| 栾城县| 宜兰市| 涞水县| 上犹县| 枞阳县| 溧阳市| 斗六市| 定西市| 青海省| 临安市| 绥芬河市| 友谊县| 东阳市| 扎囊县| 即墨市| 新余市| 马关县| 阆中市| 麦盖提县| 五原县| 湖南省| 霍城县| 石城县| 巴塘县| 景宁| 辉县市| 隆子县| 长治市| 瑞昌市| 武定县| 呼和浩特市| 元氏县| 乌鲁木齐县|