CodeIgniter load database 分析

使用 CodeIgniter 2.x 可以配置 autoload 自动加载数据库,也可以在 Model 的构造函数中使用$this->load->database()方法加载数据库:

1
2
3
4
5
6
7
8
9
10
11
12
# xxx.model
require_once 'db_config.php';
class xxx_model extends CI_Model
{
public function __construct()
{
$this->db = $this->load->database(DB_Config::$db_config["mydb"], true);

}

// other code ...
}

database() 方法

$this->load->database()方法位于 system/core/Loader.php中,此方法返回 DB($params, $active_record)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Database Loader
*
* @param string the DB credentials
* @param bool whether to return the DB object
* @param bool whether to enable active record (this allows us to override the config setting)
* @return object
*/
public function database($params = '', $return = FALSE, $active_record = NULL)
{
// Grab the super object
$CI =& get_instance();

// Do we even need to load the database class?
if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
{
return FALSE;
}

require_once(BASEPATH.'database/DB.php');

if ($return === TRUE)
{
return DB($params, $active_record);
}

// Initialize the db variable. Needed to prevent
// reference errors with some configurations
$CI->db = '';

// Load the DB class
$CI->db =& DB($params, $active_record);
}

DB() 方法

DB()方法位于 system/database/DB.php 中,此方法重点完成以下两个任务

  1. new $driver($params)
  2. $DB->initialize()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Initialize the database
*
* @category Database
* @author EllisLab Dev Team
* @link http://codeigniter.com/user_guide/database/
* @param string
* @param bool Determines if active record should be used or not
*/
function &DB($params = '', $active_record_override = NULL)
{
// Load the DB config file if a DSN string wasn't passed
// ... some code here
require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php');

// Instantiate the DB adapter
$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
$DB = new $driver($params);

if ($DB->autoinit == TRUE)
{
$DB->initialize();
}
// some code here ...
return $DB;
}

当 autoinit 为 true 时,自动初始化数据库,$DB->initialize(),此方法位于 system/database/DB_driver.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Initialize Database Settings
*
* @access private Called by the constructor
* @param mixed
* @return void
*/
function initialize()
{
// If an existing connection resource is available
// there is no need to connect and select the database
if (is_resource($this->conn_id) OR is_object($this->conn_id))
{
return TRUE;
}

// ----------------------------------------------------------------

// Connect to the database and set the connection ID
$this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();

// No connection resource? Throw an error
// some code here ...
return TRUE;
}

数据库长连接

根据 $this->pconnect 决定数据库使用长连接或者短连接,$this->pconnect 的取值在数据库配置文件中配置。MySQL 的长连接是存在 php-fpm 子进程里的,进程之间是不共享连接的,所以每个 php-fpm 进程都有一个单独的长连接。使用

ps -aux | grep fpmnetstat -anp | grep 3306 可分别查看 fpm 子进程个数和 MySQL 长连接个数相同。

使用 MySQL 长连接可以复用连接通道,减小连接时延,当 PHP 应用访问量不高,并发量不大时,我们只需要开启少量 php-fpm 子进程,每个子进程对应一个 MySQL 连接,但是当应用并发增加, php-fpm 子进程数量扩大时,MySQL 连接数也随之增加,对数据库造成较大压力,特别是当连接数达到数据库上限时会导致其他 fpm 连接异常。因此要注意使用长连接时 fpm 的子进程数不应超过数据库的最大连接数,在数据库中使用SHOW VARIABLES like 'max_connections'; 查看数据库最大连接数。

1
2
3
4
5
6
7
//查看MySQL最大连接数
mysql> SHOW VARIABLES like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 20000 |
+-----------------+-------+

数据库初始化

关于 autoinit : CI 2.x 中默认为 true, 有些业务场景使用缓存且缓存命中率较高,若启用 autoinit,处理每个请 PHP 求时都会去连接数据库,耗时严重。使用 strace -c -p 360558 分析 360558 (FPM 进程id) 进程程序执行情况得出, 程序执行时 read 操作耗时最长。

再使用 strace -p 360558 2>&1 查看 360558 (FPM 进程id) 进程程序执行情况发现有大量的 read() 和 write() 操作,经分析发现一个参数为 4 的时候为数据库相关操作。

autoinit 改为 false 后,read 和 write 耗时几乎为 0.

需要注意的时将 autoinit 改为 false 后,CI 会报如下错误

1
mysql_escape_string(): This function is deprecated; use mysql_real_escape_string() instead.

参照 https://stackoverflow.com/questions/26169455/codeigniter-use-mysql-real-escape-string-instead-database-connection-issue 修改 system/database/drivers/mysql/mysql_driver.php下的 escape_str() 为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function escape_str($str, $like = FALSE)
{
if (is_array($str))
{
foreach ($str as $key => $val)
{
$str[$key] = $this->escape_str($val, $like);
}

return $str;
}

if (function_exists('mysqli_real_escape_string') AND is_object($this->conn_id))
{
$str = mysqli_real_escape_string($this->conn_id, $str);
}
else
{
$str = addslashes($str);
}

// escape LIKE condition wildcards
if ($like === TRUE)
{
$str = str_replace(array('%', '_'), array('\\%', '\\_'), $str);
}

return $str;
}

本文标题:CodeIgniter load database 分析

文章作者:Syncher

发布时间:2018年03月31日 - 05:03

最后更新:2018年08月14日 - 21:08

原始链接:https://0x400.com/2018-03-31-php-codeigniter-load-database-analysis.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。