Node.js 主流ORM框架动态分表方案大盘点

针对高并发、数据量大的场景,通常会考虑采用分库分表进行优化。在这篇文章,我们重点盘点一下Node.js主流ORM框架的动态分表方案:

分表规则

比如我们需要对订单表进行分表操作。可以根据实际业务需求设计分表规则,在这里,我们根据顾客Id取模动态生成表名。比如,拆分为16张表,顾客Id为129,对应的表名如下:

typescript 复制代码
const tableName = `Order_${129 % 16}`;  // Order_1

TypeORM

在TypeORM中可以按照如下方式设置动态表名:

typescript 复制代码
// 获取repository
const repositoryOrder = dataSource.createQueryBuilder().connection.getRepository(EntityOrder);
// 设置动态表名
const userId = 129;
const tableName = `Order_${userId % 16}`;
repositoryOrder.metadata.tablePath = tableName;
// 查询订单
const orders = await repositoryOrder.find();

Drizzle ORM

schema.ts

typescript 复制代码
const orderFactory = userId => pgTable(
  `Order_${userId % 16}`,
  {
    id: serial('id').primaryKey(),  
    name: text('name').notNull(),
  },
);

export const order0 = orderFactory(0);
...
export const order15 = orderFactory(15);

query.ts

typescript 复制代码
import * as schema from './db/schema';

const db = drizzle(process.env.DATABASE_URL!, { schema });

const userId = 129;
const modelName=`order${userId % 16}`;
const orders = await db.query[modelName].findMany();

Prisma ORM

Prisma ORM对动态表名的支持还在规划当中,参见:Table Partitioning

作为备选方案,我们可以使用$queryRawUnsafe直接构造原始SQL:

typescript 复制代码
const userId = 129;
const tableName = `Order_${userId % 16}`;  
const orders = await prisma.$queryRawUnsafe(`SELECT * FROM "${tableName}"`);

Vona ORM

Vona ORM提供了两种模式:自动模式/手工模式

1. 自动模式

model/order.ts

typescript 复制代码
import { EntityOrder } from '../entity/order.ts';

@Model({
  entity: EntityOrder,
  table(ctx: VonaContext, defaultTable: keyof ITableRecord) {
    const user = ctx.app.bean.passport.getCurrentUser();
    if (!user) return defaultTable;
    return `${defaultTable}_${Number(user.id) % 16}`;
  },
})
export class ModelOrder {}
  • defaultTable: 是在EntityOrder中定义的缺省表名,如:Order

service/order.ts

typescript 复制代码
class ServiceOrder {
  async findAll() {
    return await this.scope.model.order.select();
  }
}

2. 手工模式

service/order.ts

typescript 复制代码
class ServiceOrder {
  async findAll() {
    const user = this.bean.passport.getCurrentUser();
    const tableName = `Order_${Number(user!.id) % 16}`;
    const modelOrder = this.scope.model.order.newInstance(undefined, tableName as any);
    return await modelOrder.select();
  }
}
  • newInstance: 第一个参数可以传入数据源,从而实现分库能力。这里忽略,因此传入undefined
相关推荐
Dragon Wu3 分钟前
Electron Forge集成React Typescript完整步骤
前端·javascript·react.js·typescript·electron·reactjs
We་ct15 小时前
LeetCode 56. 合并区间:区间重叠问题的核心解法与代码解析
前端·算法·leetcode·typescript
程序猿阿伟1 天前
《TypeScript中Protobuf到运行时类型安全的转换指南》
javascript·安全·typescript
We་ct1 天前
LeetCode 228. 汇总区间:解题思路+代码详解
前端·算法·leetcode·typescript
阿蒙Amon1 天前
TypeScript学习-第10章:模块与命名空间
学习·ubuntu·typescript
VT.馒头2 天前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
AAA阿giao2 天前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架
hedley(●'◡'●)2 天前
基于cesium和vue的大疆司空模仿程序
前端·javascript·vue.js·python·typescript·无人机
百锦再2 天前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
小杨同学呀呀呀呀2 天前
Ant Design Vue <a-timeline>时间轴组件失效解决方案
前端·javascript·vue.js·typescript·anti-design-vue