使用prisma实现跨库事务一致性

一、从分表到分库

从业务逻辑上,可以进行垂直分表,比如按记录的状态分表,不同状态进不同表,可以省略状态字段。

从数据规模上可以进行水平分表,按外键取模分表。

垂直分表可以从本质上降低业务复杂度,但对于数据规模问题,仍然需要分库解决。但要从根本上解决数据规模问题,需要基于水平分表再分库。

分表分库是一门艺术,需要针对实际业务具体分析。大的原则是要综合考虑关联查询成本、跨库事务一致性等。

跨库关联查询可通过以下方法解决:

  • ①可在分库中冗余公共表;
  • ②可通过业务代码进行两阶段查询。

跨库事务一致性可通过prisma提供的多schema接入和动态事务处理功能实现。

二、prisma的多schema接入方法

多schema接入有两种方法:一种是不同库共用同一个schema,这种情况常见于热备和负载均衡;另一种是不同库的schema也不同,这正适合于本文所讨论的分表分库话题。

(一)不同库共用一个schema

datasource db {
  provider = "postgres"
  url = env("PG_URL")
}
import { PrismaClient } from '@prisma/client'

const client1 = new PrismaClient({ datasources: { db: { url: 'postgres://localhost/db1' }} })
const client2 = new PrismaClient({ datasources: { db: { url: 'postgres://localhost/db2' }} })

从上面代码可看出,prisma客户端提供了对数据库url的覆写功能。

(二)不同库的schema不同

prisma/schema1.prisma:

datasource db {
  provider = "postgres"
  url      = env("DB1_URL")
}

generator client {
  provider = "prisma-client-js"
  output   = "./generated/client1"
}

model Model1 {
  id         Int @id @default(autoincrement())
  model1Name String
}

prisma/schema2.prisma:

datasource db {
  provider = "postgres"
  url      = env("DB2_URL")
}

generator client {
  provider = "prisma-client-js"
  output   = "./generated/client2"
}

model Model2 {
  id         Int @id @default(autoincrement())
  model2Name String
}

生成prisma客户端时,通过–schema参数指定具体的schema定义文件:

prisma generate --schema prisma/schema1.prisma
prisma generate --schema prisma/schema2.prisma

在业务代码中,可以如下使用多个prisma客户端:

import { PrismaClient as PrismaClient1 } from '../prisma/generated/client1'
import { PrismaClient as PrismaClient2 } from '../prisma/generated/client2'

const client1 = new PrismaClient1()
const client2 = new PrismaClient2()

三、prisma的交互式事务处理功能

prisma提供三种类型的事务操作:一种是在creatMany、include嵌套操作中原生支持事务性;一种是通过$transaction接口手工指定顺序执行的事务集合;一种是通过$transaction接口手工指定交互执行的事务异步函数。

(一)顺序执行

const [posts, totalPosts] = await prisma.$transaction([
  prisma.post.findMany({ where: { title: { contains: 'prisma' } } }),
  prisma.post.count(),
])

也可以在 $transaction 内使用原始查询:

const [userList, updateUser] = await prisma.$transaction([
  prisma.$queryRaw`SELECT 'title' FROM User`,
  prisma.$executeRaw`UPDATE User SET name = 'Hello' WHERE id = 2;`,
])

(二)交互执行

要使用交互式事务,可以将异步函数传递到$transaction。传递到该异步函数的第一个参数是Prisma Client的实例。 下面,将该实例称为tx。 在此tx实例上调用的任何数据库操作都会封装到事务中。

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

function transfer(from: string, to: string, amount: number) {
  return prisma.$transaction(async (tx) => {
    // 1. Decrement amount from the sender.
    const sender = await tx.account.update({
      data: {
        balance: {
          decrement: amount,
        },
      },
      where: {
        email: from,
      },
    })

    // 2. Verify that the sender's balance didn't go below zero.
    if (sender.balance < 0) {
      throw new Error(`${from} doesn't have enough to send ${amount}`)
    }

    // 3. Increment the recipient's balance by amount
    const recipient = await tx.account.update({
      data: {
        balance: {
          increment: amount,
        },
      },
      where: {
        email: to,
      },
    })

    return recipient
  })
}

async function main() {
  // This transfer is successful
  await transfer('alice@prisma.io', 'bob@prisma.io', 100)
  // This transfer fails because Alice doesn't have enough funds in her account
  await transfer('alice@prisma.io', 'bob@prisma.io', 100)
}

main()

在上面的示例中,两个 update 查询都在数据库事务中运行。 当应用到达函数末尾时,事务是 committed 到数据库。

如果你的应用在此过程中遇到错误,异步函数将抛出异常并自动对事务进行 rollback。

要捕获异常,可以将 $transaction 封装在 try-catch 块中:

try {
  await prisma.$transaction(async (tx) => {
    // Code running in a transaction...
  })
} catch (err) {
  // Handle the rollback...
}

谨慎使用交互式事务。 长时间保持事务打开会损害数据库性能,甚至可能导致死锁。 尽量避免在事务函数内执行网络请求和执行缓慢的查询。

四、利用多schema接入和交互式事务实现跨数据库事务一致性

利用第二节中的多schema接入功能生成连接多个数据库的prisma客户端,在需要跨多数据库进行事务性操作时,可在第一个客户端启用交互式事务,在异步函数中启用第二个客户端的交互式事务,以此类推,从而实现整体的事务性。

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Prisma 是一个现代化的数据库工具包,它可以帮助开发者更轻松地管理和操作数据库。Prisma 支持多种数据库,包括 MySQL。 要在 Prisma 中使用 MySQL,你需要进行以下步骤: 1. 首先,你需要在你的项目中安装 Prisma。可以使用 npm 或者 yarn 进行安装: ``` npm install @prisma/cli ``` 2. 安装完成后,你可以使用 Prisma CLI 初始化一个新的 Prisma 项目。在终端中运行以下命令: ``` npx prisma init ``` 这将创建一个新的 Prisma 项目,并生成一个 prisma 目录。 3. 接下来,你需要在 Prisma 的 `schema.prisma` 文件中定义你的数据模型和数据库连接。打开 `prisma/schema.prisma` 文件,然后添加以下代码: ```prisma datasource db { provider = "mysql" url = "mysql://username:password@localhost:3306/database_name" } generator client { provider = "prisma-client-js" } ``` 将 `username`、`password` 和 `database_name` 替换为你自己的 MySQL 连接信息。 4. 定义完数据模型和数据库连接后,运行以下命令来生成 Prisma 客户端: ``` npx prisma generate ``` 这将根据你的数据模型生成 Prisma 客户端代码。 5. 现在,你可以在你的应用程序中使用 Prisma 客户端来操作 MySQL 数据库了。你可以通过导入 Prisma 客户端并使用生成的方法来进行查询、创建、更新和删除操作。 ```javascript const { PrismaClient } = require('@prisma/client'); const prisma = new PrismaClient(); async function main() { const users = await prisma.user.findMany(); console.log(users); } main() .catch(e => console.error(e)) .finally(() => prisma.$disconnect()); ``` 这是一个简单的示例,查询所有的用户并打印出来。 这就是使用 Prisma 连接和操作 MySQL 数据库的基本步骤。你可以在 Prisma 文档中找到更多关于 Prisma 和 MySQL 的详细信息和示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值