Home > Article > Backend Development > How to connect to distributed database in Thinkphp
Thinkphp is a mainstream framework in China, and I believe there must be many people using it. Today we will take a look at how to connect to a distributed database in Thinkphp.
Of course, we are not here to explain how to use the model to add, delete, modify, and query the database. We are doing an analysis of its underlying connection code, which can help you better understand thinkphp's operation of the database. To facilitate our future use.
1. Single database connection
When used, the connection configuration of a single database is very simple. We only need to configure some information in the configuration file.
'DB_TYPE' => 'mysql', 'DB_HOST' => '192.168.5.102', 'DB_NAME' => 'databasename', 'DB_USER' => 'user', 'DB_PWD' => 'password', 'DB_PORT' => '3306', 'DB_PREFIX' => 'onmpw_',
It can be used after the setting is completed. The default is a single database connection.
2. Distributed database connection
The connection to a single database is very simple. Let’s focus on analyzing the connection to the distributed database.
'DB_TYPE' => 'mysql', 'DB_HOST' => '192.168.5.191,192.168.5.88,192.168.5.103', 'DB_NAME' => 'test,test,test', 'DB_USER' => 'masteruser,slaveuser,slaveuser', 'DB_PWD' => 'masterpass,slavepass,slavepass', 'DB_PORT' => '3306', 'DB_PREFIX' => '', 'DB_DEPLOY_TYPE' => 1, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 'DB_RW_SEPARATE' => true, // 数据库读写是否分离 主从式有效 'DB_MASTER_NUM' => 1, // 读写分离后 主服务器数量 'DB_SLAVE_NO' => '', // 指定从服务器序号
Follow the above configuration to connect to the distributed database.
Let’s look at the following options
'DB_HOST'
Distributed database, if there are several servers, you need to fill in several server addresses, and use separated by commas. If it is a master-slave distribution, the previous address must be the address of the master database.
For the following user names, passwords, listening ports, etc., of course, write down as many as you have. If the username and password of each database are the same, you can only write one.
The code for parsing these options is as follows
$_config['username'] = explode(',',$this->config['username']); $_config['password'] = explode(',',$this->config['password']); $_config['hostname'] = explode(',',$this->config['hostname']); $_config['hostport'] = explode(',',$this->config['hostport']); $_config['database'] = explode(',',$this->config['database']); $_config['dsn'] = explode(',',$this->config['dsn']); $_config['charset'] = explode(',',$this->config['charset']); ‘DB_DEPLOY_TYPE’=>1
1 means distributed, 0 means centralized (that is, a single server).
The implementation of this option is in the class Think\Db\Dirver
protected function initConnect($master=true) { if(!empty($this->config['deploy'])) // 采用分布式数据库 $this->_linkID = $this->multiConnect($master); else // 默认单数据库 if ( !$this->_linkID ) $this->_linkID = $this->connect(); }
$this->config['deploy'] represents the 'DB_DEPLOY_TYPE' configuration option, the above configuration They have been parsed before use, and the configuration items are in the $this->config array. As for how to parse the configuration file, we will not introduce it here. Those who are interested can refer to the Think\Db class.
$this->The multiConnect() function is used for distributed connections. If the 'DB_DEPLOY_TYPE' option is set to 1, this function will be executed. Otherwise, execute the $this->connect() function directly.
‘DB_RW_SEPARATE’=>true
true means reading and writing are separated; false means reading and writing are not separated.
It should be noted here that the separation of reading and writing is based on the master-slave database system. When this option is set to true, the master database writes and the slave database reads.
if($this->config['rw_separate']){ // 主从式采用读写分离 if($master) // 主服务器写入 $r = $m; else{ if(is_numeric($this->config['slave_no'])) {// 指定服务器读 $r = $this->config['slave_no']; }else{ // 读操作连接从服务器 $r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1)); // 每次随机连接的数据库 } } }else{ // 读写操作不区分服务器 $r = floor(mt_rand(0,count($_config['hostname'])-1)); // 每次随机连接的数据库 }
$this->config[‘rw_separate’] When it is true, reading and writing are separated. When it is false, reading and writing are not separated. Why does the separation of reading and writing have to be master-slave? Because the slave server cannot write and can only read, if data is written to the slave server, the data cannot be synchronized. This will cause data inconsistency. Therefore, if our system is master-slave, we must use read-write separation. In other words, the DB_RW_SEPARATE option must be configured as true.
'DB_MASTER_NUM'=>1
The number after this option indicates the number of primary servers after read and write separation. Therefore this option is also used in master-slave database systems.
The following code selects the main server.
$m = floor(mt_rand(0,$this->config['master_num']-1));
When reading from a master-slave database, select the core code to read from the slave server
$r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1)); // 每次随机连接的数据库
where $this->config[‘master_num’] represents the number of master servers.
'DB_SLAVE_NO'=> ''
Specify the serial number of the slave server for reading data. If not set, the number of slave servers will be calculated based on the number of master servers, and then one will be randomly selected for reading.
if(is_numeric($this->config['slave_no'])) {// 指定服务器读 $r = $this->config['slave_no']; }else{ // 读操作连接从服务器 $r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1)); // 每次随机连接的数据库 }
The above is a simple explanation of the implementation code of the role of each option.
Let’s take a look at the connection part
if($m != $r ){ $db_master = array( 'username' => isset($_config['username'][$m])?$_config['username'][$m]:$_config['username'][0], 'password' => isset($_config['password'][$m])?$_config['password'][$m]:$_config['password'][0], 'hostname' => isset($_config['hostname'][$m])?$_config['hostname'][$m]:$_config['hostname'][0], 'hostport' => isset($_config['hostport'][$m])?$_config['hostport'][$m]:$_config['hostport'][0], 'database' => isset($_config['database'][$m])?$_config['database'][$m]:$_config['database'][0], 'dsn' => isset($_config['dsn'][$m])?$_config['dsn'][$m]:$_config['dsn'][0], 'charset' => isset($_config['charset'][$m])?$_config['charset'][$m]:$_config['charset'][0], ); } $db_config = array( 'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0], 'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0], 'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0], 'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0], 'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0], 'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0], 'charset' => isset($_config['charset'][$r])?$_config['charset'][$r]:$_config['charset'][0], ); return $this->connect($db_config,$r,$r == $m ? false : $db_master);
Seeing this, I think everyone should understand the role of $r and $m when introducing the code for each configuration option above. .
Now let's look at $r == $m ? false : $db_master. If the database reading and writing are not separated, and the reading and writing is a server, the value passed to the connect function is false. Or if the master-slave write is separated, the value passed to connect is also false. From the above code we see that if $r and $m are not equal, $db_master will be set. In fact, it is equivalent to a backup. If the selected $r server fails and cannot be connected, it will connect to $db_master.
The third parameter of the connect() function actually indicates whether to choose an alternate connection when the $db_config server connection fails. False means no reconnection, other values mean reconnection.
The core code is as follows
try{ if(empty($config['dsn'])) { $config['dsn'] = $this->parseDsn($config); } if(version_compare(PHP_VERSION,'5.3.6','<=')){ // 禁用模拟预处理语句 $this->options[PDO::ATTR_EMULATE_PREPARES] = false; } $this->linkID[$linkNum] = new PDO( $config['dsn'], $config['username'], $config['password'],$this->options); }catch (\PDOException $e) { if($autoConnection){ //$autoConnection不为false,而是默认的主服务器 trace($e->getMessage(),'','ERR'); return $this->connect($autoConnection,$linkNum); //出现异常,使用递归函数重新连接 }elseif($config['debug']){ E($e->getMessage()); } }
In this way, for the master-slave type, $r and $m will definitely not be the same. Therefore, if the selected slave server fails when reading data, the master server will be the backup and will eventually go to the master server to read. It can ensure the timeliness of data reading.
However, I always feel that it is not perfect yet. If there are multiple slave servers, and both the slave server and the master server selected during reading fail, then the data reading will fail. At this time, it should be more secure if other slave servers can be read again. Of course, the current functions of thinkphp are quite complete and sufficient for our use. But I still hope that thinkphp will become more and more perfect in the future.
I hope the above introduction can be helpful to everyone when using thinkphp for development.
Related recommendations:
ThinkPHP5 framework basic knowledge, development specifications and directory structure
ThinkPHP file caching class code sharing
ThinkPHP Db and model performance evaluation
The above is the detailed content of How to connect to distributed database in Thinkphp. For more information, please follow other related articles on the PHP Chinese website!