Mysql是我們在日常開發(fā)中最常使用的一種數(shù)據(jù)庫响巢,當我們利用Mysql實現(xiàn)各種業(yè)務增刪改查時岂却,都是將其當做一個黑盒在使用父晶,我們的系統(tǒng)只需要從數(shù)據(jù)庫的連接池中獲取一個連接就可以執(zhí)行sql工作。執(zhí)行一條insert語句矾策,表里會多出來一條數(shù)據(jù)宛乃;執(zhí)行一條select語句悠咱,就能從表里找出滿足條件的數(shù)據(jù)。至于這個過程是怎么實現(xiàn)的征炼,對我們來說析既,底層的事情都交給了數(shù)據(jù)庫,內(nèi)部工作和運行機制完全透明谆奥,以至于在學校學了一個學期的數(shù)據(jù)庫課程眼坏,只學到了表層和皮毛,以為會寫超長復雜的Sql語句就算掌握了數(shù)據(jù)庫酸些,直到進入職場被面試官吊打宰译,在面對線上超大數(shù)據(jù)體量的Sql優(yōu)化時兩眼懵逼,我們對數(shù)據(jù)庫的了解還太少魄懂,底層原理和深層優(yōu)化有待提升沿侈,因此在這里開一個Mysql專題進行爆肝與死磕,將學習心得與總結(jié)和大家共享市栗。
一. Mysql的架構(gòu)設計
1.連接器
Mysql作為服務器缀拭,一個客戶端的Sql連接過來就需要分配一個線程進行處理,這個線程會專門負責監(jiān)聽請求并讀取數(shù)據(jù)填帽。這部分的線程和連接管理都是有一個連接器蛛淋,專門負責跟客戶端建立連接、權(quán)限認證篡腌、維持和管理連接褐荷。
2.解析器
SQL解析,就是按照SQL語法嘹悼,把我們編寫的SQL語句進行詞法分析诚卸,理解這個SQL需要做什么事情葵第,比如如下SQL語句绘迁,就會被解析器給拆成三步邏輯:
- 查詢user表合溺;
- 尋找到userId=007的那條數(shù)據(jù);
- 將數(shù)據(jù)中的name缀台、age棠赛、country字段信息提取出來;
select name,age,country from user where userId=007;
3.優(yōu)化器
當解析器把SQL語句解析出來步驟以后膛腐,緊接著會通過優(yōu)化器來選擇一個最優(yōu)的查詢路徑睛约,這個是對于我們編寫的超大型復雜SQL時十分有用,一條SQL語句可能會對應多個不同的查詢路徑樹哲身,優(yōu)化器會從中選擇一個最優(yōu)路徑出來辩涝,返回一個這個語句的執(zhí)行方案交給下一步進行繼續(xù)處理。
4.執(zhí)行器
這一步就是根據(jù)上一步驟優(yōu)化器生成的SQL執(zhí)行方案勘天,去調(diào)用存儲引擎的接口(InnoDB怔揩、mysam)完成SQL語句的執(zhí)行計劃,這個SQL引擎操作的有可能是內(nèi)存數(shù)據(jù)脯丝,也有可能是磁盤文件商膊。
于是把上面一條SQL語句的執(zhí)行抽取出來形成下面這個Mysql的邏輯架構(gòu)圖:
補充:MySQL 8.0 版本直接將查詢緩存的整塊功能刪掉了,日常也不建議開啟使用宠进。
二. InnoDB存儲引擎設計
上面流程介紹了一條SQL語句在數(shù)據(jù)庫中的執(zhí)行流程晕拆,當SQL執(zhí)行到存儲引擎這里,因為不同的引擎的實現(xiàn)機制有所不同材蹬,現(xiàn)在就以使用最廣泛的為InnoDB引擎再來細化說明Innodb在數(shù)據(jù)查詢和更新流程的細節(jié)实幕。
1. 內(nèi)存緩沖池
InnoDB中有一個非常重要的內(nèi)存組件——緩沖池(Buffer Pool),這里會緩存很多數(shù)據(jù)便于查詢時堤器,直接從讀取緩存數(shù)據(jù)昆庇,而不需要訪問磁盤。在執(zhí)行更新操作時吼旧,如對“id=007”數(shù)據(jù)進行更新凰锡,會先查詢BufferPool中是否存在,不存在的話圈暗,就從磁盤中加載到緩沖池中來掂为,然后還要對這行記錄加獨占鎖。
2.undo日志
如果執(zhí)行一個更新語句员串,且這個語句還在事務里的話勇哗,在事務提交以前,我們都可以選擇回滾寸齐,而這部分回滾的數(shù)據(jù)欲诺,就是未更新以前的數(shù)據(jù)抄谐,它是保存在undo日志里的。
3.redo日志
在更新操作時扰法,會先更新Buffer Pool中的數(shù)據(jù)然后再去操作磁盤蛹含,但是在極端情況下會出現(xiàn)系統(tǒng)宕機或者斷電導致磁盤還未更新就丟失了數(shù)據(jù),此時需要把對內(nèi)存所做的修改寫入到一個redo log buffer里去塞颁,這里也是一個內(nèi)存緩沖區(qū)浦箱,用于存放redo日志的。在事務提交策略上祠锣,有一個關鍵配置:
innodb_flush_log_at_trx_commit=1
commit=0時酷窥,事務提交成功,redo buffer不會寫入redo log
commit=1時伴网,只要事務提交成功蓬推,redo buffer一定會寫入redo log(推薦)
commit=2時,事務提交成功澡腾,redo buffer先寫入os cache沸伏,然后過段時間才刷入redo log
采用了commit=1的配置,就能保證提交事務的時候蛋铆,redo日志會刷入磁盤馋评,數(shù)據(jù)不丟失。
4.binlog日志
前面說的redo日志是偏向物理性質(zhì)的日志刺啦,記錄的是對數(shù)據(jù)頁中某一個數(shù)據(jù)進行了什么修改留特,和引擎有關。而Binlog日志則偏向于邏輯層面的一個歸檔日志玛瘸,記錄的是對表中某行數(shù)據(jù)做了什么操作蜕青,且修改后的值為多少,是Mysql Server自己的日志文件糊渊。
binlog日志的落盤是在上面redo日志落盤以后才會去執(zhí)行的右核,而且落盤策略也是可以選擇直接刷盤還是先刷到OS cache中,這個配置項取決于sync_binlog
渺绒。sync_binlog 這個參數(shù)設置成 1 的時候贺喝,表示每次事務的 binlog 都持久化到磁盤,這樣可以保證 MySQL 異常重啟之后 binlog 不丟失(一般都推薦這個值)宗兼。當Mysql把binlog寫入了磁盤文件以后躏鱼,就完成了最終的事務提交,這次提交就會把本次更新對應的binlog文件名+binlog的位置都寫入redo log的日志文件中殷绍,并同時寫入一個commit標記染苛。到這里,才最終完成了一次事務的提交主到,總的一個流程圖如下:
5.IO線程隨機刷盤
當事務提交完畢以后茶行,所有的日志文件都已經(jīng)更新到最新了躯概,此時系統(tǒng)不懼任何宕機斷電行為了,后臺的IO線程會隨機把內(nèi)存中的那個更新后的臟數(shù)據(jù)刷入到磁盤文件中畔师,此時內(nèi)存和磁盤中的數(shù)據(jù)已經(jīng)保持一致娶靡。
核心總結(jié)
基于InnoDB的數(shù)據(jù)更新做一個流程總結(jié)如下:
- 1、加載磁盤文件到buffer Pool中茉唉;
- 2固蛾、更新數(shù)據(jù)之前,寫入舊數(shù)據(jù)到undo日志度陆,便于回退;
- 3献幔、更新內(nèi)存中的buffer pool數(shù)據(jù)懂傀;
- 4、將更新部分的redo log寫入到redo log buffer中蜡感;
- 5蹬蚁、redo日志刷入磁盤
- 6、binlog日志刷入磁盤
- 7郑兴、將binlog文件和位置寫入到redo日志文件中犀斋,并寫入commit。
- 8情连、后臺的IO線程某個時間隨機將buffer pool中的臟數(shù)據(jù)同步到磁盤文件叽粹。
InnoDB引擎中主要就是包含了buffer pool、redo log buffer等內(nèi)存數(shù)據(jù)却舀,同時也包含了undo日志虫几、redo日志等磁盤文件數(shù)據(jù),另外Mysql也會有自己的binlog日志挽拔。buffer pool是Mysql里面的一個核心內(nèi)存組件辆脸,所有的增刪改查操作都是針對buffer pool來進行的,然后才是去配合寫undo螃诅、redo啡氢、binlog等操作,這些都是組件的設計既可以用于提高數(shù)據(jù)庫的并發(fā)性能术裸,同時更重要的可以設計保證事務的四個特性(ACID)倘是,譬如redo日志可以保證持久性,undo可以用于回滾保證原子性等等穗椅。這一節(jié)就到這里辨绊,后面一節(jié)的會重點安排一下InnoDB索引及數(shù)據(jù)結(jié)構(gòu)的內(nèi)容。