2008年01月15日

以源碼探索 PHP 查詢 MySQL 後儲存資料結果的方式

Tags: php mysql database 記憶體管理

基於以下系列討論內容的一大串源碼追蹤。以印證討論內容。

Remember this: Open your mind, use the source.


Source code version: PHP-5.2.5 release

/ext/mysql/php_mysql.c

1.line: 1395-1400, context of mysql_query()

2.line: 1404-1409, context of mysql_unbuffered_query()

3.line: 1345-1359, context of php_mysql_do_query_general()

上面的源碼內容符合 PHP Manual, PHP Large result sets and summary tables 等文的解釋。當程序員透過 mysql_query() 查詢時,資料結果集(result set)會被儲存在 client (即PHP 這端)。而用 mysql_unbuffered_query() 時,則只會在 clinet 儲存 "current row"。

MySQL 5.1 Reference Manual

看完 PHP 的源碼,再看 MySQL 的部份,以進一步確認 MySQL 的源碼實際上是否符合前述解釋。

PHP 源碼顯示,PHP 的 mysql_query() 調用 MySQL C API 的 mysql_store_result()mysql_unbuffered_query() 調用 MySQL C API 的 mysql_use_result()。故列示於下。

mysql_store_result()

mysql_store_result() reads the entire result of a query to the client, allocates a MYSQL_RES structure, and places the result into this structure.

在 client 端配置一個 MYSQL_RES 的結構體,並儲放資料結果於此結構中。

mysql_use_result()

mysql_use_result() initiates a result set retrieval but does not actually read the result set into the client like mysql_store_result() does. Instead, each row must be retrieved individually by making calls to mysql_fetch_row().

MYSQL_RES

This structure represents the result of a query that returns rows (SELECT, SHOW, DESCRIBE, EXPLAIN). The information returned from a query is called the result set in the remainder of this section.

Source code version: mysql-5.1.21-beta

接著我們看 MySQL 源碼以印證手冊說明。

/include/mysql.h
line: 316-332, context of MYSQL_RES

MYSQL_RES 所包含的內容有二種情形。一種是 bufered, 另一種是 unbuffered 。剛好對應 mysql_store_result(), mysql_use_result() 的儲存策略。

/sql/client.c

繼續觀察 mysql_store_result() 的源碼內容。

line: 2802-2844, context of mysql_store_result() line: 1309-1312, context of cli_read_rows()

源碼內容顯示 mysql_store_result() 會註記資料結果為 "buffered",並清除 "unbuffered" 策略的相關內容。藉由函數指標 read_rows 調用 cli_read_rows() ,將資料結果集讀取至 client 。

結論

前一系列的討論中,我們的出發點是 CakePHP 的資料庫存取行為,因為 CakePHP 使用的是 mysql_query(),而非 mysql_unbuffered_query(),所以我們對資料結果集的記憶體配置方式,也是針對 mysql_query()。事實上,絕大多數的案例中, PHP 程序員使用的都是 mysql_query() 而非 mysql_unbuffered_query()

我在稍後的回應中也說明,PHP 的 MySQL 函數,可以區分2種儲存策略 (即mysql_query()mysql_unbuffered_query())。不過 FIEND 顯然忽視這點,隻字未提mysql_unbuffered_query(),始終堅持他的認知: "資料結果應該是儲存在 DB 端"。亦即他認為只有一種策略。

Ok, 也許資料庫管理與規劃書籍介紹的策略只有一種,但那是通則、是理論。但此處討論的卻是 PHP 查詢 MySQL 的實例。我說明的內容,並不是我的個人認知,而是程式碼明擺著的事實。如果 FIEND 還要說誰的 DB觀念不好,在誤導別人,請去對 PHP 和 MySQL 的開發團隊說吧。


Posted by shirock at 樂多Roodo! │11:35 │回應(2)引用(0)SQL/Database
樂多分類:學術/學習 工具:加入樂多書籤編輯本文
Ads by Roodo! 

引用URL

http://cgi.blog.roodo.com/trackback/4986803
回應文章

有兩個問題請教:
1.使用mysql_unbuffered_query(),是否會造成你在討論串中所說的,佔用mysql server的記憶體?

2.到底mysql做換頁功能的最好方法是什麼?先用limit,再用不含limit的count嗎?
Posted by Moby at 2008年04月4日 19:15
1. MySQL 本身必然要配置記憶體(mysql server的記憶體)儲存查詢結果,這不需多談。
本文談的是 php 的記憶體使用(application server的記憶體)。

2.最適策略視資料量而定。
有時,做 results cache 是比較好的策略。
Posted by 遊手好閒的石頭成 at 2008年04月8日 10:46