跳至主内容区

多数据源、多数据库、多模式与复制设置

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

使用多个数据源

要连接多个不同数据库的数据源,只需创建多个 DataSource 实例:

import { DataSource } from "typeorm"

const db1DataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "db1",
entities: [__dirname + "/entities/*{.js,.ts}"],
synchronize: true,
})

const db2DataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "db2",
entities: [__dirname + "/entities/*{.js,.ts}"],
synchronize: true,
})

在单个数据源中使用多个数据库

要在单个数据源中使用多个数据库, 您可以为每个实体指定数据库名称:

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity({ database: "secondDB" })
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
firstName: string

@Column()
lastName: string
}
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity({ database: "thirdDB" })
export class Photo {
@PrimaryGeneratedColumn()
id: number

@Column()
url: string
}

User 实体将在 secondDB 数据库中创建,Photo 实体将在 thirdDB 数据库中创建。 所有其他实体将在数据源选项中定义的默认数据库中创建。

若要从不同数据库查询数据,只需提供对应实体:

const users = await dataSource
.createQueryBuilder()
.select()
.from(User, "user")
.addFrom(Photo, "photo")
.andWhere("photo.userId = user.id")
.getMany() // userId is not a foreign key since its cross-database request

此代码将生成以下 SQL 查询(具体语法取决于数据库类型):

SELECT * FROM "secondDB"."user" "user", "thirdDB"."photo" "photo"
WHERE "photo"."userId" = "user"."id"

您也可以直接指定表路径替代实体:

const users = await dataSource
.createQueryBuilder()
.select()
.from("secondDB.user", "user")
.addFrom("thirdDB.photo", "photo")
.andWhere("photo.userId = user.id")
.getMany() // userId is not a foreign key since its cross-database request

此功能仅支持 MySQL 和 MSSQL 数据库。

在单个数据源中使用多个模式

要在应用中使用多个模式,只需为每个实体设置 schema

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity({ schema: "secondSchema" })
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
firstName: string

@Column()
lastName: string
}
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity({ schema: "thirdSchema" })
export class Photo {
@PrimaryGeneratedColumn()
id: number

@Column()
url: string
}

User 实体将在 secondSchema 模式中创建,Photo 实体将在 thirdSchema 模式中创建。 所有其他实体将在数据源选项中定义的默认数据库中创建。

若要从不同模式查询数据,只需提供对应实体:

const users = await dataSource
.createQueryBuilder()
.select()
.from(User, "user")
.addFrom(Photo, "photo")
.andWhere("photo.userId = user.id")
.getMany() // userId is not a foreign key since its cross-database request

此代码将生成以下 SQL 查询(具体语法取决于数据库类型):

SELECT * FROM "secondSchema"."question" "question", "thirdSchema"."photo" "photo"
WHERE "photo"."userId" = "user"."id"

您也可以直接指定表路径替代实体:

const users = await dataSource
.createQueryBuilder()
.select()
.from("secondSchema.user", "user") // in mssql you can even specify a database: secondDB.secondSchema.user
.addFrom("thirdSchema.photo", "photo") // in mssql you can even specify a database: thirdDB.thirdSchema.photo
.andWhere("photo.userId = user.id")
.getMany()

此功能仅支持 PostgreSQL 和 MSSQL 数据库。 在 MSSQL 中您还可以组合使用数据库和模式,例如:

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity({ database: "secondDB", schema: "public" })
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
firstName: string

@Column()
lastName: string
}

数据复制

您可以通过 TypeORM 设置读写复制功能。 复制配置示例:

const datasource = new DataSource({
type: "mysql",
logging: true,
replication: {
master: {
host: "server1",
port: 3306,
username: "test",
password: "test",
database: "test",
},
slaves: [
{
host: "server2",
port: 3306,
username: "test",
password: "test",
database: "test",
},
{
host: "server3",
port: 3306,
username: "test",
password: "test",
database: "test",
},
],
},
})

配置从实例后,TypeORM 默认会将所有可能的查询发送至从实例。

  • 所有通过 find 方法或 SelectQueryBuilder 执行的查询将使用随机的 slave 实例

  • 所有通过 updatecreateInsertQueryBuilderUpdateQueryBuilder 等方法执行的写操作将使用 master 实例

  • 所有通过调用 .query() 执行的原始查询将使用 master 实例

  • 所有模式更新操作将使用 master 实例执行

显式选择查询目标

默认情况下,TypeORM 会将所有读查询发送到随机的读从实例,所有写操作发送到主实例。这意味着当您首次在配置中添加 replication 设置时,任何未明确指定复制模式的现有读查询执行器将开始使用从实例。这有利于扩展性,但如果某些查询必须返回最新数据,则需要在创建查询执行器时显式传递复制模式。

若要在读查询中显式使用 master,请在创建 QueryRunner 时传递明确的 ReplicationMode

const masterQueryRunner = dataSource.createQueryRunner("master")
try {
const postsFromMaster = await dataSource
.createQueryBuilder(Post, "post", masterQueryRunner) // you can either pass QueryRunner as an optional argument with query builder
.setQueryRunner(masterQueryRunner) // or use setQueryRunner which sets or overrides query builder's QueryRunner
.getMany()
} finally {
await masterQueryRunner.release()
}

若要在原始查询中使用从实例,请在创建查询执行器时传递 slave 作为复制模式:

const slaveQueryRunner = dataSource.createQueryRunner("slave")
try {
const userFromSlave = await slaveQueryRunner.query(
"SELECT * FROM users WHERE id = $1",
[userId],
slaveQueryRunner,
)
} finally {
return slaveQueryRunner.release()
}

注意:手动创建的 QueryRunner 实例必须显式释放。若不释放查询执行器,它们将保持占用连接池中的连接,阻碍其他查询使用。

调整默认读目标

若您不希望所有读查询默认使用slave实例,可通过在复制选项中设置 defaultMode: "master" 来更改默认读查询目标:

const datasource = new DataSource({
type: "mysql",
logging: true,
replication: {
// set the default destination for read queries as the master instance
defaultMode: "master",
master: {
host: "server1",
port: 3306,
username: "test",
password: "test",
database: "test",
},
slaves: [
{
host: "server2",
port: 3306,
username: "test",
password: "test",
database: "test",
},
],
},
})

在此模式下,默认情况下不会有查询发送至读从实例,您需要通过显式调用 .createQueryRunner("slave") 来选择使用读从实例。

如果是首次在现有应用中添加复制选项,这是一个很好的选择,可以确保不会立即改变行为,而是可以逐个查询运行器地逐步采用读取副本。

支持的驱动程序

复制功能由 MySQL、PostgreSQL、SQL Server、Cockroach、Oracle 和 Spanner 连接驱动程序支持。

MySQL 复制支持额外的配置选项:

{
replication: {
master: {
host: "server1",
port: 3306,
username: "test",
password: "test",
database: "test"
},
slaves: [{
host: "server2",
port: 3306,
username: "test",
password: "test",
database: "test"
}, {
host: "server3",
port: 3306,
username: "test",
password: "test",
database: "test"
}],

/**
* If true, PoolCluster will attempt to reconnect when connection fails. (Default: true)
*/
canRetry: true,

/**
* If connection fails, node's errorCount increases.
* When errorCount is greater than removeNodeErrorCount, remove a node in the PoolCluster. (Default: 5)
*/
removeNodeErrorCount: 5,

/**
* If connection fails, specifies the number of milliseconds before another connection attempt will be made.
* If set to 0, then node will be removed instead and never re-used. (Default: 0)
*/
restoreNodeTimeout: 0,

/**
* Determines how slaves are selected:
* RR: Select one alternately (Round-Robin).
* RANDOM: Select the node by random function.
* ORDER: Select the first node available unconditionally.
*/
selector: "RR"
}
}