MikroORM 7 读副本连接(Read Replica Connections)完全指南:配置、路由策略与源码实现
发布时间:2026/9/28 20:36:10来源:尧图网络
后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载本指南以 MikroORM 7 的官方文档 read-connections.md 为核心系统讲解如何通过replicas配置为 ORM 挂载多个只读数据库副本掌握读操作SELECT/COUNT自动分流、connectionType显式路由、事务内强制写连接等完整行为。读完本文你将能在一主多从的数据库架构下正确配置 MikroORM理解随机读副本的解析机制及其底层源码实现并安全地将裸 SQL 查询路由到读副本。一、核心概念写连接与读副本MikroORM 的数据库驱动IDatabaseDriver内部维护两类连接写连接write connection即主连接master在初始化配置时通过dbName、user、host等顶层选项指定所有写操作INSERT/UPDATE/DELETE以及事务内的全部操作都在此连接上执行读副本read replicas通过replicas选项声明的额外连接仅用于承担读流量从而把查询压力从主库分流到只读从库。从源码看SQL 驱动在构造时会为每个副本配置创建独立的连接实例// packages/sql/src/AbstractSqlDriver.ts#L110-L120 protected constructor( config: Configuration, platform: Platform, connection: ConstructorConnection, connector: string[], ) { super(config, connector); this.connection new connection(this.config); this.replicas this.createReplicas(conf new connection(this.config, conf, read)); this.platform platform; }createReplicas定义于 packages/core/src/drivers/DatabaseDriver.ts它会读取配置中的replicas数组并逐一构造副本连接。二、配置多个读副本replicas选项在MikroORM.init()时通过replicas数组声明读副本。关键设计是副本只需提供与主连接不同的字段其余配置如dbName、port、password等会自动从主连接继承。const orm await MikroORM.init({ entities: [Author, ...], dbName: my_database, user: master_user, host: master_host, preferReadReplicas: true, // 可选属性默认值为 true replicas: [ { name: read-1, host: read_host_1, user: read_user }, { name: read-2, host: read_host_2 }, // 未提供 user将从主连接继承 ], });配置继承的源码证据createReplicas中明确定义了从主连接继承的属性白名单packages/core/src/drivers/DatabaseDriver.ts#L879-L904const props [ dbName, clientUrl, host, port, user, password, multipleStatements, pool, name, driverOptions, ] as const; for (const conf of replicas) { const replicaConfig Utils.copy(conf) as Dictionary; for (const prop of props) { if (conf[prop]) { continue; } // 从主连接配置中补齐缺失字段 } }注意两点实操细节显式提供clientUrl的副本源码中会对「已提供clientUrl但缺失host/port/user/password的副本」做特殊处理不强行覆盖这些可推断字段避免冲突name字段用于日志标识配置类型中name的注释明确说明其用途是「使用副本时用于日志记录的连接名」packages/core/src/utils/Configuration.ts#L603。副本的name与preferReadReplicas、replicas是否存在共同决定了日志系统是否启用副本标识usesReplicas标志见 Configuration.ts#L209。replicas的类型为ConnectionOptions[]Configuration.ts#L1179因此每个副本都支持完整的连接配置项包括pool池化选项与driverOptions驱动级参数。三、默认路由策略随机读副本文档明确当解析读连接时默认策略是为所有不在事务内的读操作SELECT、COUNT随机分配一个读副本。const connection em.getConnection(); // 写连接 const readConnection em.getConnection(read); // 随机读副本连接这一随机逻辑在 packages/core/src/drivers/DatabaseDriver.ts#L225-L233 中实现getConnection(type: ConnectionType write): C { if (type write || this.replicas.length 0) { return this.connection; } const rand Utils.randomInt(0, this.replicas.length - 1); return this.replicas[rand]; }值得强调的两个边界行为未配置任何副本时即使请求read也会回退到写连接this.replicas.length 0分支随机策略是每次调用随机抽取多个副本间大致负载均衡但不保证严格轮询。em.getConnection(type)的type取值只有read | write两种。四、preferReadReplicas反转默认策略preferReadReplicas是全局配置开关默认值为true见 Configuration.ts#L175 的 DEFAULTS 定义。当设置为false时默认连接永远是写连接除非显式请求读副本。这在「读副本存在但只希望特定场景使用」的应用中非常有用。配置为false后即使执行的是纯读操作findOne默认也会落在写连接上// 假设配置了 preferReadReplicas: false const res5 await em.findOne(Author, 1); // 写连接——即使是读操作 const res6 await em.findOne(Author, 1, { connectionType: read }); // 除非显式要求读副本底层解析函数连接类型的最终裁决逻辑集中在 SQL 驱动的resolveConnectionTypepackages/sql/src/AbstractSqlDriver.ts#L3140-L3154其优先级为事务上下文优先若存在ctx事务无条件返回write显式connectionType次之调用方显式指定时直接采用preferReadReplicas兜底为true时读操作默认走read否则走write。protected resolveConnectionType(args: { ctx?: Transaction; connectionType?: ConnectionType }): ConnectionType { if (args.ctx) { return write; } if (args.connectionType) { return args.connectionType; } if (this.config.get(preferReadReplicas)) { return read; } return write; }这段源码同时印证了本文第五节、第六节的行为无论connectionType如何显式声明事务都会覆盖它。五、操作级显式路由connectionType选项你可以通过各操作 Options 参数上的connectionType属性为单个操作指定连接类型如FindOptions、CountOptions以及 QueryBuilder 的第三个参数。EntityManager 层面const res4 await em.findOne(Author, 1, { connectionType: write }); // 显式写连接 const res5 await em.findOne(Author, 1, { connectionType: read }); // 显式读副本FindOptions等选项类型中均包含可选的connectionType?: ConnectionType字段例如 packages/core/src/entity/EntityLoader.ts#L73并会传递给底层驱动参与连接解析。find、count、countBy、findOne等操作全部适用。QueryBuilder 层面createQueryBuilder的第三个参数用于声明连接类型const qb1 em.createQueryBuilder(Author); const res1 await qb1.select(*).execute(); // 随机读副本 const qb2 em.createQueryBuilder(Author, a, write); const res2 await qb2.select(*).execute(); // 写连接 const qb3 em.createQueryBuilder(Author); const res3 await qb3.update(...).where(...).execute(); // 写连接写操作永远走写连接注意第三个示例即使没有显式指定UPDATE/DELETE 等写操作也必然路由到写连接这是由操作语义决定的与preferReadReplicas无关。六、事务内一律使用写连接所有在事务内执行的查询无论读还是写都固定使用写连接这是保证事务隔离性与数据一致性的硬性约束// 事务内的所有查询都会使用写连接 await em.transactional(async em { const a await em.findOne(Author, 1); // 写连接 const b await em.findOne(Author, 1, { connectionType: read }); // 仍是写连接——我们处于事务中 a.name test; // 将在 flush 时于写连接上触发 update });即使在事务内显式传入{ connectionType: read }也不会路由到读副本。原因已在第四节源码中说明resolveConnectionType首先检查args.ctx一旦存在事务上下文就直接返回write。这避免了「事务中的修改未同步到从库、又从从库读到旧数据」这类经典脏读/不一致问题。测试用例 tests/features/read-replicas.test.ts 中专门验证了事务内connectionType: read仍落在写连接上的行为。七、裸 SQL 查询Raw SQL的读副本路由MikroORM 7 文档进一步补充了裸 SQL 的处理规则em.execute()默认走写连接因为裸 SQL 无法静态判断是读还是写——无论其返回结果是行集还是影响行数都可能包含写入语句。要显式将裸 SQL 路由到读副本需要在 options 中传入connectionType: read同时保留 EntityManager 会话上下文与取消cancellation选项const rows await em.execute(select * from author where age ?, [18], { connectionType: read, }); const author await em.execute(select * from author where id ?, [1], { method: get, connectionType: read, });裸 SQL 路由的三条边界规则事务优先处于活动事务中时事务连接始终优先于connectionType查询被固定在事务绑定的连接上RLS 会话设置使用事务级行级安全row-level security时隐式事务与 session 设置会在所选副本上应用无副本回退未配置副本时显式的read请求会回退到写连接。最重要的一条安全提醒只有当 SQL 确定可以在读副本上安全执行时才应请求读连接——MikroORM 不会解析 SQL 文本来判断其安全性。若把写语句发往只读副本将由数据库自身的只读限制来拒绝而非 ORM 帮你把关。八、测试验证与完整行为矩阵仓库测试 tests/features/read-replicas.test.ts 覆盖了preferReadReplicas为true默认与false两条路径下的完整行为包括findOne、count、countBy的显式connectionType: read | write路由以及事务内强制写连接的行为测试中通过orm.config.set(preferReadReplicas, false)动态切换配置说明该选项运行时亦可调整。综合文档与源码连接路由的完整决策矩阵如下场景默认行为显式connectionType行为非事务读操作SELECT/COUNT/find/countpreferReadReplicas: true时随机读副本按指定类型路由read/write非事务写操作UPDATE/DELETE/insert写连接始终写连接事务内任意操作写连接写连接事务优先preferReadReplicas: false时非事务读操作写连接按指定类型路由裸 SQLem.execute写连接显式connectionType: read可路由到副本未配置任何副本写连接请求read时回退写连接九、小结MikroORM 7 的读副本体系围绕「默认随机分流 显式覆盖 事务强制写」三条原则设计通过replicas数组声明从库缺失的连接字段自动从主连接继承preferReadReplicas默认true控制全局默认策略可整体反转connectionType在操作级提供精确控制适用于 find/count/QueryBuilder/裸 SQL事务上下文在resolveConnectionType中拥有最高优先级保证一致性裸 SQL 默认走写连接如需走读副本必须显式声明且自行确保语句为只读。在配置生产环境时建议为每个副本设置清晰的name以便日志排查并依据实际从库延迟与一致性要求决定是否全局开启preferReadReplicas。更完整的配置项说明可参考 Configuration.ts 的类型定义与 docs/docs/read-connections.md当前主线文档版本。赞分享后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载相关推荐MikroORM 只读副本连接Read Replica Connections完整指南配置、解析策略与源码实现MikroORM 只读副本连接Read Replica Connections完整指南配置、解析策略与源码实现 导读 本文聚焦 MikroORM 的 只读后端MikroORM 只读副本连接Read Replicas实战指南配置、连接路由策略与源码级原理MikroORM 只读副本连接Read Replicas实战指南配置、连接路由策略与源码级原理 MikroORM 是构建在 Data Mapper、Uni后端Simple USB Terminal高级应用如何利用前台服务实现后台数据缓冲Simple USB Terminal高级应用如何利用前台服务实现后台数据缓冲 Simple USB Terminal是一款专为Android设备设计的串口终后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网