数据库读写分离技术,可用于缓解数据库服务器高并发所引起的问题。
常用的数据库读写分离技术,主要有以下几种:
读操作:CRUD中的R操作(read),自多个从库读取数据。
写操作:CRUD中的CUD操作(create、update、delete),向主库写入数据。
主从复制:将主库的数据通过 binlog 日志(二进制日志)同步更新到从库。
这里以 Yii 2.0 基础版为例
Yii 2.0 中控制数据库连接的类为
E:\xampp\htdocs\basic\vendor\yiisoft\yii2\db\Connection.php文件,可以在其中找到相应的属性,如:
$slaves、
$slaveConfig等。
注意:读写分离技术是要和主从复制技术结合起来使用的,而主从复制中的“主”指的是MySQL主服务器上的数据库,“从”指的是MySQL从服务器上的数据库,且这种复制是基于数据库级别的,为此从服务器中的数据库名称必须和主服务器中的数据库名称保持一致,那么,要想实现主从复制,我们至少要有两个MySQL服务器(最好是两个MySQL服务器分别位于不同的主机上,或者在一个主机上安装两个MySQL,端口不同即可)。
由此可知,下面的数据库配置是和主从复制技术是相冲突的,如果想让 Yii 的读写分离和MySQL的主从复制有效的结合起来,还需稍微更改一下db.php配置文件,方法很简单,这里就不作赘述。
一主多从:
例如,现在有一个主库(test),两个从库(slave1,slave2),
为了方便演示,我们将两个从数据库建立在和主数据库相同的主机中的同一个MySQL服务器上,真正的项目禁止这样做。
在
/basic/config/db.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 |
<?php
return
[
'class'
=>
'yii\db\Connection'
,
'dsn'
=>
'mysql:host=localhost;port=3306;dbname=test'
,
'username'
=>
'root'
,
'password'
=>
''
,
'charset'
=>
'utf8'
,
//一主多从
'slaves'
=>
[
[
'dsn'
=>
'mysql:host=localhost;dbname=slave1'
,
'charset'
=>
'utf8'
,],
[
'dsn'
=>
'mysql:host=localhost;dbname=slave2'
,
'charset'
=>
'utf8'
,],
],
'slaveConfig'
=>
[
'username'
=>
'root'
,
'password'
=>
''
,
'attributes'
=>
[
// use a smaller connection timeout
PDO
::
ATTR_TIMEOUT
=>
10
,
],
],
];
|
然后,在MySQL数据库中,新建两个从数据库(slave1 和 slave2),然后将主库(test)中的 basic_user 表复制到两个从库,并将 id = 1 的记录的 username 的值,分为修改为
jack_slave1 和
jack_slave2,以便下面演示时,可以清楚的知道请求访问的是哪一个数据库。
在控制器层,添加 TryController.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
26
27
28
29
30
31
32
33
34
35
36 |
<?php
namespace
app\controllers
;
use
yii\web\Controller
;
class
TryController
extends
Controller
{
/**
* 主库的写操作(增、删、改)
*/
public
function
actionMaster
(){
// 写操作只会操作主数据库 test
try
{
$db
=
\Yii
::
$app
->
db
;
$sql
=
"INSERT basic_user(username,passwd) VALUES('user001','123'),('user002','123')"
;
$cmd
=
$db
->
createCommand
(
$sql
);
$res
=
$cmd
->
execute
();
echo
$res
;
}
catch
(
\Exception
$e
)
{
echo
$e
->
getMessage
();
}
}
/**
* 从库的读操作(查)
*/
public
function
actionSlave
(){
// 读操作只会由从数据库(slave1或slave2)读取,多次刷新,可发现有时访问slave1,有时访问slave2(负载均衡)
// 如果某个从数据库出现了故障(如:slave1数据库不存在或slave1所在的主机宕机了),Yii 会自动屏蔽掉salve1,进而只读取未发生故障的salve2数据库(从库故障自动摘除)
$db
=
\Yii
::
$app
->
db
;
$cmd
=
$db
->
createCommand
(
'SELECT * FROM basic_user where id<=:id'
,
array
(
':id'
=>
4
));
$res
=
$cmd
->
queryAll
();
echo
json_encode
(
$res
);
}
}
|
在浏览器地址栏,分别访问以下地址,查看效果:
http://basic.com/try/master
http://basic.com/try/slave
多主多从:
例如,现在有两个主库(test、test2),三个从库(slave1、slave2、slave2),两个主数据库互为主从关系,三个从数据库都是主库的从库。
在
/basic/config/db.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
26
27
28
29
30
31
32
33
34
35 |
<?php
return
[
'class'
=>
'yii\db\Connection'
,
'charset'
=>
'utf8'
,
//多主多从
'masters'
=>
[
[
'dsn'
=>
'mysql:host=localhost;dbname=master1'
,
'charset'
=>
'utf8'
,],
[
'dsn'
=>
'mysql:host=localhost;dbname=master2'
,
'charset'
=>
'utf8'
,],
],
'masterConfig'
=>
[
'username'
=>
'root'
,
'password'
=>
''
,
'attributes'
=>
[
// use a smaller connection timeout
PDO
::
ATTR_TIMEOUT
=>
10
,
],
],
'slaves'
=>
[
[
'dsn'
=>
'mysql:host=localhost;dbname=slave1'
,
'charset'
=>
'utf8'
,],
[
'dsn'
=>
'mysql:host=localhost;dbname=slave2'
,
'charset'
=>
'utf8'
,],
[
'dsn'
=>
'mysql:host=localhost;dbname=slave3'
,
'charset'
=>
'utf8'
,],
],
'slaveConfig'
=>
[
'username'
=>
'root'
,
'password'
=>
''
,
'attributes'
=>
[
// use a smaller connection timeout
PDO
::
ATTR_TIMEOUT
=>
10
,
],
],
];
?>
|
然后,在MySQL数据库中新建第三个从库(slave3),并新建两个主库(master1、master2),并将相应的数据表拷贝进去。
访问地址
http://basic.com/try/master,查看写操作的效果,可发现:写操作只会在某一个主库进行,多次请求该地址,有时写操作在 master1 进行,有时写操作在 master2 进行。(2个主库自动实现负载均衡)
访问地址
http://basic.com/try/slave,查看读操作的效果,可发现:读操作只会访问某一个从库,多次请求该地址,有时读操作在 slave1 进行,有时在 slave2 进行,有时在 slave3 进行。(3个从库自动实现负载均衡)
注意:多主多从时,不仅3个从库要与主库实现主从复制,2个主库之间也要实现主从复制(因为2个主库是互为主从的,必须通过主从复制,保持两个主库的数据是同步的)。
强制读主:
通过主从复制实现数据同步的技术,虽然可以保持从库和主库的数据一致,但这种数据的同步是存在延时的,也就是说,可能在某一个时间点,写操作更改了主库的数据,但从库的数据还未来得及和主库同步。我们又迫切希望查询操作可以读取到最新的数据,此时,就可以使用强制读主的技术,让读操作强制从主库查询数据。
当某条查询语句对数据的及时性要求很高时,我们就可局部实现强制读主,以获取最新的数据。
强制读主技术,是通过
useMaster(callable $callback) 方法来实现的。
这里,我们在
TryController.php 控制器文件中,新增一个方法:
1
2
3
4
5
6
7
8
9
10
11 |
/**
* 强制读主(强制自主库查询数据)
*/
public function actionForceRead(){
//如果有多个主库,强制读主也是自动实现负载均衡的
$db = \Yii::$app->db;
$res = $db->useMaster(function($db){
return $db->createCommand('SELECT * FROM basic_user where id<=:id', array(':id'=>4))->queryAll();
});
echo json_encode($res);
}
|
然后,在浏览器地址栏访问:
http://basic.com/try/force-read
,就可查看到对应的效果。
Yii 2.0 框架 读写分离 的优点:
- 多个从库自动实现负载均衡
- 从库故障自动摘除
- 主库自动实现负载均衡(如果有多个主库)
- 强制自主库查询数据(强制读主)