一、简单介绍
当用PHP访问数据库时,常见的选择是 PDO和MySQLi,PDO和MySQLi都支持通过面向对象的形式提供API,其中MySQLi也提供了面向过程的API。MySQLi只支持MySQL,而PDO支持多种数据库,因此使用PDO可以不改代码,就讲后端存储的数据库进行替换。
PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口。还需要更具体驱动扩展,才能真正连接相应的数据库。 具体的驱动扩展列表如下
;extension=php_pdo_firebird.so
;extension=php_pdo_informix.so
;extension=php_pdo_mssql.so
;extension=php_pdo_mysql.so
;extension=php_pdo_oci.so
;extension=php_pdo_oci8.so
;extension=php_pdo_odbc.so
;extension=php_pdo_pgsql.so
;extension=php_pdo_sqlite.so
以 php_pdo_mysql.so 为例,就是用来连接mysql数据库的驱动。
执行 phpinfo(),pdo部分输出如下,可以看到这里PDO支持的驱动有sqlite, mysql,也就是该环境PDO目前支持连接sqlite, mysql这两种数据库。
PDO
PDO support => enabled
PDO drivers => sqlite, mysql
pdo_mysql
PDO Driver for MySQL => enabled
Client API version => mysqlnd 5.0.12-dev - 20150407 - $Id: b396954eeb2d1d9ed7902b8bae237b287f21ad9e $
Directive => Local Value => Master Value
pdo_mysql.default_socket => /tmp/mysql.sock => /tmp/mysql.sock
二、PDO::ATTR_TIMEOUT参数研究
PDO在创建对象的时候,可以通过 PDO::ATTR_TIMEOUT 参数设置超时时间,官方文档对这个参数的说明如下,
PDO::ATTR_TIMEOUT: 指定超时的秒数。并非所有驱动都支持此选项,这意味着驱动和驱动之间可能会有差异。比如,SQLite等待的时间达到此值后就放弃获取可写锁,但其他驱动可能会将此值解释为一个连接或读取超时的间隔。 需要 int 类型。
这个说明比较模糊,没有说明是连接超时,还是读写超时,只是说和具体驱动的实现有关。于是研究 php 7.1.2的源码,进行确认。 用 ATTR_TIMEOUT 关键字到 ext目录搜索,
vim pdo_dbh.c +255 driver = pdo_find_driver(data_source, colon - data_source); vim pdo_dbh.c +356 driver->db_handle_factory(dbh, options)
vim pdo.c +185 db_handle_factory 方法是 pdo_driver_t 结构体的一个成员 搜索 grep pdo_driver_t -rn *,找到下面的结果 vim pdo_mysql/mysql_driver.c +805
pdo_driver_t pdo_mysql_driver = {
PDO_DRIVER_HEADER(mysql),
pdo_mysql_handle_factory
};
搜索 pdo_mysql_handle_factory 函数的定义 vim mysql_driver.c +540, 超时相关的代码如下
603 if (driver_options) {
604 zend_long connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
631 if (mysql_options(H->server, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout)) {
632 pdo_mysql_error(dbh);
633 goto cleanup;
634 }
其中 pdo_attr_lval 函数的定义如下,也就是超时时间没有设置的话,默认为 30s
// vim pdo/php_pdo_driver.h +197
static inline zend_long pdo_attr_lval(zval *options, enum pdo_attribute_type option_name, zend_long defval)
{
zval *v;
if (options && (v = zend_hash_index_find(Z_ARRVAL_P(options), option_name))) {
return zval_get_long(v);
}
return defval;
}
所以对于pdo_mysql驱动来说,结论如下:
1、PDO::ATTR_TIMEOUT设置的是连接超时时间 2、不设置超时时间的话,默认的连接超时时间是 30s
三、读写超时参数
数据库往往还有读/写超时时间,那么PDO是否可以设置读/写超时时间呢?
https://www.cnblogs.com/feng18/p/6523646.html
grep MYSQL_OPT_WRITE_TIMEOUT -rn * 查看以下代码 vim mysqlnd/mysqlnd_connection.c +1681
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
enum_mysqlnd_client_option option,
const char * const value
)
{
const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
if (PASS != conn->m->local_tx_start(conn, this_func)) {
goto end;
}
switch (option) {
#ifdef WHEN_SUPPORTED_BY_MYSQLI
case MYSQL_OPT_READ_TIMEOUT:
case MYSQL_OPT_WRITE_TIMEOUT:
#endif
case MYSQLND_OPT_SSL_KEY:
case MYSQLND_OPT_SSL_CERT:
case MYSQLND_OPT_SSL_CA:
case MYSQLND_OPT_SSL_CAPATH:
case MYSQLND_OPT_SSL_CIPHER:
case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
case MYSQL_OPT_CONNECT_TIMEOUT:
case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
ret = conn->vio->data->m.set_client_option(conn->vio, option, value);
看代码似乎是只有 mysqli才支持设置读/写超时时间,网上也有搜到文档,通过修改php源码,让pdo支持读/写超时,pdo应该是不支持这两个参数的。接下来通过一个程序,验证pdo支持读/写超时的效果。
测试方法: 预先登录数据库, 开启事务, 通过select for update锁定一条记录, 但是不commit, 然后通过接口向数据库发出请求,update此记录,此时请求会因为拿不到锁,而一直等待 开始时php-fpm 设定的请求终止时间为 30s request_terminate_timeout = 30 所以请求达到30s后,会自动终止脚本运行,接口返回502,没有错误日志,
接下来,修改 request_terminate_timeout = 60 重启php-fpm
重复以上测试步骤,发现超过了 30s,接口请求仍然在正常等待,等到55s,执行commit动作后,请求可以正常返回
说明 数据库读写超时不止30s,目前完全是依赖php-fpm 设定的请求终止时间来结束请求
MYSQL_OPT_READ_TIMEOUT, MYSQL_OPT_WRITE_TIMEOUT, 这两个常量是 mysqlnd 扩展定义的常量, pdo_mysql 扩展没有引用这两个常量, 看pdo的文档,也没有搜到可以使用这两个常量, 目前基本上可以确定,pdo不支持设置读/写超时时间。
四、数据库处理
像上面这种,一个进程提交了sql,sql在等待数据库执行,然后进程突然被kill掉了,数据库会怎么对待sql?
|