## 使用主从数据库 >[success] Redis 也是同样的配置方法,只是 ::class 不同 框架中获取连接的主力方式是通过连接池,而 mix 的连接池是通过拨号器的方式由用户在拨号器自主创建连接,得益于这种灵活的设计,我们可以通过:新增一个从库单例 ConnectionPool::class 通过控制拨号器实例化不配置的连接而实现用户获取连接时获取到不同的数据库从库,从而达到主从的目的。 ## 开始 >[danger] 使用该功能需先阅读 "核心功能 > 依赖注入" 章节 首先在 .env 增加从库使用的连接信息: ~~~ DATABASE_DSN_SLAVE1='mysql:host=127.0.0.1;port=3306;charset=utf8;dbname=test' DATABASE_USERNAME_SLAVE1=root DATABASE_PASSWORD_SLAVE1= DATABASE_DSN_SLAVE2='mysql:host=127.0.0.1;port=3306;charset=utf8;dbname=test' DATABASE_USERNAME_SLAVE2=root DATABASE_PASSWORD_SLAVE2= ~~~ 然后在 manifest.php 为每个从库配置一个 Connection::class 的依赖配置: - 配置中使用了上面配置的新环境变量 ~~~ // Database Slave1 [ // 名称 'name' => 'dbSlave1', // 类路径 'class' => \Mix\Database\Connection::class, // 初始化方法 'initMethod' => 'connect', // 属性注入 'properties' => [ // 数据源格式 'dsn' => getenv('DATABASE_DSN_SLAVE1'), // 数据库用户名 'username' => getenv('DATABASE_USERNAME_SLAVE1'), // 数据库密码 'password' => getenv('DATABASE_PASSWORD_SLAVE1'), // 驱动连接选项: http://php.net/manual/zh/pdo.setattribute.php 'attributes' => [ // 设置默认的提取模式: \PDO::FETCH_OBJ | \PDO::FETCH_ASSOC \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, // 超时 \PDO::ATTR_TIMEOUT => 5, ], // 事件调度器 'eventDispatcher' => ['ref' => \Mix\Event\EventDispatcher::class], ], ], // Database Slave2 [ // 名称 'name' => 'dbSlave2', // 类路径 'class' => \Mix\Database\Connection::class, // 初始化方法 'initMethod' => 'connect', // 属性注入 'properties' => [ // 数据源格式 'dsn' => getenv('DATABASE_DSN_SLAVE2'), // 数据库用户名 'username' => getenv('DATABASE_USERNAME_SLAVE2'), // 数据库密码 'password' => getenv('DATABASE_PASSWORD_SLAVE2'), // 驱动连接选项: http://php.net/manual/zh/pdo.setattribute.php 'attributes' => [ // 设置默认的提取模式: \PDO::FETCH_OBJ | \PDO::FETCH_ASSOC \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, // 超时 \PDO::ATTR_TIMEOUT => 5, ], // 事件调度器 'eventDispatcher' => ['ref' => \Mix\Event\EventDispatcher::class], ], ], ~~~ 新增一个拨号器 DatabaseSlaveDialer::class: - 通过多个依赖名称轮转实例化数据库连接对象,实现创建不同从库的连接 ~~~ <?php namespace App\Common\Dialers; use Mix\Database\Connection; use Mix\Pool\DialerInterface; /** * Class DatabaseSlaveDialer * @package App\Common\Dialers * @author liu,jian <coder.keda@gmail.com> */ class DatabaseSlaveDialer implements DialerInterface { /** * @var array */ protected $slaveBeanNames = ['dbSlave1', 'dbSlave2']; /** * 拨号 * @return Connection */ public function dial() { // 轮转 $beanName = array_pop($this->slaveBeanNames); array_unshift($this->slaveBeanNames, $beanName); // 创建一个连接并返回 return context()->get($beanName); } } ~~~ 在 manifest.php 增加名称为 `dbPoolSlave` 的 ConnectionPool::class 依赖配置: - 配置中 dialer 属性引用了 DatabaseSlaveDialer::class 完成拨号 ~~~ // Database Slave 连接池 [ // 名称 'name' => 'dbPoolSlave', // 作用域 'scope' => \Mix\Bean\BeanDefinition::SINGLETON, // 类路径 'class' => \Mix\Database\Pool\ConnectionPool::class, // 属性注入 'properties' => [ // 最多可空闲连接数 'maxIdle' => 5, // 最大连接数 'maxActive' => 50, // 拨号器 'dialer' => ['ref' => \App\Common\Dialers\DatabaseSlaveDialer::class], ], ], // Database连接池拨号 [ // 类路径 'class' => \App\Common\Dialers\DatabaseSlaveDialer::class, ], ~~~ 业务代码中调用: >[danger] 与传统框架自动通过 SQL 切换主从的方式不同,mix 的这种主从实现其实只是扩展了多个从库调用的方式,因此需要用户自己在代码中选择使用主库还是从库。 - 当使用主库时,使用默认配置的 `dbPool` 依赖名称获取池实例 ``` /** @var \Mix\Database\Pool\ConnectionPool $dbPool */ $dbPool = context()->get('dbPool'); $db = $dbPool->getConnection(); // ... $db->release(); // 不手动释放的连接不会归还连接池,会在析构时丢弃 ``` - 当使用从库时,使用刚刚上面配置的 `dbPoolSlave` 依赖名称获取池实例 ~~~ /** @var \Mix\Database\Pool\ConnectionPool $dbPool */ $dbPool = context()->get('dbPoolSlave'); $db = $dbPool->getConnection(); //... $db->release(); // 不手动释放的连接不会归还连接池,会在析构时丢弃 ~~~