PHP源碼之 ext/mysql擴充部分

來源:互聯網
上載者:User

我寫過一個外部模組擴充,現在開始看PHP源碼中的mysql擴充,它是可以被整合到PHP內部的,所以應該算是內建的擴充了。
該擴充需要用到mysql資料庫提供的一些介面,所以需要安裝了mysql,並能夠確定mysql.h的位置。
該擴充的位置一般在 PHP-source-code/ext/mysql 下。
在linux下,主要需要注意的檔案是: config.m4, php_mysql.c, php_mysql_structs.h。
ps:該目錄下有tags檔案,所以可以利用ctags的各種特性,直接找到函數、宏定義等。
ps:linux下mysql的啟動 sudo mysql-dir/bin/mysqld_safe &
之後會有兩個進程運行:

複製代碼 代碼如下:root 5297 0.0 0.0 5920 1416 pts/5 S 11:08 0:00 /bin/sh /usr/local/mysql/bin/mysqld_safe
mysql 5320 1.4 1.1 202728 23796 pts/5 Sl 11:08 1:47 /usr/local/mysql/libexec/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/var --user=mysql --pid-file=/usr/local/mysql/var/tj1clnxweb0004.pid --skip-external-locking --port=3306 --socket=/tmp/mysql.sock

----------------------------------------------
以下先記錄閱讀過程中的一些細節問題:
1、php_mysql_do_query_general函數
該擴充提供的函數mysql_query和mysql_unbuffered_query最後都要用到php_mysql_do_query_general來執行核心功能。
首先看下trace模式:
if (MySG(trace_mode)) { .... }
在php.ini中有配置:
mysql.trace_mode = Off
而如果配置是開啟的,那麼就會執行if中的句子,而如果執行的句子是select的話,就會在前面加上explain,分析sql句子的效能。
然後看一下mysql_use_result和mysql_store_result的區別:
可以看到,mysql_query使用的是mysql_store_result函數,而mysql_unbuffered_query是用的是mysql_use_result。
參考文章(http://school.cnd8.com/mysql/jiaocheng/25143_8.htm),並總結如下:
mysql_store_result 查詢並擷取所有的結果集,儲存在用戶端,準備供用戶端使用,這樣對於用戶端的記憶體和效能要求較大。
mysql_use_result 僅查詢,而將結果擷取延遲。相當於是在服務前端維護了一個結果集。
當調用完mysql_store_result ,使用mysql_fetch_row擷取結果時,是直接從用戶端擷取結果,如果返回為NULL,就是沒有結果了。
而當調用完mysql_use_result,使用mysql_fetch_row擷取結果時,是從服務前端擷取結果,如果返回為NULL,那麼可能是沒用結果了,也可能是網路連接出錯等原因。
由於結果集的維護地方不同,mysql_store_result 的結果可以提供更多的處理函數,比如任意的seek、擷取總數、非順序訪問等。而mysql_use_result的結果集就不可以。
另外,由於mysql_use_result的結果集是維持在伺服器端,那麼它就提出一個要求:用戶端對結果集中的每一行都必須調用mysql_fetch_row,否則,結果集中剩餘的記錄就會成為下一個查詢結果集中的一部分,並且發生“不同步”的錯誤。
那麼,為什麼還要用到mysql_use_result呢?看下這個情況:
mysql 和mysqldump 預設時,使用mysql_store_result,但是如果指定--quick 選項,則使用mysql_use_result。
那說明mysql_use_result在效率方面佔有優勢?
看下mysql的協助手冊:
-q, --quick Don't cache result, print it row by row. This may slow
down the server if the output is suspended. Doesn't use
history file.
mysqldump的協助手冊:
-q, --quick Don't buffer query, dump directly to stdout.
那麼在我沒有徹底弄明白為什麼quick對應著mysql_use_result的時候,先搞明白什麼時候不要用mysql_use_result吧。由於mysql_use_result的結果集維護在伺服器端,那麼如果用戶端程式可能被掛起,別用它。如果結果集的行與行之間有過多操作,別用它。也就是一句話,如果查詢完,不是立馬用完結果,free掉,那麼就別用mysql_use_result。
為了嘗試一下效果,寫了以下測試代碼: 複製代碼 代碼如下:$sql = sprintf("select * from pet;");
$result = mysql_unbuffered_query($sql, $conn);
$rows = mysql_fetch_row($result);
var_dump($rows);
$sql = sprintf("select * from shop");
$result = mysql_unbuffered_query($sql, $conn);
$rows = mysql_fetch_row($result);
var_dump($rows);

執行的結果是,第二次fetch不會顯示第一次的結果,但是php會報notice:
PHP Notice: mysql_unbuffered_query(): Function called without first fetching all rows from a previous unbuffered query in /home/yicheng/test-all/mysqltest/test.php on line 28
修改測試代碼: 複製代碼 代碼如下:$i = 1000000;
while($i--){
$sql = sprintf("select * from pet;");
$result = mysql_query($sql, $conn);
#$result = mysql_unbuffered_query($sql, $conn);
while($rows = mysql_fetch_row($result)){

}
if ($result){
mysql_free_result($result);
}
}

使用unbuffered的結果:
:!time ./test.php
real 1m10.220s
user 0m17.853s
sys 0m9.541s
使用mysql_query的結果:
:!time ./test.php
real 1m11.191s
user 0m19.297s
sys 0m10.133s
貌似時間差別也不大嘛
2、一些資源相關的宏定義 複製代碼 代碼如下:#define ZEND_VERIFY_RESOURCE(rsrc) \
if (!rsrc) { \
RETURN_FALSE; \
}
#define ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type) \
rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC, default_id, resource_type_name, NULL, 1, resource_type); \
ZEND_VERIFY_RESOURCE(rsrc);
#define ZEND_FETCH_RESOURCE2(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type1, resource_type2) \
rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC, default_id, resource_type_name, NULL, 2, resource_type1, resource_type2); \
ZEND_VERIFY_RESOURCE(rsrc);
#define ZEND_REGISTER_RESOURCE(rsrc_result, rsrc_pointer, rsrc_type) \
zend_register_resource(rsrc_result, rsrc_pointer, rsrc_type);
#define ZEND_GET_RESOURCE_TYPE_ID(le_id, le_type_name) \
if (le_id == 0) { \
le_id = zend_fetch_list_dtor_id(le_type_name); \
}

我們由mysql_connect函數返回的其實是一個link id(resource(4) of type (mysql link)),通過ZEND_FETCH_RESOURCE和ZEND_FETCH_RESOURCE2宏,可以映射到對應的mysql資源上去。這兩個宏都調用了zend_fetch_resource方法,所以下面我們看下這個方法。
ZEND_API void *zend_fetch_resource(zval **passed_id TSRMLS_DC, int default_id, char *resource_type_name, int *found_resource_type, int num_resource_types, ...)
比如在mysql_list_dbs函數中調用ZEND_FETCH_RESOURCE2宏:
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
其中mysql儲存了返回的有效資源,php_mysql_conn *定義了返回資源的類型,mysql_link, id分別對應著passed_id 和 default_id(因為很多函數調用,不傳入具體的conn,就是使用default值),"MySQL-Link"是resource_type_name,le_link, le_plink是zend_fetch_resource的...部分,它們倆是static int類型的值。
由zend_fetch_resource可以看出,resource(4) of type (mysql link)的value.lval中包含了long型的id。如果default_id為-1,那麼就是用passed_id傳入的id,否則就使用default_id作為id,利用zend_list_find來尋找其對應的資源。
看了幾個函數之後,其實該擴充也就是對mysql提供的c介面的封裝而已。但是封裝的很規範也很穩定!
如果想瞭解進一步,那還是得看MYSQL的原始碼。下面貼了幾個重要的資料結構。
-----------------------------------------
一些對於查錯可能有用的php函數: 複製代碼 代碼如下:error_reporting(E_ALL);
#var_dump(mysql_get_host_info($conn));
#var_dump(mysql_get_proto_info($conn));
#var_dump(mysql_get_server_info($conn));
#var_dump(mysql_stat($conn));
#var_dump(mysql_errno($conn));
#var_dump(mysql_error($conn));
#var_dump(mysql_info($conn));

--------------------------------------------
MYSQL源碼中一些有用的struct 複製代碼 代碼如下:typedef struct st_mysql
{
NET net; /* Communication parameters */
gptr connector_fd; /* ConnectorFd for SSL */
char *host,*user,*passwd,*unix_socket,*server_version,*host_info,*info;
char *db;
struct charset_info_st *charset;
MYSQL_FIELD *fields;
MEM_ROOT field_alloc;
my_ulonglong affected_rows;
my_ulonglong insert_id; /* id if insert on table with NEXTNR */
my_ulonglong extra_info; /* Not used */
unsigned long thread_id; /* Id for connection in server */
unsigned long packet_length;
unsigned int port;
unsigned long client_flag,server_capabilities;
unsigned int protocol_version;
unsigned int field_count;
unsigned int server_status;
unsigned int server_language;
unsigned int warning_count;
struct st_mysql_options options;
enum mysql_status status;
my_bool free_me; /* If free in mysql_close */
my_bool reconnect; /* set to 1 if automatic reconnect */
/* session-wide random string */
char scramble[SCRAMBLE_LENGTH+1];
/*
Set if this is the original connection, not a master or a slave we have
added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave()
*/
my_bool rpl_pivot;
/*
Pointers to the master, and the next slave connections, points to
itself if lone connection.
*/
struct st_mysql* master, *next_slave;
struct st_mysql* last_used_slave; /* needed for round-robin slave pick */
/* needed for send/read/store/use result to work correctly with replication */
struct st_mysql* last_used_con;
LIST *stmts; /* list of all statements */
const struct st_mysql_methods *methods;
void *thd;
/*
Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag
from mysql_stmt_close if close had to cancel result set of this object.
*/
my_bool *unbuffered_fetch_owner;
#if defined(EMBEDDED_LIBRARY) || defined(EMBEDDED_LIBRARY_COMPATIBLE) || MYSQL_VERSION_ID >= 50100
/* needed for embedded server - no net buffer to store the 'info' */
char *info_buffer;
#endif
} MYSQL;
typedef struct st_mysql_methods
{
my_bool (*read_query_result)(MYSQL *mysql);
my_bool (*advanced_command)(MYSQL *mysql,
enum enum_server_command command,
const char *header,
unsigned long header_length,
const char *arg,
unsigned long arg_length,
my_bool skip_check,
MYSQL_STMT *stmt);
MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
unsigned int fields);
MYSQL_RES * (*use_result)(MYSQL *mysql);
void (*fetch_lengths)(unsigned long *to,
MYSQL_ROW column, unsigned int field_count);
void (*flush_use_result)(MYSQL *mysql);
#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
int (*stmt_execute)(MYSQL_STMT *stmt);
int (*read_binary_rows)(MYSQL_STMT *stmt);
int (*unbuffered_fetch)(MYSQL *mysql, char **row);
void (*free_embedded_thd)(MYSQL *mysql);
const char *(*read_statistics)(MYSQL *mysql);
my_bool (*next_result)(MYSQL *mysql);
int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
#endif
} MYSQL_METHODS;

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.