国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

帶你讀 MySQL 源碼:Limit,Offset

數據庫 MySQL
從 LimitOffsetIterator::Read() 的實現邏輯來看,offset 越大,讀取之后被丟棄的記錄就越多,讀取這些記錄所做的都是無用功。

我一直想寫 MySQL 源碼分析文章,希望能夠達成 2 個目標:

  • 不想研究源碼的朋友,可以通過文章了解 MySQL 常用功能的實現邏輯,做到知其然,也知其所以然。
  • 想研究源碼的朋友,能夠以文章為切入點,邁進 MySQL 源碼研究之門。

目標是明確的,任務是艱巨的。

MySQL 源碼數量龐大,各種功能的代碼盤根錯節,相互交織在一起,形成一張復雜的網。

想要把這張網中的某些部分拎出來寫成文章,還要做到通俗易懂,這并不是件容易的事,我也就遲遲沒有動手。

萬事開頭難,但是再難,總得開始,才能有后續,所以,就有了這篇文章。

寫文章是件費時費力的事,寫出來了總希望有更多人看,否則就沒有寫下去的動力了。

對 MySQL 源碼感興趣的朋友們,如果想看到源碼分析系列的更多文章,請幫忙把文章傳播出去,分享給更多人。

嘮叨完前因后果,再說說我準備怎么寫這個系列文章:

  • 我會挑一些常用功能,每篇文章介紹一個單點功能的源碼,從簡單功能開始,逐漸過渡到復雜功能。
  • 每篇文章只會介紹核心源碼邏輯,源碼之中增加注釋,源碼之外盡可能用文字展開介紹源碼邏輯,以幫助大家更好的理解源碼。
  • 每篇文章不會太長,如果功能復雜導致內容太長,我會拆分文章,盡量降低大家的閱讀負擔。

接下來,我們開始源碼分析系列的第 1 篇文章。

本文內容基于 MySQL 8.0.32 源碼。

正文

1、準備工作

創建測試表:

CREATE TABLE `t1` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`str1` varchar(255) NOT NULL DEFAULT '',
`i1` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

插入測試數據:

INSERT INTO t1(id, str1, i1) VALUES
(1, 's1', 10),
(2, 's2', 20),
(3, 's3', 30),
(4, 's4', 40),
(5, 's5', 50),
(6, 's6', 60),
(7, 's7', 70),
(8, 's8', 80);

示例 SQL:

select * from t1 limit 5, 2

2、整體介紹

我們先通過 explain 來看一下執行計劃:

圖片

從 explain 輸出可以看到,執行計劃比較簡單,SQL 執行過程包含 2 個迭代器:

  • Limit/Offset,對應 LimitOffsetIterator 迭代器。
  • Table scan,對應 TableScanIterator 迭代器。

代碼執行時堆棧如下:

| > handle_connection(void*) sql/conn_handler/connection_handler_per_thread.cc:302
| + > do_command(THD*) sql/sql_parse.cc:1439
| + - > dispatch_command(...) sql/sql_parse.cc:2036
| + - x > dispatch_sql_command(THD*, Parser_state*) sql/sql_parse.cc:5322
| + - x = > mysql_execute_command(THD*, bool) sql/sql_parse.cc:4688
| + - x = | > Sql_cmd_dml::execute(THD*) sql/sql_select.cc:578
| + - x = | + > Sql_cmd_dml::execute_inner(THD*) sql/sql_select.cc:778
| + - x = | + - > Query_expression::execute(THD*) sql/sql_union.cc:1823
| + - x = | + - x > // 查詢入口
| + - x = | + - x > Query_expression::ExecuteIteratorQuery(THD*) sql/sql_union.cc:1770
| + - x = | + - x = > // 實現 limit, offset
| + - x = | + - x = > LimitOffsetIterator::Read() sql/iterators/composite_iterators.cc:128
| + - x = | + - x = | > // 從存儲引擎讀取一條記錄
| + - x = | + - x = | > TableScanIterator::Read() sql/iterators/basic_row_iterators.cc:218

3、源碼分析

TableScanIterator 迭代器用于從存儲引擎讀取記錄,留到以后的文章介紹。

limit, offset 由 LimitOffsetIterator 迭代器實現,我們會介紹兩個方法的代碼:

  • Query_expression::ExecuteIteratorQuery(THD*),這是查詢入口方法,介紹了它,流程才算完整。
  • LimitOffsetIterator::Read(),limit, offset 的邏輯都在這個方法里實現。

(1)ExecuteIteratorQuery()

// sql/sql_union.cc
bool Query_expression::ExecuteIteratorQuery(THD *thd) {
...
{
...
for (;;) {
// 從存儲引擎讀取一條記錄
int error = m_root_iterator->Read();
DBUG_EXECUTE_IF("bug13822652_1", thd->killed = THD::KILL_QUERY;);

// 讀取出錯,直接返回
if (error > 0 || thd->is_error()) // Fatal error
return true;
// error < 0
// 表示已經讀完了所有符合條件的記錄
// 查詢結束
else if (error < 0)
break;
// SQL 被客戶端干掉了
else if (thd->killed) // Aborted by user
{
thd->send_kill_message();
return true;
}
...
// 發送數據給客戶端
if (query_result->send_data(thd, *fields)) {
return true;
}
...
}
}
...
}

從以上代碼可以看到,select 查詢入口方法的主體是一個無限 for 循環。

每一輪循環都會調用 m_root_iterator->Read() 方法從存儲引擎讀取一條記錄。

對于示例 SQL 來說,m_root_iterator->Read() 就是 LimitOffsetIterator::Read()。

for 循環會一直執行,直到 m_root_iterator->Read() 的返回值命中以下任意一個條件才會結束:

  • if (error > 0 || thd->is_error()),讀取出錯了,以錯誤狀態結束查詢。
  • if (error < 0),已經讀完所有符合條件的記錄,以正常狀態結束查詢。
  • if (thd->killed),SQL 被客戶端通過 kill <query_id> 干掉了,中止查詢。

<query_id> 為 show processlist 中的 Id 字段。

  • for 循環中,每次從存儲引擎讀取到一條記錄,都會調用 query_result->send_data(thd, *fields) 方法。

對于示例 SQL 來說,這個方法的行為就是把記錄發送給客戶端。

(2)LimitOffsetIterator::Read()

// sql/iterators/composite_iterators.cc
int LimitOffsetIterator::Read() {
// 這個 if 括號里的條件理解起來會有點困難
// 所以被省略了,眼不見為凈
//【重點】只有讀取第一條和最后一條記錄時才會進入這個 if 分支
if (...) {
...
// m_needs_offset = true
// 表示 SQL 語句中指定了 offset
if (m_needs_offset) {
...
// 循環從存儲引擎讀取 m_offset 條記錄
// 每讀取到一條記錄,直接丟棄
for (ha_rows row_idx = 0; row_idx < m_offset; ++row_idx) {
// 讀取一條記錄之后
// 如果沒有出錯,就接著讀取下一條記錄
int err = m_source->Read();
// 讀取出錯,直接返回錯誤碼
if (err != 0) {
return err;
}
...
}
// 讀取 m_offset 條記錄并丟棄之后
// 把 m_seen_rows 設置為已讀取記錄數
m_seen_rows = m_offset;
// 然后把 m_needs_offset 設置為 false
// 表示不需要再處理 offset 邏輯了(因為已處理完成)
// 下次讀取時也就不需要再跳過 m_offset 條記錄了
m_needs_offset = false;
...
}
// 如果已經讀取了 m_limit 條記錄
// 就返回 -1,表示讀取結束
// m_limit = SQL 中的 limit + offset
if (m_seen_rows >= m_limit) {
...
return -1;
}
}

// 讀取需要返回給客戶端的記錄
const int result = m_source->Read();
...
// 已讀取記錄數加 1
++m_seen_rows;
// 返回當前讀取的記錄
// 給 Query_expression::ExecuteIteratorQuery() 方法
return result;
}

除了處理 offset 邏輯之外,LimitOffsetIterator::Read() 每次只讀取一條記錄,這個方法的核心邏輯分為三部分:

第 1 部分:if (m_needs_offset),SQL 語句中指定了 offset,返回第一條記錄給客戶端之前,需要讀取 offset 條記錄并丟棄,從第 offset + 1 條記錄開始返回給客戶端。

這部分的主要邏輯是一個 for 循環,會循環 offset 次,每次讀取一條記錄。

如果讀取成功,就接著讀取下一條記錄,而不會對這條記錄做任何操作,也就相當于丟棄了。

如果讀取失敗,直接返回錯誤碼,讀取結束,客戶端會收到報錯信息。

第 2 部分:if (m_seen_rows >= m_limit),表示已經讀取了 m_limit 條記錄,返回 -1 表示讀取正常結束。

