首页 > 解决方案 > 发生重复键错误时 Postgres 自动递增

问题描述

我定义了一个模型和迁移:

module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('user', {
    name: DataTypes.STRING,
    username: DataTypes.STRING,
    email: DataTypes.STRING,
    password: DataTypes.STRING,
},
    {})

user.associate = function (models) {
    // associations can be defined here
}

return user

}

'use strict';
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable('users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        allowNull: false,
        type: Sequelize.STRING
      },
      username: {
        unique: true,
        allowNull: false,
        type: Sequelize.STRING
      },
      email: {
        allowNull: false,
        type: Sequelize.STRING
      },
      password: {
        allowNull: false,
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('users');
  }
};

当我尝试创建user.create({...}) 具有重复键值(在这种情况下为电子邮件或用户名)的新用户时,续集会按预期抛出错误,即使这样它也会增加主键自动增量值。

示例:我创建了一个具有id1 的新用户,然后我尝试创建 5 次具有重复唯一键(用户名或电子邮件)的新用户,然后当我成功创建用户时,主键id变为 6。

为什么会发生这种情况?正确的做法是按照顺序,如果错误则不增加,仅在用户成功插入时增加。

标签: node.jspostgresqlsequelize.js

解决方案


我认为值是由序列生成的。

那么这是所有 DBMS 中序列的正常行为。

想象以下场景。

  1. User 1要求一个新的身份证。他得到id = 1.
  2. User 2要求一个新的身份证。他得到id = 2.
  3. User 1无法在依赖表中插入记录。他执行一个rollback.
  4. User 2成功插入他需要的每一个。他执行一个commit.

现在想象一下重用序列需要什么id = 1,因为它没有提交。

  • 将序列回滚到 1?复杂,因为它需要记住它返回id = 2给另一个用户。
  • 替换用户 2 插入的内容 id=1?由于外键而昂贵且有风险(User 2可能使用映射id=2到内存中某个对象的软件,更改数据库中的 id 为时已晚)
  • 让用户 2 等到用户 1 提交或回滚他的事务后才知道要返回什么?每个人都会一直在等待。

最重要的是,插入具有连续值的记录id仍然不足以使其保持连续。桌子上的第一个delete将打破一切。

结论:确保 id 连续的成本对于数据库性能来说是疯狂的,并且该值不存在,因为它可能随时崩溃。


推荐阅读