优化 PostgreSQL 查询以将用户插入数据库

问题描述 投票:0回答:1
  • 用例是这样的。
  • 有一个注册端点,用户可以在该端点上提交他们的电子邮件和密码
  • 必须一次创建 4 行并生成几个 id
  • 桌子看起来像这样

身份验证类型

id 名字
uuid varchar
主键
0aa4d9a9-e024-4792-bc41-36a4f3528d36 密码

账户

id 电子邮件 密码 ...其他几列 身份验证_类型_id
uuid varchar varchar uuid
主键 独一无二 authentication_types 的外键
7a9d912a-69ab-4615-9058-e1bb1c4e36c5 密码 ... ... 0aa4d9a9-e024-4792-bc41-36a4f3528d36

用户

id 已启用
uuid 布尔值
主键
fc9ca826-63dc-43b8-97b6-2e949ffd8a30 真实

用户帐户

id 账户ID 用户 ID
uuid uuid uuid
主键 帐户的外键 用户的外键
bd4b338f-1b5a-4b24-9908-e5cfb4080dd4 7a9d912a-69ab-4615-9058-e1bb1c4e36c5 fc9ca826-63dc-43b8-97b6-2e949ffd8a30

验证令牌

id 过期 代币 账户 ID
uuid 时间戳 varchar uuid
主键 独一无二 帐户的外键
865a6389-67ea-4e38-a48f-9f4b60ffe816 ... ... 7a9d912a-69ab-4615-9058-e1bb1c4e36c5

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

  • 生成一个 uuid 并向帐户中插入一行
  • 生成一个uuid并向users中插入一行
  • 生成一个uuid + 从accounts和users中获取插入行的id并插入到user_accounts中
  • 生成上面生成的uuid+token+账户id并插入到verification_tokens中

这是我经过深思熟虑后提出的问题。获取密码的身份验证类型 ID,并在每一步中运行 CTE 并生成一些 uuid。有没有办法进一步优化?

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

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

postgresql performance query-optimization common-table-expression
1个回答
0
投票

我假设在此操作之前,后端会进行唯一性检查,检查帐户中是否已存在电子邮件,或者由 API 服务基于 SQLSTATE 适当捕获和处理 ins2 引发的唯一索引违规。响应。以下优化依赖于约束将全部通过的预先知识。

对 DML 进行一些小调整:

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;

您还可以尝试更改 DDL 以使 PRIMARY KEY 和 FOREIGN KEY 约束DEFERRABLE/INITIALLY DEFERRED,这将导致约束检查和索引更新仅在成功完成所有 4 个 INSERT 后一起运行,而不是在每一个 INSERT 之后运行。这应该会减少您的开销以及高并发实时环境中的死锁风险。

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

© www.soinside.com 2019 - 2024. All rights reserved.