读取副本

当你的项目涉及一组读取副本实例,并且你需要一个方便的方法来管理来自读取副本的 SELECT 查询,以及在主实例上执行创建、删除和更新操作时,你可以利用 Drizzle 中的 withReplicas() 函数

PostgreSQL
MySQL
SQLite
import { sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/node-postgres';
import { boolean, jsonb, pgTable, serial, text, timestamp, withReplicas } from 'drizzle-orm/pg-core';

const usersTable = pgTable('users', {
  id: serial('id' as string).primaryKey(),
  name: text('name').notNull(),
  verified: boolean('verified').notNull().default(false),
  jsonb: jsonb('jsonb').$type<string[]>(),
  createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
});

const primaryDb = drizzle(new Pool({
  connectionString: "postgres://user:password@host:port/primary_db",
}));
const read1 = drizzle(new Pool({
  connectionString: "postgres://user:password@host:port/read_replica_1",
}));
const read2 = drizzle(new Pool({
  connectionString: "postgres://user:password@host:port/read_replica_2",
}));

const db = withReplicas(primaryDb, [read1, read2]);
import { drizzle } from "drizzle-orm/mysql2";
import mysql from "mysql2/promise";
import { boolean, mysqlTable, serial, text, withReplicas } from 'drizzle-orm/mysql-core';

const usersTable = mysqlTable('users', {
  id: serial('id' as string).primaryKey(),
  name: text('name').notNull(),
  verified: boolean('verified').notNull().default(false),
});

const primaryClient = await mysql.createConnection({
  host: "host",
  user: "user",
  database: "primary_db",
})
const primaryDb = drizzle(primaryClient);

const read1Client = await mysql.createConnection({
  host: "host",
  user: "user",
  database: "read_1",
})
const read1 = drizzle(read1Client);

const read2Client = await mysql.createConnection({
  host: "host",
  user: "user",
  database: "read_2",
})
const read2 = drizzle(read2Client);

const db = withReplicas(primaryDb, [read1, read2]);
import { sql } from 'drizzle-orm';
import { sqliteTable, int, text, withReplicas } from 'drizzle-orm/sqlite-core';
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';

const usersTable = sqliteTable('users', {
  id: int('id' as string).primaryKey(),
  name: text('name').notNull(),
});

const primaryDb = drizzle(createClient({ url: 'DATABASE_URL', authToken: 'DATABASE_AUTH_TOKEN' }));
const read1 = drizzle(createClient({ url: 'DATABASE_URL', authToken: 'DATABASE_AUTH_TOKEN' }));
const read2 = drizzle(createClient({ url: 'DATABASE_URL', authToken: 'DATABASE_AUTH_TOKEN' }));

const db = withReplicas(primaryDb, [read1, read2]);

你现在可以像以前一样使用 db 实例。Drizzle 会自动处理读取副本和主实例之间的选择

// 从 read1 连接或 read2 连接读取
await db.select().from(usersTable)

// 使用主数据库进行删除操作
await db.delete(usersTable).where(eq(usersTable.id, 1))

你可以使用 $primary 键来强制使用主实例进行读取操作

// 从主实例读取
await db.$primary.select().from(usersTable);

使用 Drizzle,你还可以指定选择读取副本的自定义逻辑。 你可以做出加权决策或任何其他自定义选择方法来随机选择读取副本。 这是一个选择读取副本的自定义逻辑的实现示例,其中第一个副本有 70% 的机会被选择,第二个副本有 30% 的机会被选中。

请记住,你可以为读取副本实现任何类型的随机选择方法

const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
    const weight = [0.7, 0.3];
    let cumulativeProbability = 0;
    const rand = Math.random();

    for (const [i, replica] of replicas.entries()) {
      cumulativeProbability += weight[i]!;
      if (rand < cumulativeProbability) return replica;
    }
    return replicas[0]!
});

await db.select().from(usersTable)
Become a Gold Sponsor