数据库配置
数据库配置文件为 config/database.php,以通道(Channel)为组织单元管理数据库连接:每个通道对应一组驱动与连接选项,并持有独立的连接池(Connection Pool)。
本文依据框架默认配置
config/database.php及源码src/Database/DbManager.php、src/Database/Channel/PDO/{PDOChannel,PDOConfig,DriverType}.php编写。
配置文件结构
<?php
// config/database.php
use Viswoole\Database\Channel\PDO\PDOChannel;
use Viswoole\Database\Facade\Db;
return [
// 默认通道名称
'default' => env('DATABASE_DEFAULT', 'default'),
// 是否开启调试模式(打印/记录 SQL 运行信息)
'debug' => env('app_debug', true),
// 调试信息保存方式:1 控制台,2 日志文件,3 同时保存(位运算组合)
'info_save_manner' => Db::DEBUG_SAVE_CONSOLE | Db::DEBUG_SAVE_LOGGER,
// XA 两阶段提交事务配置(详见「XA 配置」一节)
'xa' => [
// 是否在每个 worker 启动时自动执行 XA 崩溃恢复(默认关闭,
// 悬挂事务经 php viswoole xa:recover 手动/定时收敛)
'auto_recovery' => env('DATABASE_XA_AUTO_RECOVERY', false),
// journal 表所在通道名,留空使用默认通道
'journal_channel' => env('DATABASE_XA_JOURNAL_CHANNEL', ''),
// journal 表名,首次执行 XA 事务时自动建表
'journal_table' => env('DATABASE_XA_JOURNAL_TABLE', 'viswoole_xa_journal'),
],
// 通道列表
'channels' => [
'default' => [
// 驱动类,必须继承 Viswoole\Database\Channel
'driver' => PDOChannel::class,
// 驱动构造参数
'options' => [
'host' => env('DATABASE_HOST', '127.0.0.1'),
'port' => (int)env('DATABASE_PORT', 3306),
'database' => env('DATABASE_NAME', ''),
'username' => env('DATABASE_USER', 'root'),
'password' => env('DATABASE_PASSWORD', '123456'),
],
],
],
];顶层配置
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
default | string | default | 默认使用的通道名称,Db::channel() 不传参时使用 |
debug | bool | true | 调试模式开关,控制 SQL 运行信息的输出(生产环境建议关闭) |
info_save_manner | int | 3 | 调试信息保存方式,按位组合:1 控制台、2 日志文件 |
channels | array | — | 通道列表,键为通道名(不区分大小写) |
xa | array | 见下 | XA 事务 journal 配置,见下节 |
调试信息保存方式
info_save_manner 使用 Db 门面提供的位常量,通过 | 组合:
| 常量 | 值 | 说明 |
|---|---|---|
Db::DEBUG_SAVE_CONSOLE | 1 | SQL 信息输出到终端控制台 |
Db::DEBUG_SAVE_LOGGER | 2 | SQL 信息写入日志文件 |
// 仅写入日志文件
'info_save_manner' => Db::DEBUG_SAVE_LOGGER,运行时切换调试模式
调试配置通过 Swoole\Table 跨进程共享,可在运行中动态调整:Db::setDebug(bool $debug) 开关调试,Db::setDebugInfoSaveManner(int $manner) 调整保存方式,对所有 Worker 进程即时生效。
通道配置(channels)
每个通道包含两个键:
| 配置项 | 类型 | 说明 |
|---|---|---|
driver | string | 通道驱动类名,须继承 Viswoole\Database\Channel,内置 PDOChannel |
options | array | 传给驱动构造函数的命名参数 |
PDO 通道选项(options)
options 的键与 PDOChannel 构造函数参数一一对应:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type | DriverType | DriverType::MYSQL | 驱动类型枚举,见下表 |
host | string | array | 127.0.0.1 | 主机地址;传 ['read' => ..., 'write' => ...] 数组启用读写分离 |
port | int | 3306 | 端口 |
sticky | bool | true | 粘性读:同一协程写入后的后续读操作复用写连接池 |
database | string | test | 数据库名称 |
username | string | root | 用户名 |
password | string | root | 密码 |
charset | string | | utf8mb4 | 字符集 |
timezone | string | null | | null | 连接时区:null 对齐应用时区,字符串使用指定时区(命名时区或 UTC 偏移),空串 '' 不设置;仅 MySQL 与 PostgreSQL 生效 |
options | array | [] | 附加 PDO 属性(如 PDO::ATTR_TIMEOUT) |
pool_max_size | int | 10 | 连接池最大连接数 |
pool_fill_size | int | 0 | 连接池初始填充数,0 表示不预填充 |
pool_timeout_time | int | 5 | 获取/归还连接的超时时间(秒) |
连接时区对齐
timezone 默认 null 时连接时区对齐应用时区(date_default_timezone_get()):MySQL 经 MYSQL_ATTR_INIT_COMMAND 注入 SET time_zone(命名时区自动转为 UTC 偏移,不依赖服务端时区表),PostgreSQL 经 DSN options 注入;SQLite/Oracle/SQL Server 暂不支持。用户已设置 MySQL INIT_COMMAND 时时区语句追加而非覆盖。
Unix Socket 连接
host 以 / 开头且以 .sock 结尾时,框架自动识别为 Unix Socket 路径并优先于 TCP 连接使用,例如 /tmp/mysql.sock。
驱动类型(DriverType)
type 使用 Viswoole\Database\Channel\PDO\DriverType 枚举,枚举值对应 PDO DSN 的驱动标识:
| 枚举成员 | PDO 驱动 | 数据库 |
|---|---|---|
DriverType::MYSQL | mysql | MySQL / MariaDB |
DriverType::POSTGRESQL | pgsql | PostgreSQL |
DriverType::ORACLE | oci | Oracle |
DriverType::SQLite | sqlite | SQLite |
DriverType::SQLServer | sqlsrv | SQL Server |
连接池参数
Swoole 常驻内存下连接不能每次请求重建,框架为每个通道维护连接池,协程内借出、用完自动归还:
- pool_max_size:池内连接数上限,即该通道的最大并发连接数。并发高峰时超出的协程会挂起等待空闲连接,等待超时抛出异常。应根据数据库
max_connections与 Worker 数量估算,避免打满数据库连接。 - pool_fill_size:服务启动时预创建的连接数,默认 0(首次请求时按需创建)。流量稳定的预热场景可设置为与
pool_max_size相同,避免冷启动时集中建连。 - pool_timeout_time:从池中获取连接(含事务期间持有连接)的最大等待秒数,默认 5。超时通常意味着并发数超过了
pool_max_size或存在慢查询占住连接。
请勿手动缓存连接
连接的借出与归还由连接池和协程上下文自动完成,业务代码不要把连接对象存到静态属性或容器单例中,否则会破坏协程间的连接隔离。
连接归属与 Master/Manager 短连接
连接池归 Worker 进程所有:连接只在 Worker/TaskWorker 触发 workerStart 时独立建立与计数,总连接数 = 单 Worker 池内占用 × Worker 数量,Master/Manager 进程不会额外持有 N × pool_max_size 条连接。
若在服务启动阶段或 Master/Manager 进程回调(onStart / onManagerStart 等)中使用 Db,每次操作走一次性短连接(直连直关、不经过连接池):执行完毕连接即销毁,不会长期占用数据库连接数。服务启动之前(如 AppInitialized 事件、命令行脚本)进程处于 CLI 状态,协程内照常使用连接池,非协程脚本等价为短连接。
自定义 Swoole\Process 子进程默认同样走短连接;此类进程为常驻进程、如需使用连接池,可在进程回调首行调用 Viswoole\Core\Server\ProcessRole::markAsWorker()。
避免在非 Worker 进程高频操作数据库
短连接模式下每条语句各建一次连接(握手 + 认证),高频循环会产生大量 TIME_WAIT 套接字并显著增加耗时。此类场景有两种规避方式:通过 Task::emit 将任务投递到工作进程执行,由工作进程的连接池承接;或手动借还连接,在 pop/put 窗口内循环复用同一条连接执行原生 SQL:
$channel = Db::channel();
$conn = $channel->pop('write'); // 取连接(此时才建连)
try {
$stmt = $conn->prepare('SELECT * FROM orders WHERE status = ?');
$stmt->execute([1]);
$rows = $stmt->fetchAll();
// 循环内继续复用 $conn,不再逐语句建连
} finally {
$channel->put($conn); // 归还(此刻才关闭)
}读写分离
host 传入数组即可启用读写分离,读库与写库各自支持多个地址(字符串或字符串数组),多个读库之间按轮询(Round-Robin)分发:
use Viswoole\Database\Channel\PDO\PDOChannel;
'channels' => [
'default' => [
'driver' => PDOChannel::class,
'options' => [
// 读库走 192.168.1.20,写库走 192.168.1.10
'host' => [
'read' => ['192.168.1.20', '192.168.1.21'],
'write' => '192.168.1.10',
],
'database' => 'app',
'username' => 'root',
'password' => env('DATABASE_PASSWORD', ''),
],
],
],调度规则(见 PDOChannel 源码):
- SELECT 等查询语句路由到读库,其余语句路由到写库;
sticky = true(默认)时,同一协程内一旦发生写入,后续读操作继续复用写库,避免主从延迟读到旧数据;FOR UPDATE/LOCK IN SHARE MODE等锁定读强制路由到写库,保证锁语义成立;- 事务期间的所有操作统一使用写库连接。
读写分离场景下,原生查询 Db::query($sql, $bindings, true) 的第三个参数可强制从主库读取,详见查询构造器 — 原生查询。
多通道
在 channels 中定义多个通道即可实现多库访问,通过 Db::channel('通道名') 切换,不传参时使用 default 通道:
use Viswoole\Database\Facade\Db;
// 使用默认通道
Db::table('user')->where('id', 1)->find();
// 切换到 order 通道(订单库)
Db::channel('order')
->table('order')
->where('user_id', 1)
->select();
// 判断通道是否已注册
Db::hasChannel('order');ORM 模型通过 $channelName 属性绑定通道,详见 ORM 模型。
动态注册通道
除配置文件外,可在服务启动前通过 Db::addChannel() 注册通道:
use Viswoole\Database\Channel\PDO\PDOChannel;
use Viswoole\Database\Facade\Db;
// 通道实例、驱动类名或 ['driver' => ..., 'options' => [...]] 配置数组均可
Db::addChannel('order', [
'driver' => PDOChannel::class,
'options' => [
'host' => '192.168.1.30',
'database' => 'order',
'username' => 'root',
'password' => 'secret',
],
]);必须在服务启动前注册
addChannel() 需在 Swoole 服务启动之前调用(例如监听 AppInitialized 事件或服务提供者注册阶段)。在 Worker 进程运行期添加的通道仅存在于当前进程,不会同步到其他进程。
XA 配置
xa 节配置 XA 两阶段提交事务的 journal(提交意图日志,崩溃恢复的决策依据):
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
auto_recovery | bool | false | 是否在 worker 启动时自动执行 XA 崩溃恢复;关闭时经 php viswoole xa:recover 手动/定时收敛 |
journal_channel | string | ''(默认通道) | journal 表所在通道名,须为支持 XA 的 MySQL 通道 |
journal_table | string | viswoole_xa_journal | journal 表名,首次执行 XA 事务时自动建表(恢复路径只探测不建表,未使用 XA 的项目不会出现该表) |
journal 通道通常指向参与事务的库之一,不引入额外部署组件。更多说明见 XA 事务。
