Vai al contenuto principale

Configurazione di multiple sorgenti dati, database, schemi e replicazione

Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

Utilizzo di multiple sorgenti dati

Per utilizzare multiple sorgenti dati connesse a database diversi, è sufficiente creare diverse istanze di 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,
})

Utilizzo di più database all'interno di una singola sorgente dati

Per utilizzare più database in una singola sorgente dati, è possibile specificare il nome del database per ogni entità:

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
}

L'entità User verrà creata nel database secondDB mentre l'entità Photo nel database thirdDB. Tutte le altre entità saranno create nel database predefinito definito nelle opzioni della sorgente dati.

Se desideri selezionare dati da un database differente, devi semplicemente fornire un'entità:

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

Questo codice produrrà la seguente query SQL (variabile in base al tipo di database):

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

Puoi anche specificare un percorso della tabella invece dell'entità:

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

Questa funzionalità è supportata solo nei database mysql e mssql.

Utilizzo di più schemi all'interno di una singola sorgente dati

Per utilizzare più schemi nelle tue applicazioni, imposta semplicemente schema su ogni entità:

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
}

L'entità User verrà creata nello schema secondSchema e l'entità Photo nello schema thirdSchema. Tutte le altre entità saranno create nel database predefinito definito nelle opzioni della sorgente dati.

Se desideri selezionare dati da uno schema differente, devi semplicemente fornire un'entità:

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

Questo codice produrrà la seguente query SQL (variabile in base al tipo di database):

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

Puoi anche specificare un percorso della tabella invece dell'entità:

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()

Questa funzionalità è supportata solo nei database postgres e mssql. In mssql puoi anche combinare schemi e database, ad esempio:

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

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

@Column()
firstName: string

@Column()
lastName: string
}

Replicazione

Puoi configurare la replicazione lettura/scrittura utilizzando TypeORM. Esempio di opzioni di replicazione:

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",
},
],
},
})

Con slave di replicazione definiti, TypeORM invierà per impostazione predefinita tutte le query possibili agli slave.

  • tutte le query eseguite dai metodi find o da SelectQueryBuilder utilizzeranno un'istanza slave casuale

  • tutte le query di scrittura eseguite da update, create, InsertQueryBuilder, UpdateQueryBuilder, ecc. utilizzeranno l'istanza master

  • tutte le query raw eseguite chiamando .query() utilizzeranno l'istanza master

  • tutte le operazioni di aggiornamento dello schema vengono eseguite utilizzando l'istanza master

Selezione esplicita delle destinazioni delle query

Per impostazione predefinita, TypeORM invierà tutte le query di lettura a uno slave di lettura casuale e tutte le scritture al master. Ciò significa che quando aggiungi per la prima volta le impostazioni replication alla tua configurazione, qualsiasi query runner di lettura esistente che non specifichi esplicitamente una modalità di replicazione inizierà a utilizzare uno slave. Questo è vantaggioso per la scalabilità, ma se alcune di queste query devono restituire dati aggiornati, allora è necessario passare esplicitamente una modalità di replicazione quando si crea un query runner.

Se desideri utilizzare esplicitamente il master per query di lettura, passa un ReplicationMode esplicito durante la creazione del tuo QueryRunner:

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()
}

Se desideri utilizzare uno slave nelle query raw, passa slave come modalità di replicazione durante la creazione di un query runner:

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

Nota: Le istanze di QueryRunner create manualmente devono essere rilasciate esplicitamente. Se non rilasci i query runner, manterranno una connessione riservata dal pool, impedendo ad altre query di utilizzarla.

Modifica della destinazione predefinita per le letture

Se non desideri che tutte le letture vadano per impostazione predefinita a un'istanza slave, puoi modificare la destinazione predefinita delle query di lettura passando defaultMode: "master" nelle opzioni di replicazione:

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",
},
],
},
})

Con questa modalità, nessuna query andrà per impostazione predefinita agli slave di lettura, e dovrai scegliere esplicitamente di inviare query agli slave di lettura con chiamate .createQueryRunner("slave").

Se stai aggiungendo per la prima volta opzioni di replica a un'app esistente, questa è un'ottima scelta per garantire che il comportamento non cambi immediatamente, consentendoti di adottare gradualmente le repliche di lettura query runner per query runner.

Driver supportati

La replica è supportata dai driver di connessione MySQL, PostgreSQL, SQL Server, Cockroach, Oracle e Spanner.

La replica MySQL supporta opzioni di configurazione aggiuntive:

{
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"
}
}