之前,有一年多的工作客戶端領(lǐng)域的工作經(jīng)驗(yàn)滑臊。
后來口芍,也在字節(jié)做了一年多的后端業(yè)務(wù)。
現(xiàn)在希望做一些MySQL
總結(jié)雇卷,豐富一下自己在后端領(lǐng)域的積累鬓椭。
目錄如下:
MySQL 基礎(chǔ)技術(shù)(一) —— MySQL 是如何查詢的颠猴?
MySQL 基礎(chǔ)技術(shù)(二) —— MySQL 是如何更新的?
MySQL 基礎(chǔ)技術(shù)(三)—— MySQL 如何保證數(shù)據(jù)不丟失小染?
MySQL 基礎(chǔ)技術(shù)(四)—— MySQL 如何保證高可用翘瓮?
一、引子
在日常工作中裤翩,我們執(zhí)行了一個(gè)簡(jiǎn)單的查詢語句:
select * from <table_name> where id = 647;
那么资盅,在
MySQL
內(nèi)部會(huì)如何工作呢?
二踊赠、架構(gòu)
首先呵扛,MySQL
架構(gòu)可以分為 Server層 和 Engine層兩部分。
Server(服務(wù))層:
涵蓋了 MySQL
上層核心服務(wù)功能筐带。
包括 “連接器”今穿、“查詢緩存”、“分析器”伦籍、“優(yōu)化器”蓝晒、“執(zhí)行器”等等。
以及內(nèi)置功能函數(shù)(數(shù)學(xué)帖鸦、時(shí)間計(jì)算等等)芝薇,觸發(fā)器讼载、視圖等等跨存儲(chǔ)引擎的功能都在 Server 層實(shí)現(xiàn)鼻吮。
Engine(引擎)層:
主要負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和提取等底層服務(wù)眷蚓。
其實(shí)現(xiàn)是插件式的,MySQL 支持 InnoDB
立倍、MyISAM
、Memory
等多個(gè)存儲(chǔ)引擎侣滩。
目前口注,最常用的是 InnoDB 存儲(chǔ)引擎。
而從 MySQL 5.5.5 版本開始君珠,InnoDB
成為了默認(rèn)存儲(chǔ)引擎寝志。
可以使用 show engines;
命令來查看各個(gè)引擎。
show engines;
幾個(gè)常用底層存儲(chǔ)引擎的簡(jiǎn)單區(qū)別:
InnoDB:支持事務(wù)策添,支持行鎖材部,支持外鍵。
MyISAM:不支持事務(wù)唯竹,只支持表鎖乐导,不支持外鍵。
Memory:所有數(shù)據(jù)置于內(nèi)存的存儲(chǔ)引擎浸颓,擁有極高的插入物臂,更新和查詢效率旺拉。但是會(huì)占用和數(shù)據(jù)量成正比的內(nèi)存空間。并且其內(nèi)容會(huì)在 MySQL 重新啟動(dòng)時(shí)丟失棵磷。
三蛾狗、架構(gòu)圖解
接下來我們依次解釋下各個(gè)組件:
1.連接器:
顧名思義,連接器負(fù)責(zé)跟客戶端建立連接仪媒、獲取權(quán)限沉桌、維持和管理鏈接的。
常用命令如下:
mysql -h$ip -P$port -u$user -p
在完成 TCP 握手后规丽,連接器會(huì)基于用戶名和密碼來驗(yàn)證身份蒲牧。
驗(yàn)證通過會(huì)查詢出當(dāng)前用戶的權(quán)限。
(這代表:即使修改了用戶權(quán)限赌莺,也只會(huì)影響下次連接時(shí)的權(quán)限冰抢。之前連接過的權(quán)限依然是舊的權(quán)限)
執(zhí)行 show processlist;
可以查詢當(dāng)前的連接狀態(tài)。
show processlist;
如果客戶端長(zhǎng)時(shí)間沒有操作艘狭,連接器會(huì)自動(dòng)斷開挎扰。這個(gè)時(shí)間是由 wait_timeout 控制的,默認(rèn)是8小時(shí)巢音。
2.查詢緩存
建立完連接之后遵倦,會(huì)優(yōu)先查詢有沒有緩存。
在打開查詢緩存的前提下官撼,
每次執(zhí)行過的 select
語句會(huì)以 Key
- Value
的形式保存在內(nèi)存中梧躺。
如果緩存命中,就會(huì)直接返回結(jié)果給客戶端傲绣。
但是查詢緩存往往弊大于利掠哥,得看場(chǎng)景使用。
查詢緩存的失效非常頻繁秃诵,只要表有更新续搀,那么表上所有的查詢緩存都會(huì)被清空。
大大降低了查詢緩存的命中率菠净,還犧牲了MySQL的部分性能禁舷。
除非是一種更新頻率較低的靜態(tài)表,可以打開查詢緩存毅往。(可以用SQL_CACHE顯示制定牵咙。 如:select SQL_CACHE * from <table_name>)
更新頻率比較高的表建議不要使用查詢緩存。(query_cache_type 設(shè)置成 DEMAND)
注意:
MySQL 8.0 開始攀唯,已經(jīng)廢棄查詢緩存功能霜大。
因?yàn)榫彺胬鲜菚?huì)被 update/insert/delete 操作清除掉,緩存了還沒來得及用就又沒了革答。
3.分析器
詞法分析
首先战坤,會(huì)進(jìn)行詞法分析曙强。
將一個(gè)完整的SQL語句,拆分成語句類型(select? insert? update? ...)途茫、表名碟嘴、列名等等。
語法分析
其次囊卜,會(huì)進(jìn)行語法分析娜扇。
判斷 SQL 語句是否符合 MySQL 語法。
如果錯(cuò)誤栅组,會(huì)報(bào)出下面的錯(cuò)誤:
ou have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ...
這時(shí)雀瓢,我們只要修正 use near 后面的語句即可。
4.優(yōu)化器
上一步玉掸,分析器通過“詞法分析”刃麸、“語法分析”解析出這條 SQL 具體要做的事。
優(yōu)化器司浪,會(huì)基于這條 SQL 來分析具體要使用哪個(gè)索引方案泊业。
具體可以使用 explain <sql>;
命令來分析 MySQL 的索引選擇。
這個(gè)命令在優(yōu)化 MySQL 查詢效率時(shí)會(huì)經(jīng)常用到啊易。
例如:
explain select * from <table_name> where id = 647;
會(huì)分析出具體使用哪個(gè)索引方案吁伺,是否符合我們預(yù)期。
如果不符合我們的預(yù)期租谈,可以使用 force index 命令來強(qiáng)制使用某個(gè)索引篮奄。(絕大部分場(chǎng)景下,MySQL選擇的都是最優(yōu)的)
5.執(zhí)行器
分析器知道了 SQL 要做什么割去,
優(yōu)化器明確了 SQL 需要用哪個(gè)索引方案窟却。
執(zhí)行器才會(huì)真正的基于索引方案,開始執(zhí)行查詢語句劫拗。
執(zhí)行之前间校,會(huì)判斷一下當(dāng)前連接的用戶有無該表的權(quán)限矾克。
如果有權(quán)限页慷,就會(huì)根據(jù)表的 Engine 選擇來調(diào)用對(duì)應(yīng)的引擎接口。
舉例:
user_info 表的存儲(chǔ)引擎是 InnoDB
胁附。
select * from user_info where name = "647";
如果 name 列沒有聲明任何索引酒繁,執(zhí)行步驟如下:
- 調(diào)用 innoDB 引擎接口獲取表的第一行,判斷
name
是否等于647
控妻。如果不是州袒,跳過。如果是弓候,將結(jié)果保存郎哭。 - 調(diào)用 innoDB 引擎接口獲取表的下一行他匪,重復(fù)相同邏輯,一直到表的最后一行夸研。
- 將所有滿足條件的結(jié)果集返回給客戶端邦蜜。
如果 name 列有索引,執(zhí)行步驟如下:
- 調(diào)用 innoDB 引擎接口獲取索引樹(B+樹)亥至,基于索引樹快速查到
name
等于647
的所有主鍵id悼沈。 - 將所有滿足條件的組件 id,回主表查詳細(xì)信息姐扮。(這個(gè)操作稱為“回表”)
- 將所有滿足條件的結(jié)果集返回給客戶端絮供。
參考與致謝:
1.《MySQL實(shí)戰(zhàn)45講》(林曉斌老師)
2.《MySQL存儲(chǔ)引擎對(duì)比》