標籤: 標籤: 「咱們都是小青蛙」公衆號文章mysql
SQL
的全稱是Structured Query Language
,翻譯成中國話就是結構化查詢語言
。這是一種聲明式的語法,何爲聲明式?能夠聯想一下咱們生活中的老闆,老闆在佈置任務的時候會告訴你:小王啊,今天把這些磚從A地搬到B地啊,而後就沒而後了。老闆並不關心你是用手擡,仍是用車拉,老闆只關心結果:你把磚搬過去就行了。咱們之於數據庫而言,就是一個老闆,SQL
語句就是咱們給數據庫下達的任務,至於具體數據庫怎麼執行咱們並不關心,咱們只關心最後數據庫給咱們返回的結果。程序員
對於設計數據庫的人而言,語句怎麼執行就得好好考慮了,老闆不操心,事兒總還得幹。設計MySQL
的大叔人爲的把MySQL
分爲server
層和存儲引擎
層,可是什麼操做是在server
層作的,什麼操做是在存儲引擎
層作的你們可能有些迷糊。本文將以一個實例來展現它們兩者各自負責的事情。sql
爲了故事的順利發展,咱們先建立一個表:數據庫
CREATE TABLE hero (
id INT,
name VARCHAR(100),
country varchar(100),
PRIMARY KEY (id),
KEY idx_name (name)
) Engine=InnoDB CHARSET=utf8;
複製代碼
咱們爲hero
表的id
列建立了聚簇索引,爲name
列建立了一個二級索引。這個hero
表主要是爲了存儲三國時的一些英雄,咱們向表中插入一些記錄:bash
INSERT INTO hero VALUES (1, 'l劉備', '蜀'), (3, 'z諸葛亮', '蜀'), (8, 'c曹操', '魏'), (15, 'x荀彧', '魏'), (20, 's孫權', '吳'); 複製代碼
如今表中的數據就是這樣的:markdown
mysql> SELECT * FROM hero; +----+------------+---------+ | id | name | country | +----+------------+---------+ | 1 | l劉備 | 蜀 | | 3 | z諸葛亮 | 蜀 | | 8 | c曹操 | 魏 | | 15 | x荀彧 | 魏 | | 20 | s孫權 | 吳 | +----+------------+---------+ 5 rows in set (0.00 sec) 複製代碼
準備工做就作完了。學習
一條語句在執行以前須要生成所謂的執行計劃,也就是該語句將採用什麼方式來執行(使用什麼索引,採用什麼鏈接順序等等),咱們能夠經過Explain
語句來查看這個執行計劃,比方說對於下邊語句來講:優化
mysql> EXPLAIN SELECT * FROM hero WHERE name < 's孫權' AND country = '蜀'; +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+ | 1 | SIMPLE | hero | NULL | range | idx_name | idx_name | 303 | NULL | 2 | 20.00 | Using index condition; Using where | +----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+ 1 row in set, 1 warning (0.03 sec) 複製代碼
輸出結果的key
列值爲idx_name
,type
列的值爲range
,代表會針對idx_name
二級索引進行一個範圍查詢。不少同窗在這裏有一個疑惑:究竟是一次性把全部符合條件的二級索引都取出來以後再統一進行回表操做,仍是每從二級索引中取出一條符合條件的記錄就進行回表一次?其實server層和存儲引擎層的交互是以記錄爲單位的,上邊這個語句的完整執行過程就是這樣的:spa
server層第一次開始執行查詢,把條件name < 's孫權'
交給存儲引擎,讓存儲引擎定位符合條件的第一條記錄。翻譯
存儲引擎在二級索引idx_name
中定位name < 's孫權'
的第一條記錄,很顯然符合該條件的二級索引記錄的name
列的值爲'c曹操'
。而後須要注意,咱們看到EXPLAIN
語句的輸出結果的Extra
列有一個Using index condition
的提示,這代表會將有關idx_name
二級索引的查詢條件放在存儲引擎層判斷一下,這個特性就是所謂的索引條件下推
(Index Condition Pushdown,簡稱ICP
)。很顯然這裏的ICP
條件就是name < 's孫權'
。有的同窗可能會問這不就是脫了褲子放屁麼,name
值爲'c曹操'
的這條記錄就是經過name < 's孫權'
這個條件定位的,爲啥還要再判斷一次?這就是設計MySQL 的大叔的粗暴設計,十分簡單,沒有爲啥~
小貼士: 對於使用二級索引進行等值查詢的狀況有些許不一樣,比方說上邊的條件換成`name = 's孫權'`,對於等值查詢的這種狀況,設計MySQL的大叔在InnoDB存儲引擎層有特殊的處理方案,是不做爲ICP條件進行處理的。
而後拿着該二級索引記錄中的主鍵值去回表,把完整的用戶記錄都取到以後返回給server層
(也就是說獲得一條二級索引記錄後當即去回表,而不是把全部的二級索引記錄都拿到後統一去回表)。
咱們的執行計劃輸出的Extra
列有一個Using Where
的提示,意味着server層在接收到存儲引擎層返回的記錄以後,接着就要判斷其他的WHERE條件是否成立(就是再判斷一下country = '蜀'
是否成立)。若是成立的話,就直接發送給客戶端。
小貼士: 什麼?發現一條記錄符合條件就發送給了客戶端?那爲何個人客戶端不是一條一條的顯示查詢結果,而是一會兒所有展現呢?這是客戶端軟件的鬼,人家規定在接收徹底部的記錄以後再展現而已。
若是不成立的話,就跳過該條記錄。
接着server層向存儲引擎層要求繼續讀剛纔那條記錄的下一條記錄。
由於每條記錄的頭信息中都有next_record
的這個屬性,因此能夠快速定位到下一條記錄的位置,而後繼續判斷ICP
條件,存儲引擎把下一條記錄取出後就將其返回給server層。
而後重複第3步的過程,直到存儲引擎層遇到了不符合name < 's孫權'
的記錄,而後向server層返回了讀取完畢的信息,這是server層將結束查詢。
這個過程用語言描述仍是有點兒囉嗦,咱們寫一個超級簡化版的僞代碼來瞅瞅(注意,是超級簡化版):
first_read = true; //是不是第一次讀取 while (true) { if (first_read) { first_read = false; err = index_read(...); //調用存儲引擎接口,定位到第一條符合條件的記錄; } else { err = index_next(...); //調用存儲引擎接口,讀取下一條記錄 } if (err = 存儲引擎的查詢完畢信息) { break; //結束查詢 } if (是否符合WHERE條件) { send_data(); //將該記錄發送給客戶端; } else { //跳過本記錄 } } 複製代碼
上述的僞代碼雖然很粗糙,但也基本代表了意思哈~ 以後有機會咱們再嘮叨嘮叨使用臨時表的狀況已經使用filesort
的狀況是怎麼執行的。
寫文章挺累的,有時候你以爲閱讀挺流暢的,那實際上是背後無數次修改的結果。若是你以爲不錯請幫忙轉發一下,萬分感謝~ 這裏是個人公衆號「咱們都是小青蛙」,裏邊有更多技術乾貨,時不時扯一下犢子,歡迎關注:
另外,做者還寫了一本MySQL小冊:《MySQL是怎樣運行的:從根兒上理解MySQL》的連接 。小冊的內容主要是從小白的角度出發,用比較通俗的語言講解關於MySQL進階的一些核心概念,好比記錄、索引、頁面、表空間、查詢優化、事務和鎖等,總共的字數大約是三四十萬字,配有上百幅原創插圖。主要是想下降普通程序員學習MySQL進階的難度,讓學習曲線更平滑一點~ 有在MySQL進階方面有疑惑的同窗能夠看一下: