[转帖]PHP源码之ext/mysql扩展部分_MySQL, Oracle及数据库讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  MySQL, Oracle及数据库讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 1907 | 回复: 0   主题: [转帖]PHP源码之ext/mysql扩展部分        下一篇 
wlcbhrss
注册用户
等级:新兵
经验:68
发帖:65
精华:0
注册:2011-8-21
状态:离线
发送短消息息给wlcbhrss 加好友    发送短消息息给wlcbhrss 发消息
发表于: IP:您无权察看 2014-12-16 14:33:23 | [全部帖] [楼主帖] 楼主

我写过一个外部模块扩展,现在开始看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;


--转自 北京联动北方科技有限公司




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论