跳到主要内容

数据库高可用

问题

如何实现数据库高可用?主从复制、读写分离、分库分表怎么做?

答案

高可用架构演进

主从复制

读写分离

read-write-split.ts
// 基于 Prisma 的读写分离
import { PrismaClient } from '@prisma/client';

const writeClient = new PrismaClient({
datasources: { db: { url: process.env.MASTER_DATABASE_URL } },
});

const readClient = new PrismaClient({
datasources: { db: { url: process.env.SLAVE_DATABASE_URL } },
});

class UserService {
// 写操作走主库
async createUser(data: CreateUserDto) {
return writeClient.user.create({ data });
}

// 读操作走从库
async getUser(id: string) {
return readClient.user.findUnique({ where: { id } });
}

// 需要强一致性的读走主库
async getUserAfterUpdate(id: string) {
return writeClient.user.findUnique({ where: { id } });
}
}

分库分表

方式策略场景
垂直分库按业务拆分库用户库、订单库、商品库
水平分表按规则拆分同一张表按 user_id 取模、按日期分区
sharding.ts
// 水平分表:按 user_id 取模
function getTableName(userId: number): string {
const shardId = userId % 4; // 分 4 张表
return `orders_${shardId}`; // orders_0, orders_1, orders_2, orders_3
}

// 查询路由
async function getOrders(userId: number) {
const table = getTableName(userId);
return db.$queryRaw(`SELECT * FROM ${table} WHERE user_id = ?`, [userId]);
}

连接池配置

connection-pool.ts
// Prisma 连接池
const prisma = new PrismaClient({
datasources: {
db: {
url: `${process.env.DATABASE_URL}?connection_limit=20&pool_timeout=10`,
},
},
});

// TypeORM 连接池
const dataSource = new DataSource({
type: 'mysql',
extra: {
connectionLimit: 20, // 最大连接数
waitForConnections: true,
queueLimit: 0,
},
});

常见面试问题

Q1: 主从延迟怎么处理?

答案

  1. 关键业务读主库:写后立即读走主库
  2. 半同步复制:主库等至少一个从库确认
  3. 延迟监控SHOW SLAVE STATUS 查看 Seconds_Behind_Master
  4. 业务层缓存:写入后更新缓存

Q2: 分库分表后的问题?

答案

问题解决方案
跨表 JOIN宽表冗余 / 应用层聚合
分布式事务Saga / Event Sourcing
全局排序各分片取 TopN 再合并
全局 ID雪花算法 / 号段模式

Q3: 什么时候该分库分表?

答案

  • 单表超过 500 万行2GB
  • 单库 QPS 超过 5000
  • 先考虑:加索引 → 读写分离 → 缓存 → 最后再分库分表

相关链接