当前位置:首页 > 问答 > 正文

Yii框架里头怎么搞多个数据库连接和操作,简单说说经验分享

在Yii框架里搞多个数据库连接,这事儿听起来好像挺高级,但其实弄明白了也就那么回事儿,我根据自己的经验,简单跟你唠唠怎么弄,以及一些容易踩的坑。

最核心的一步:配置。

你想用多个数据库,总得先告诉Yii这些数据库在哪儿、叫啥名、用户名密码是啥吧?这个就是在配置文件里完成的,我们主要在 config/db.php 或者主配置文件 config/web.php 里的 components 部分下手。

默认情况下,Yii给你配好了一个叫 db 的数据库连接,现在你想加第二个,比如是一个专门用来记录日志的数据库,或者是一个老的、需要读取数据的旧系统数据库,你只需要照着 db 的样子,再写一个配置项就行。

你的主数据库是MySQL,叫 main_db,然后还有一个SQLite数据库用来存缓存或者日志,叫 log_db,你的配置大概会长这样(根据Yii2的惯例,在 config/db.php 中返回一个数组,或在 config/web.phpcomponents 里配置):

return [
    // ... 其他配置 ...
    'components' => [
        // 主数据库,默认连接,名字就叫 'db'
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=main_db',
            'username' => 'root',
            'password' => 'password',
            'charset' => 'utf8',
        ],
        // 第二个数据库,专门用于日志,名字我们起为 'log_db'
        'log_db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'sqlite:/path/to/database/log.db',
            'username' => '',
            'password' => '',
            'charset' => 'utf8',
        ],
    ],
];

看到了吗?很简单,就是给 components 数组里再加一个元素,键名(log_db)就是你给这个连接起的名字,值就是它的连接参数,跟第一个 db 的结构一模一样,这样,Yii应用启动的时候,就会创建两个数据库连接对象备用。

Yii框架里头怎么搞多个数据库连接和操作,简单说说经验分享

配置好了,接下来就是怎么用的问题了。

这里有几个常见的用法场景:

  1. 在模型(Model)里指定用哪个库: 这是最常用的情况,Yii的ActiveRecord模型默认会使用那个叫 db 的连接,如果你的模型(比如一个叫 User 的模型)数据是存在主库里的,那你什么都不用改,照常写 User::find()->all() 就行。 但如果你有一个 Log 模型,它的数据是存在第二个 log_db 数据库里的,那你需要在 Log 模型类里重写一个叫 getDb() 的方法,告诉它:“别用默认的 db,去用我新配的 log_db”。

    Yii框架里头怎么搞多个数据库连接和操作,简单说说经验分享

    namespace app\models;
    use yii\db\ActiveRecord;
    class Log extends ActiveRecord
    {
        // 这个方法返回我们在配置中定义的组件名 'log_db'
        public static function getDb()
        {
            // 使用 Yii::$app->get('log_db') 来获取第二个数据库连接
            return \Yii::$app->get('log_db');
        }
    }

    这样以后,所有对 Log 模型的操作,Log::find()$log->save(),都会自动跑到 log_db 这个数据库里去操作,非常省心。

  2. 在代码里临时指定连接: 你可能不想或者不能在模型层面固定数据库连接,想临时用一下,比如你想直接在控制器或者某个服务类里, raw SQL 查询另一个数据库里的一张表(可能连模型都没建)。 这时候,你可以直接通过Yii的应用容器(Yii::$app)来获取你配置好的那个连接对象,然后像使用默认的 Yii::$app->db 一样去使用它。

    // 获取默认的 'db' 连接,执行SQL
    $mainUsers = Yii::$app->db->createCommand('SELECT * FROM user')->queryAll();
    // 获取我们配置的 'log_db' 连接,执行SQL
    $logRecords = Yii::$app->log_db->createCommand('SELECT * FROM operation_log')->queryAll();

    这种方式很灵活,随用随取。

一些经验分享和容易遇到的坑:

  • 分清主次: 一定要明确哪个是主数据库(db),负责主要的写操作,像事务、主从复制(如果配置了的话)通常都是围绕默认的 db 连接进行的,不要把重要的写操作随便放到次要的连接上,可能会出问题。
  • 注意事务的范围: 在Yii里,事务是绑定到某个具体的数据库连接上的。你不能开启一个跨多个数据库连接的大事务,你不能让主库 db 的更新和日志库 log_db 的插入在同一个事务里同生共死,它们是两个独立的连接,事务互不干扰,如果你需要保证两个库的操作一致性,得想别的办法,比如用消息队列或者最终一致性补偿机制,这就比较复杂了。
  • 模型关系(Relations)的陷阱: 这是个大坑!假如你有一个 User 模型在用默认的 db 连接,另一个 UserProfile 模型在用 log_db 连接,然后你在 User 模型里定义了一个关系 getProfile() 关联到 UserProfile,当你调用 $user->profile 时,Yii会尝试用 User 模型自己的数据库连接(也就是 db)去 JOIN 或查询 UserProfile 表,但 UserProfile 表根本不在 db 这个数据库里!这肯定会报“表不存在”的错误。建立关系的两个模型,最好是在同一个数据库里,否则关系查询会非常麻烦,几乎没法直接用Yii的关系功能,得手动写查询。
  • 配置管理: 当项目要上线到测试、生产环境时,数据库密码、地址都会变,最好把不同环境的数据库配置分开管理,比如用环境变量(.env 文件)来注入这些敏感信息,避免把密码硬写在代码配置文件里,Yii的 dotenv 扩展可以帮上忙。

在Yii里搞多数据库连接,核心就是“配置”和“指定”,配置好多个连接组件,然后在用的时候,通过模型里的 getDb() 方法或者直接通过 Yii::$app->组件名 来指定当前操作要用哪个连接,只要小心事务和模型关系这两个主要的坑,用起来还是挺顺手的,希望这些经验对你有帮助。