m_limit = SQL 中的 limit + offset。

第 3 部分:result = m_source->Read() 從存儲引擎讀取一條記錄,然后,把結果返回給 Query_expression::ExecuteIteratorQuery() 方法。

4、總結

limit, offset 邏輯比較簡單,全部由 LimitOffsetIterator::Read() 實現,核心邏輯總結如下:

  • 從存儲引擎讀取返回給客戶端的第 1 條記錄之前,會先讀取 offset 條記錄并丟棄,然后再讀取一條記錄,用于返回給客戶端。
  • 從存儲引擎讀取第 2 ~ limit + offset 條記錄時,每讀取一條記錄,都返回給 Query_expression::ExecuteIteratorQuery(),由該方法把記錄返回給客戶端。
  • 讀取 limit + offset 條記錄之后,返回 -1 表示讀取流程正常結束。

從 LimitOffsetIterator::Read() 的實現邏輯來看,offset 越大,讀取之后被丟棄的記錄就越多,讀取這些記錄所做的都是無用功。

為了提高 SQL 的執行效率,可以通過改寫 SQL 讓 offset 盡可能小,理想狀態是 offset = 0。

本文轉載自微信公眾號「一樹一溪」,可以通過以下二維碼關注。轉載本文請聯系一樹一溪公眾號。

責任編輯:姜華 來源: 一樹一溪
相關推薦

2023-04-17 08:19:47

select *MySQL

2023-05-26 14:08:00

Where 條件MySQL

2022-10-27 21:34:28

數據庫機器學習架構

2021-06-09 06:41:11

OFFSETLIMIT分頁

2025-09-26 07:46:07

2020-08-06 08:00:51

數據分頁優化

2022-09-07 07:37:06

LIMITOFFSET分頁

2020-09-18 07:01:38

分頁offsetlimit

2022-09-09 19:01:02

接口Reader?Spark

2024-10-07 10:02:28

2010-05-25 15:12:22

MySQL分頁

2023-02-26 23:43:43

MySQL數據庫分頁查詢

2021-02-11 13:30:56

Nodejs源碼c++

2010-05-17 17:23:27

MySQL limit

2010-11-25 10:12:02

MySQL查詢優化

2022-02-09 07:44:30

Go源碼工具

2022-01-26 07:18:57

工具GoGo 項目

2012-09-06 10:07:26

jQuery

2021-01-04 05:53:35

MyBatis底層Java

2010-10-13 16:31:18

優化MySQL查詢
點贊
收藏

51CTO技術棧公眾號

