abstract:本篇手記主要介紹了"PHP到MySQL數(shù)據(jù)查詢過(guò)程概述",主要涉及到方面的內(nèi)容,對(duì)于MySql感興趣的同學(xué)可以參考一下:HP層到MySQL層Php到sql組件層次如下圖所示:ext/mysqli和ext/mysql 是客戶端的擴(kuò)展程序庫(kù)(庫(kù)函數(shù)) ,在客戶端腳本層面的擴(kuò)展庫(kù)。 Mysqli庫(kù)是mysql庫(kù)的擴(kuò)展版本,擴(kuò)展版本增加了列版定(Bind Column)綁定。PDO (
本篇手記主要介紹了"PHP到MySQL數(shù)據(jù)查詢過(guò)程概述",主要涉及到方面的內(nèi)容,對(duì)于MySql感興趣的同學(xué)可以參考一下:
HP層到MySQL層
Php到sql組件層次如下圖所示:
ext/mysqli和ext/mysql 是客戶端的擴(kuò)展程序庫(kù)(庫(kù)函數(shù)) ,在客戶端腳本層面的擴(kuò)展庫(kù)。 Mysqli庫(kù)是mysql庫(kù)的擴(kuò)展版本,擴(kuò)展版本增加了列版定(Bind Column)綁定。PDO (PHP Data Object) 是另外一種面向數(shù)據(jù)對(duì)象的 擴(kuò)展庫(kù)。這些擴(kuò)展庫(kù)直接面向編程者,而它的底層實(shí)現(xiàn)是mysql連接引擎(如mysqlnd和libmysql )(參考 http://bbs.chinaunix.net/thread-3679393-1-1.html 、http://blog.csdn.net/treesky/article/details/7286098 )。
mysqlnd和libmysql 是PHP端(客戶端)的數(shù)據(jù)庫(kù)連接驅(qū)動(dòng)引擎。libmysql 是通用的數(shù)據(jù)庫(kù)連接引擎,而mysqlnd是專屬PHP開發(fā)的連接引擎,從屬于Zend中。 當(dāng)PHP通過(guò)調(diào)用擴(kuò)展庫(kù)(ext/mysqli和ext/mysql)中的mysql_query() 函數(shù)進(jìn)行數(shù)據(jù)庫(kù)查詢的時(shí)候,Zend引擎將通過(guò)mysql(mysqlnd和libmysql)查詢引擎向MySQL服務(wù)器發(fā)出查詢請(qǐng)求。
MySQL層的數(shù)據(jù)查詢
MySQL服務(wù)器接受到客戶端的查詢請(qǐng)求后,查詢執(zhí)行過(guò)程如上圖所示:
1. 查詢緩存,如果命中則直接將結(jié)果集返回給到客戶端,否則進(jìn)入步驟2
2. 對(duì)SQL語(yǔ)句依次進(jìn)行解析、預(yù)處理、查詢優(yōu)化等操作,最終生成查詢執(zhí)行計(jì)劃(select的查詢執(zhí)行計(jì)劃可以通過(guò)explain select 查看)
3. MySQL服務(wù)端的查詢執(zhí)行引擎將依據(jù)查詢執(zhí)行計(jì)劃 調(diào)用存儲(chǔ)引擎對(duì)數(shù)據(jù)進(jìn)行查詢。當(dāng)SQL語(yǔ)句的最后一層關(guān)聯(lián)被執(zhí)行后,將產(chǎn)生查詢結(jié)果集
4. 查詢結(jié)果集發(fā)送到客戶端,傳回的方式有兩種:MySQL服務(wù)端緩存結(jié)果集 或 不緩存,這個(gè)由參數(shù)SQL_BUFFER_RESULT設(shè)置。 并且,如果用戶設(shè)置了SQL_CACHE 那么本次的查詢的結(jié)果集的一份副本存儲(chǔ)于 查詢緩存 中(步驟1相關(guān))。
SQL_CACHE參數(shù)的啟示:
將復(fù)雜的(多個(gè)關(guān)聯(lián))查詢分解為多條簡(jiǎn)單的查詢,因?yàn)?nbsp;
1)簡(jiǎn)單查詢的緩存命中搞、
2)復(fù)雜查詢結(jié)果的緩存易失效(關(guān)聯(lián)太多表)
3)簡(jiǎn)單查詢鎖的持有率低
MySQL Server 到 PHP層
通信模式MySQL Server和客戶端的通信采用“半雙工通信”,意思是:客戶端和服務(wù)端只能有一個(gè)在讀,并且另外一個(gè)必須是寫。
優(yōu)點(diǎn):協(xié)議簡(jiǎn)單,客戶端和服務(wù)端的寫權(quán)限是互斥的
缺點(diǎn):無(wú)法進(jìn)行流量控制,一端開始發(fā)送消息,另一端要完整的接受這個(gè)消息后才能響應(yīng)它。
啟示:服務(wù)端查詢后的結(jié)果集發(fā)送給客戶端,客戶端(客戶端的查詢引擎,例如mysqlnd)必須完整的接受。所以,如果只需要少數(shù)行,記得在sql語(yǔ)句添加使用limit,少用select *。
結(jié)果集回傳模式結(jié)果集回傳中,每一行記錄都通過(guò) 客戶端-服務(wù)器通信協(xié)議進(jìn)行包裝,然后再交接給下層的tcp協(xié)議;當(dāng)然,在tcp層,可以先緩存每行記錄的協(xié)議包,組成大包在發(fā)出(對(duì)應(yīng)用層透明)。
MySQL服務(wù)端只有將結(jié)果集全部發(fā)送給客戶端后,才能釋放結(jié)果集所占用的buffer。
服務(wù)端緩存模式
客戶端命令: mysql_unbuffer_query(),在客戶端的sql驅(qū)動(dòng)擴(kuò)展(mysqlnd)中不設(shè)置結(jié)果集的緩存,所以在fecth_array_xxx從結(jié)果集中讀取一條記錄時(shí),需要從服務(wù)端的緩沖區(qū)中讀取。
服務(wù)端無(wú)緩存模式
客戶端命令: mysql_query(),在客戶端的sql驅(qū)動(dòng)擴(kuò)展(mysqlnd)中設(shè)置了buffer用于緩存服務(wù)端的結(jié)果集,所以在fecth_array_xxx從結(jié)果集中讀取一條記錄時(shí),是直接從mysqlnd擴(kuò)展的緩沖區(qū)中取得row。
小結(jié)
如果結(jié)果集很大: 服務(wù)端無(wú)緩存模式可以減少服務(wù)端的內(nèi)存壓力喲,但是占用客戶端的內(nèi)存。這樣只有看情況取舍了。
PHP層到用戶層
在客戶端,于服務(wù)端對(duì)接的是mysql擴(kuò)展引擎(libmysql 或者 mysqlnd),而用戶層是通過(guò)擴(kuò)展庫(kù)(ext/mysql 或 ext/mysqli)和mysql引擎進(jìn)行交互(啟示就是調(diào)用引擎的api讀取結(jié)果集)。
引 擎libmysql 和 mysqlnd 的機(jī)制并不同,主要區(qū)別是mysqlnd是轉(zhuǎn)為php寫的,被編譯到zend內(nèi)部。而libmysql是通用的庫(kù),zend需要調(diào)用該庫(kù)實(shí)現(xiàn)數(shù)據(jù)庫(kù)的連 接。在這種卻別下,mysqlnd和zend具有更好的粘合性,在數(shù)據(jù)傳輸?shù)接脩魧訒r(shí),少了一層數(shù)據(jù)的拷貝。具體的架構(gòu)區(qū)別如下圖所示。圖中,五角星表示 緩存 buffer。
ext/mysqli和ext/mysql 是客戶端的擴(kuò)展程序庫(kù)(庫(kù)函數(shù)) : 在客戶端腳本層面mysqlInd和libmysql 是MySQL Server端的驅(qū)動(dòng)程序。其中,libmysql是通用的MySQL查詢驅(qū)動(dòng)程序,而mysqlnd是專為PHP設(shè)置的基于Zend引擎的SQL驅(qū)動(dòng),即mysqlnd的數(shù)據(jù)驅(qū)動(dòng)動(dòng)作需要經(jīng)過(guò)Zend和mysqlserver交互,而libmysql直接和mysqlserver交互的。
對(duì)比:
ext/mysqli(或者ext/mysql)和libmysql的數(shù)據(jù)庫(kù)查詢中的過(guò)程為:
1)mysqi向libmysql驅(qū)動(dòng)發(fā)送查詢請(qǐng)求
2)Libmysql執(zhí)行請(qǐng)求并得到結(jié)果集存儲(chǔ)域libmysql的buffers中
3)Mysqli申請(qǐng)內(nèi)存:zval指定的一塊buffer
4)Mysqii從libmysql拷貝結(jié)果集到zval指定的buffer中
ext/mysqli(或者ext/mysql)和mysqlnd的數(shù)據(jù)庫(kù)查詢中的過(guò)程為:
1) mysqi向mysqlnd驅(qū)動(dòng)發(fā)送查詢請(qǐng)求
2) mysqlnd驅(qū)動(dòng)通過(guò)zend引擎執(zhí)行sql查詢,結(jié)果集的每一行由一個(gè)buffer存儲(chǔ)(各個(gè)buffer是分散的)
3) Mysqlnd創(chuàng)建多個(gè)zval,并指向這些buffers
例如:
在ext/mysql & libmysql 中,libmysql驅(qū)動(dòng)執(zhí)行SQL語(yǔ)句后得到結(jié)果集Row1~Row3,然后ext/mysql將結(jié)果集拷貝到zend buffer中,之后mysqli_fetch_xxx函數(shù)從該區(qū)域內(nèi)存中讀取結(jié)果集中的內(nèi)容。
在ext/mysqli & mysqlInd 中,mysqlnd 驅(qū)動(dòng)執(zhí)行SQL語(yǔ)句得到結(jié)果集Row1~Row3,其中,每個(gè)row直接由zend的一個(gè)buffer存儲(chǔ),并由一個(gè)zval指向??蛻舳送ㄟ^(guò)映射直接從 該內(nèi)存區(qū)域中讀取結(jié)果實(shí)現(xiàn)mysqli_fetch_xxx。
小結(jié)
mysqlnd和zend更具有粘合性,在sql查詢驅(qū)動(dòng)中,mysqlnd通過(guò)zend引擎訪問數(shù)據(jù)庫(kù),并直接將將結(jié)果存儲(chǔ)域zend的buffer中,相比libmysql驅(qū)動(dòng)(獨(dú)立于zend),少了一次結(jié)果集緩存拷貝。