• 用例是这样的.
  • 有一个注册端点,用户将在该端点上提交邮箱和密码
  • 必须同时创建4行并生成几个id
  • 桌子看起来是这样的

authentication_types

id name
uuid varchar
primary key
0aa4d9a9-e024-4792-bc41-36a4f3528d36 password

accounts

id email password ...few other columns authentication_type_id
uuid varchar varchar uuid
primary key unique foreign key to authentication_types
7a9d912a-69ab-4615-9058-e1bb1c4e36c5 password ... ... 0aa4d9a9-e024-4792-bc41-36a4f3528d36

users

id enabled
uuid boolean
primary key
fc9ca826-63dc-43b8-97b6-2e949ffd8a30 true

user_accounts

id account_id user_id
uuid uuid uuid
primary key foreign key to accounts foreign key to users
bd4b338f-1b5a-4b24-9908-e5cfb4080dd4 7a9d912a-69ab-4615-9058-e1bb1c4e36c5 fc9ca826-63dc-43b8-97b6-2e949ffd8a30

verification_tokens

id expires token account_id
uuid timestamptz varchar uuid
primary key unique foreign key to accounts
865a6389-67ea-4e38-a48f-9f4b60ffe816 ... ... 7a9d912a-69ab-4615-9058-e1bb1c4e36c5

当新注册发生时,我想做以下事情

  • 生成uid并将行插入帐户
  • 生成uid并将行插入用户
  • 生成uuid +从帐户和用户获取插入行的id并插入到user_accounts
  • 生成上面生成的uuid + token +帐户ID并插入verification_token

这是我深思熟虑后提出的疑问.获取密码的身份验证类型id,并在每一步中运行带有一些uuid生成的CTE.有没有方法进一步优化它?

WITH account_data(id, email, password, authentication_type_id) AS (
      VALUES( gen_random_uuid()
             ,:email
             ,:password
             ,(SELECT id 
               FROM authentication_types 
               WHERE name = :authenticationTypeName) ) )
,ins1(user_id) AS (
      INSERT INTO users(id, enabled) 
      VALUES( gen_random_uuid()
             ,true)
      RETURNING id AS user_id )
,ins2(account_id) AS (
      INSERT INTO accounts (id, email, password, authentication_type_id) 
      SELECT id
            ,email
            ,password
            ,authentication_type_id 
      FROM account_data 
      RETURNING id AS account_id )
,ins3 AS (
      INSERT INTO user_accounts (id, account_id, user_id) 
      VALUES( gen_random_uuid()
             ,(SELECT account_id 
               FROM ins2)
             ,(SELECT user_id 
               FROM ins1)       ) )
INSERT INTO verification_tokens (id, token, account_id) 
VALUES( gen_random_uuid()
       ,:token
       ,(SELECT account_id 
         FROM   ins2)     ) 
RETURNING (SELECT account_id FROM ins2) AS id

如果有更快的方法来执行上述查询,我会很高兴听到.提前感谢您的帮助

EDIT 1

这是一个包含实际数据的示例查询

WITH account_data(id, email, password, authentication_type_id) AS (
      VALUES( gen_random_uuid()
             ,'abc@example.com'
             ,'$2a$12$71qwp8jK0z6EVbpe9FZSIeHQkYh4Wtn2sOxH/Y3xuEhSLP1IjPAEK'
             ,(SELECT id 
               FROM authentication_types 
               WHERE name = 'password') ) )
,ins1(user_id) AS (
      INSERT INTO users(id, enabled) 
      VALUES( gen_random_uuid()
             ,true)
      RETURNING id AS user_id )
,ins2(account_id) AS (
      INSERT INTO accounts (id, email, password, authentication_type_id) 
      SELECT id
            ,email
            ,password
            ,authentication_type_id 
      FROM account_data 
      RETURNING id AS account_id )
,ins3 AS (
      INSERT INTO user_accounts (id, account_id, user_id) 
      VALUES( gen_random_uuid()
             ,(SELECT account_id 
               FROM ins2)
             ,(SELECT user_id 
               FROM ins1)       ) )
INSERT INTO verification_tokens (id, token, account_id) 
VALUES( gen_random_uuid()
       ,'$2a$12$VNP/ya5ILSP.yYjSa3anyuZhm8Jxc97Y3p95grDjS/Xe1XjBmX/VK'
       ,(SELECT account_id 
         FROM   ins2)     ) 
RETURNING (SELECT account_id FROM ins2) AS id

这是视觉查询分析器

enter image description here

这是解释分析结果

enter image description here

推荐答案

我认为这是一个既定的事实:

  • 您的设计受限于基于DMS的CRUD.
  • 在此操作之前,正在后台进行针对帐户中是否已存在邮箱的唯一性判断,或者API服务根据响应的SQL STATE适当捕获和处理ins 2引发的唯一约束违规-以下优化依赖于约束将全部通过的预先知识

对TLR进行了一些小调整:

WITH account_data(id, email, password, authentication_type_id) AS (
      VALUES( gen_random_uuid()
             ,:email
             ,:password
             ,:authentication_type_id  --cache these at the level of the CRUD service and submit parameterized 
             ) 
), ins1(user_id) AS (
      INSERT INTO users(id, enabled) 
      VALUES( gen_random_uuid(), true )
      RETURNING id AS user_id 
), ins2(account_id) AS (
      INSERT INTO accounts (id, email, password, authentication_type_id) 
      SELECT id
            ,email
            ,password
            ,:authentication_type_id  --parameterized
      FROM account_data
      RETURNING id AS account_id
), ins3 AS (
      INSERT INTO user_accounts (id, account_id, user_id) 
      SELECT gen_random_uuid()
             , account_id
             , user_id )
      FROM ins1, ins2  --ditch the subqueries, just use the cartesian product of the two preceding always-single records
)
INSERT INTO verification_tokens (id, token, account_id) 
SELECT gen_random_uuid()
       , :token
       , account_id 
FROM ins2
RETURNING account_id AS id;

您还可以try 更改收件箱,使PRIMARY Key和FOREIGN Key约束为DEFERRABLE/INITIALLY DEARCH,这将导致约束判断和索引更新仅在成功完成所有4个收件箱后一起运行,而不是在每个收件箱之后. 在高并发实时环境中,这应该存在reduce your overhead和僵局风险.

请注意,在数据库上获得类似于prod的并发负载之前,您可能不会看到基准测试性能有任何实质性改进.

Postgresql相关问答推荐

Trunc函数不删除小数

无法在kubernetes中设置postgres复制

为什么我的唯一索引在 Postgresql 中不起作用?

Postgres 使用不同元素数据类型的订单数据

Docker compose read connection reset by peer error on pipeline

将一列与另一列的每个元素进行比较,该列是一个数组

PostgreSQL 函数按名称传递参数

TimescaleDB 连续聚合:如何存储连续聚合结果

GORM 不会创建 many2many 关联

PostgreSQL 中的复制槽是否会阻止删除 WALL,即使它们来自另一个数据库?

brew 程序更新恼人的错误信息

带有 postgres 的 DOCKER 容器,警告:could not open statistics file "pg_stat_tmp/global.stat": Operation not permitted

org.postgresql.util.PSQLException:错误:relation "app_user" does not exist

Docker Compose + Spring Boot + Postgres 连接

MAC OS X 上的 Postgres 权限被拒绝

Postgres 外键on update和on delete选项如何工作?

在 postgresql 中,如何在 jsonb 键上返回布尔值而不是字符串?

在 Postgresql 中按窗口函数结果过滤

判断 PostgreSQL 中的角色是否设置了密码

Postgresql 用随机值更新每一行