上一篇文章講述了Mysql的基本框架,和sql執(zhí)行的流程,這篇文章首先分析下每個流程的具體細(xì)節(jié),然后介紹日志文件和數(shù)據(jù)文件
以下面語句作為例子來分析流程中每一步的具體細(xì)節(jié):
select * from t1 where id=10000;
連接器
mysql>mysql -uroot -p
連接器負(fù)責(zé)建立、管理、維護(hù)連接,獲取權(quán)限,當(dāng)輸入上面的命令的時候會要求輸入密碼,賬戶密碼校驗通過之后就是建立連接,同時獲取該賬戶的權(quán)限,也就是說當(dāng)這時管理修改了該用戶的權(quán)限的話,對于當(dāng)前連接也是沒有影響的。
查詢緩存
當(dāng)連接建立完成之后,會先到查詢緩存中查看時候有緩存存在,select語句的hash值作為key,查詢結(jié)果作為value,當(dāng)key存在于緩存當(dāng)中時,直接返回value,當(dāng)key不存在時候會繼續(xù)執(zhí)行后面的流程,流程執(zhí)行結(jié)束之后,將查詢結(jié)果放入查詢緩存中,值得注意的是查詢緩存往往利大于弊,因為查詢緩存失效太頻繁,當(dāng)表中有數(shù)據(jù)更新時,mysql就會清除查詢緩存,所以不建議使用查詢緩存,mysql8之后也將查詢緩存功能刪除了
分析器
當(dāng)查詢緩存中不存在key時流程進(jìn)入到分析器中,分析器主要負(fù)責(zé)詞法分析和語法分析,首先會做詞法分析,輸入的一條SQL語句是由多個字符串和空格組成的,MYSQL需要識別出字符串是什么,代表什么意思,如關(guān)鍵詞select,識別出來這是一個查詢語句,字符串t1識別為表名t1,id識別為例id。之后就是做語法分析,根據(jù)詞法分析結(jié)果,語法分析器會根據(jù)語法規(guī)則,判斷是你輸入的這個SQL語句是否滿足Mysql語法,如果語法不對,就會收到語法錯誤提醒
優(yōu)化器
經(jīng)過了分析器之后Mysql就知道你要做什么了,在開始執(zhí)行之前Mysql還會經(jīng)過優(yōu)化器的處理,例如索引的選擇,多表join的時候,選擇那個表作為驅(qū)動表,例如下面的語句
mysql>select * from t1 join t2 on t1.code = t2.code where t1.a=10 and t2.a=10;
當(dāng)執(zhí)行這條語句的時候Mysql有兩個選擇:
- 先從t1表中取出a=10的記錄,然后根據(jù)code關(guān)聯(lián)到t2,再判斷t2的a是否等于10
- 先從t2表中取出a=10的記錄,然后根據(jù)code關(guān)聯(lián)到t1,再判斷t1的a是否等于10
很明顯兩種方法的執(zhí)行結(jié)果是一樣的,但是執(zhí)行效率會有不同, 優(yōu)化器要做的就是選擇最優(yōu)的方案
執(zhí)行器
Mysql經(jīng)過了優(yōu)化器之后,就進(jìn)入了執(zhí)行器階段,開始執(zhí)行語句,執(zhí)行之前會先判斷是否有執(zhí)行權(quán)限,如果沒有則會報錯,如果有權(quán)限,則會打開表繼續(xù)執(zhí)行,執(zhí)行就會調(diào)用具體引擎的接口,比如我們這個例子中id字段沒有索引,則執(zhí)行器的執(zhí)行流程如下:
調(diào)用InnoDB引起的接口取出表中的第一行,判斷id是不是等于10,如果不是則跳過,如果是則將這行存在結(jié)果集中
調(diào)用引擎接口取下一行,重復(fù)相同的邏輯判斷,知道渠道這個表的最后一行
執(zhí)行器將上述遍歷過程中所有滿足條件的行組成記錄集作為結(jié)果集返回給客戶端
至此,這個語句就執(zhí)行完成了
Mysql的物理結(jié)構(gòu)
mysql是通過文件系統(tǒng)對數(shù)據(jù)和索引進(jìn)行存儲的
mysql從物理結(jié)構(gòu)上可以分為日志文件和數(shù)據(jù)索引文件
日志文件
Mysql通過日志記錄了數(shù)據(jù)庫操作信息和錯誤信息,常用的日志文件有錯誤日志、二進(jìn)制日志、查詢?nèi)罩尽⒙樵內(nèi)罩竞褪聞?wù)Redo日志、中繼日志等
可以通過命令查看當(dāng)前數(shù)據(jù)庫中的日志使用信息:
show VARIABLES like 'log_%';
錯誤日志(errorlog)
默認(rèn)是開啟的,Mysql5.5.7之后無法關(guān)閉錯誤日志,錯誤日志記錄了運行過程中遇到的所有的嚴(yán)重錯誤信息以及Mysql每次啟動和關(guān)閉的詳細(xì)信息,
錯誤日志可以通過log-error和log-warnings來定義,其中l(wèi)og-err是定義是否啟用錯誤日志的功能和錯誤日志的存儲位置,log-warings是定義是否將警告信息也定義至錯誤日志中
log-error=/var/log/mysqld.log
#使用1|0來定義啟動和關(guān)閉
log_waring=1
二進(jìn)制日志
默認(rèn)是關(guān)閉的,可以通過下面的配置開啟
log-bin=mysql-bin
其中mysql-bin是binlong日志文件的basename,binlog日志文件的完整名稱:mysql-bin-000001.log
binlog記錄了數(shù)據(jù)庫的所有的ddl語句和dml語句,但不包括select語句內(nèi)容,語句以事件的形式保存,描述了數(shù)據(jù)的變更順序,binlog還包括了每個更新語句的執(zhí)行時間信息,如果是DDL,則直接記錄到binlog日志,而DML語句必須通過失誤提交才能記錄到binlog日志中。
binlog主要用于實現(xiàn)mysql的主從復(fù)制、數(shù)據(jù)備份、數(shù)據(jù)恢復(fù)。
通用查詢?nèi)罩荆╣eneral query log)
默認(rèn)情況下是關(guān)閉的,由于通用查詢?nèi)罩緯涗浻脩舻乃胁僮鳎渲羞€包括增刪改查等信息,在并發(fā)操作的大的環(huán)境下會產(chǎn)生大量的信息從而導(dǎo)致不必要的磁盤IO,會影響mysql的性能,如果不是為了調(diào)試數(shù)據(jù)庫的目的建議生產(chǎn)環(huán)境不要開啟查詢?nèi)罩?br> show global VARIABLES like 'general_Log';
開啟方式如下:
#啟動或關(guān)閉
general_log=(ON|OFF)
#日志文件變量,如果general_log_file沒有指定,默認(rèn)名是host_name.log
general_log_file=/path/to/file
#記錄類型 table:將日志存入數(shù)據(jù)庫,file:將日志存入文件
log_output=(TABLE|FILE|NONE)
慢查詢?nèi)罩荆╯how query log)
默認(rèn)是關(guān)閉的,可以通過如下設(shè)置進(jìn)行開啟:
#開啟慢查詢?nèi)罩?slow_query_log=ON
#慢查詢的閾值
long_query_time=1
#日志文件如果沒有給出file_name,默認(rèn)為主機(jī)名,后綴為-slow.log,如果給出了文件名,但不是絕對路徑、文件則寫入數(shù)據(jù)目錄
slow_query_log_file=file_name
記錄執(zhí)行時間超過long_query_time秒的所有查詢,便于收集查詢時間比較長的SQL語句
重做日志(redo log)
作用:確保事務(wù)的持久性。防止在發(fā)生故障的時間點,尚有葬爺未寫入磁盤,在重啟myslq服務(wù)的時候,根據(jù)redo log進(jìn)行重做,從而達(dá)到事務(wù)的持久性這一特性
內(nèi)容:物理格式的日志,記錄的是物理數(shù)據(jù)頁面的修改的信息,其中redo log是順序?qū)懭雛edo log file的物理文件中的。
什么時候產(chǎn)生:事務(wù)開啟之后就會產(chǎn)生redo log,redo log的落盤并不是隨著事務(wù)的提交才寫入的,而是在事務(wù)的執(zhí)行過程中,便開始寫入redo log文件中
什么時候釋放:當(dāng)對應(yīng)的事務(wù)的臟頁寫入到磁盤之后,redo log的使命也就是完成了,重做日志占用的空間就可以重用(被覆蓋)
對應(yīng)的物理文件:默認(rèn)情況下,對應(yīng)的物理文件位于數(shù)據(jù)庫的data目錄下的ib_logfile1&ib_logfile2,innodb_log_group_home_dir指定日志文件組所在的路徑,默認(rèn)是/,表示數(shù)據(jù)庫的數(shù)據(jù)目錄下。innodb_log_files_in_group指定重做日志文件組中的文件的數(shù)量,innodb_log_file_size指定重做日志文件的大小
其他:很重要的一點,redo log是什么時候?qū)懕P的?前面說了是在事務(wù)開始之后逐步寫盤的,之所以說重做日志是在事務(wù)開始之后逐步寫入,而不一定是事務(wù)提交才寫入,原因就是重做日志有一個緩存區(qū)innodb_log_buffer,默認(rèn)大小為8M,innodb存儲引擎先將重做日志寫入innodb_log_buffer中。然后通過三種方式將innodb日志緩沖區(qū)的日志刷新到磁盤 1、每秒一次執(zhí)行刷新innodb_log_buffer到重做日志文件。2、每個事務(wù)提交時會將重做日志刷新到重做日志文件。3、當(dāng)重做日志緩存可用空間少于一半是,重做日志緩存被刷新到重做日志文件
回滾日志(undo log)
作用:保存了事務(wù)發(fā)生之前的一個數(shù)據(jù)版本,可以用于回滾,同時可以提供多版本并發(fā)控制下的讀(MVCC),也即非鎖定讀(快照讀)
內(nèi)容:邏輯格式的日志,在執(zhí)行undo的時候,僅僅是將數(shù)據(jù)從邏輯上恢復(fù)至事務(wù)之前的狀態(tài),而不是從物理頁面上操作實現(xiàn)的,這一點是不同于redo log的
什么時候產(chǎn)生:事務(wù)開始之前,將當(dāng)前的版本生成undo log,undo也會產(chǎn)生redo來保證undo log的可靠性
什么時候釋放:當(dāng)事務(wù)提交之后,undo log并不能立馬被刪除,而是放入待清理的鏈表,有purge線程判斷是否由其他事務(wù)在使用undo段中的表上一個事務(wù)之前的版本信息。決定是否可以清理undo log的日志空間
對應(yīng)的物理文件:Mysql5.6之前,undo表空間位于共享表空間的回滾段中,共享表空間的默認(rèn)名稱是ibdata,位于數(shù)據(jù)文件目錄中,Mysql5.6之后,undo表空間可以配置成獨立的文件,但是提前需要在配置文件中配置,
中繼日志(relay log)
是在主從復(fù)制環(huán)境中產(chǎn)生的日志
主要作用是為了叢機(jī)可以從中繼日志中獲取到主機(jī)同步過來的SQL語句,然后執(zhí)行到從機(jī)中。
數(shù)據(jù)文件
數(shù)據(jù)文件寫入是隨機(jī)IO
InnoDB數(shù)據(jù)文件
.frm文件:主要存放與表相關(guān)的數(shù)據(jù)信息,主要包括表結(jié)構(gòu)的定義信息
.ibd:使用獨享表空間存儲數(shù)據(jù)和索引信息,一張表對應(yīng)一個ibd文件
ibdata文件:使用共享表空間存儲數(shù)據(jù)和索引信息,所有表共同使用一個或者多個ibdata文件
MyISAM數(shù)據(jù)文件
.frm文件:主要存放與表相關(guān)的數(shù)據(jù)信息,主要包括表結(jié)構(gòu)的定義信息
.myd文件:主要用來存儲表數(shù)據(jù)信息
.myi文件:主要用來存儲表數(shù)據(jù)文件中任何索引的數(shù)據(jù)樹
更多技術(shù)文章可關(guān)注個人公眾號: 碼農(nóng)Fly