亚洲国产成人精品女人久久久| 欧美一区二区色| 电影天堂久久| 日韩欧美aaaaaa| 人妻无码视频一区二区三区| 91影院成人| 欧美亚洲成人精品| 日韩精品成人在线观看| 精品中文视频在线| 国产美女高潮在线观看| 日韩一级二级三级| 怡红院红怡院欧美aⅴ怡春院| 欧美三级在线看| 四色成人av永久网址| 一区二区三区色| www.男人的天堂.com| 中文在线一区二区| 羞羞网站在线观看入口免费| 国产调教视频一区| 欧美日韩一二三区| 久久久久久青草| 欧美亚洲国产bt| 第一福利在线| 欧美日韩一区二区在线视频| 成人在线观看免费| 欧美疯狂性受xxxxx喷水图片| aⅴ在线视频男人的天堂 | 国模吧一区二区三区| 精品一区二区三区中文字幕视频 | 国产宾馆实践打屁股91| 欧美亚洲黄色片| 国产亚洲一区二区三区在线观看 | av在线中出| 91精品国产综合久久婷婷香蕉 | 亚洲成人av一区二区| 99久re热视频精品98| 另类综合日韩欧美亚洲| 亚洲一区二区综合| 日韩欧美亚洲一区| 欧美日韩裸体免费视频| 暖暖日本在线观看| 精品一区精品二区| 鲁鲁狠狠狠7777一区二区| 久久社区一区| 久久国产精品-国产精品| 日韩国产欧美一区二区三区| 四虎影院一区二区| 久久久国产综合精品女国产盗摄| 国产精品xxx视频| 一个色综合网| 久久精品中文字幕电影| 国产色噜噜噜91在线精品| 国产精品久久久久久久久久久新郎 | 国产精品1区2区在线观看| 牛牛国产精品| 在线看无码的免费网站| 久久久久久免费网| 在线观看免费网站| 亚洲精品视频二区| 亚洲精品动态| 欧美一区二区三区在线播放| 大胆亚洲人体视频| 米奇.777.com| 精品免费一区二区三区| 91午夜精品| 亚洲国产精品人久久电影| 亚洲男女网站| 国产精品国产精品国产专区不卡| 青青草91视频| 免费h片在线| 亚洲国产精品成人va在线观看| a级日韩大片| 日韩.欧美.亚洲| 亚洲少妇30p| 在线看的毛片| 国产日本欧美一区| 国产盗摄女厕一区二区三区| 日本在线一二三| 久久亚洲欧美日韩精品专区| 悠悠资源网久久精品| 国产精品高潮在线| 男人的j进女人的j一区| 午夜3点看的视频| 精品丝袜一区二区三区| 国产精品久久天天影视| 黄页网站大全在线观看| 欧美日韩精品一区二区| 先锋影音国产精品| 日韩黄色短视频| 日韩一区二区三区免费看 | 亚洲欧美综合v| 亚洲电影在线一区二区三区| 国产女大学生av| 欧美不卡一区二区三区| 婷婷精品进入| 网上成人av| 亚洲最新在线视频| 西西人体一区二区| 亚洲色图16p| 国产91精品不卡视频| 成人免费毛片a| a级片在线免费观看| 91中文字幕在线| 亚洲欧美色一区| 国产精品日韩精品在线播放| 在线免费观看成人| 欧美一区二区啪啪| 好吊日精品视频| 亚洲高清成人影院| 欧洲精品毛片网站| 久久久国产精品麻豆| 中国色在线日|韩| 日韩精品欧美一区二区三区| 欧美午夜精品久久久| 在线观看免费一区二区| 99热在线网站| 国产精品视频一| 亚洲午夜一区二区三区| 免费视频国产一区| 91av入口| 欧美日韩日本国产| 老司机精品视频在线播放| 99视频在线免费| 操人视频在线观看欧美| 粉嫩在线一区二区三区视频| av综合电影网站| 香港三级日本三级a视频| 国产亚洲精品美女久久久久| 国产激情精品久久久第一区二区 | 99re6热只有精品免费观看| 日本久久久精品视频| 欧美巨大黑人极品精男| 97久久精品人人澡人人爽| 天堂综合在线播放| 久久国产色av免费观看| 欧美激情在线观看| 国产精品进线69影院| 日韩mv欧美mv国产网站| 中国国产一级毛片| 亚洲最大福利网| 久久精品道一区二区三区| 2017亚洲天堂1024| 欧美日韩免费观看一区| 日韩欧美一区在线| 精品一区二区三区免费播放| 原纱央莉成人av片| 精品国产一区三区| 日韩中文字幕第一页| 久久精品夜夜夜夜久久| 欧美日韩夜夜| 日本aa大片在线播放免费看| 国产美女91呻吟求| 在线视频一区二区三| 麻豆精品网站| 欧美电影网站| 亚洲欧美在线精品| 国产mv免费观看入口亚洲| 色999日韩国产欧美一区二区| 免费永久网站黄欧美| 东京一区二区| 91制片厂毛片| 成人福利视频网| 日韩三级视频中文字幕| 福利一区在线观看| 欧美日韩一区二区三区不卡视频| 在线看三级网站视频| 亚洲精品国产精品国自产观看浪潮 | 黄色录像1级片| 国产欧美一区二区视频| 亚洲乱亚洲乱妇无码| 亚洲国产精品av| 国产精品theporn| 日本美女一区| 色偷偷免费视频| 日韩精品最新在线观看| 久久综合久久88| 精品国产老师黑色丝袜高跟鞋| 国产欧美一级| 美国十次综合久久| 黄色av免费在线看| 人妻夜夜添夜夜无码av| 国产成人精品电影| 精品欧美一区二区三区精品久久| 91丝袜高跟美女视频| 91精品国产乱码久久久久久久| 国产美女一区视频| 天天色综合6| 中文字幕欧美日韩精品| 亚洲黄一区二区三区| 亚洲综合精品| 9l视频自拍九色9l视频成人| 岛国在线视频| 熟女人妇 成熟妇女系列视频| 好吊色欧美一区二区三区四区 | 国产精品二区三区| 久久色免费在线视频| 欧美在线free| 亚洲欧洲一区二区在线播放| 看国产成人h片视频| 99久久激情|