PostgresAWS Aurora没有强制执行唯一索引/约束

问题描述

我们将Postgres用于生产数据库,从技术上讲,它是使用10.11引擎版本的Amazon AWS aurora数据库。它似乎并没有承受任何不合理的负载(100-150个并发连接,cpu始终低于10%,大约使用了50%的内存,峰值达到300写入IOPS /每秒1500读取IOPS)。

我们想确保真正良好的数据一致性,因此我们广泛使用外键,触发器来验证数据的插入/更新以及许多独特的约束条件。

大多数写入操作源自简单的REST API请求,这导致非常标准的插入和更新查询。但是,在某些情况下,我们还使用触发器和函数来处理更复杂的逻辑。例如,对一个表的更新将导致对其他表的一些相当复杂的级联更新。

所有查询始终包裹在事务中,在大多数情况下,我们不使用显式锁定。

那怎么了?

我们有很多(数十行,数十个表)实例,其中数据库中存在不符合我们唯一约束的数据。

有时候,有问题的行的created_at和updated_at时间戳是相同的,而有时它们是非常相似的(半秒之内)。这使我相信这是由种族状况引起的。

我们不确定,但可以肯定的是,与这些记录的共同点是写操作触发了一个函数(该记录是通过简单的插入或更新操作写入的,并导致了其他几个表的更新)或该写入来自函数(通过简单的插入或更新写入了另一条记录,这触发了写入有问题数据的函数)。

根据我的研究,独特的约束/索引非常可靠且“有效”。这是真的?如果是这样,那为什么会发生这种情况?

这是一些令人讨厌的数据的示例,我不得不将其中的一些数据涂黑,但是我向您保证user_id字段中的值是相同的。正如您将在下面看到的,在user_id,position和uneleted中有一个唯一的索引。因此,该数据的存在应该是不可能的。

Offending data

这是表结构的导出:

-- Table DeFinition ----------------------------------------------

CREATE TABLE guides.preferences (
    id uuid DEFAULT gen_random_uuid() PRIMARY KEY,user_id uuid NOT NULL REFERENCES users.users(id),guide_id uuid NOT NULL REFERENCES users.users(id),created_at timestamp without time zone NOT NULL,updated_at timestamp without time zone NOT NULL,undeleted boolean DEFAULT true,deleted_at timestamp without time zone,position integer NOT NULL CHECK ("position" >= 0),completed_meetings_count integer NOT NULL DEFAULT 0,CONSTRAINT must_concurrently_set_deleted_at_and_undeleted CHECK (undeleted IS TRUE AND deleted_at IS NULL OR undeleted IS NULL AND deleted_at IS NOT NULL),CONSTRAINT preferences_guide_id_user_id_undeleted_unique UNIQUE (guide_id,user_id,undeleted),CONSTRAINT preferences_user_id_position_undeleted_unique UNIQUE (user_id,position,undeleted) DEFERRABLE INITIALLY DEFERRED
);
COMMENT ON COLUMN guides.preferences.undeleted IS 'Set simultaneously with deleted_at to flag this as deleted or undeleted';
COMMENT ON COLUMN guides.preferences.deleted_at IS 'Set simultaneously with deleted_at to flag this as deleted or undeleted';

-- Indices -------------------------------------------------------

CREATE UNIQUE INDEX preferences_pkey ON guides.preferences(id uuid_ops);
CREATE UNIQUE INDEX preferences_user_id_position_undeleted_unique ON guides.preferences(user_id uuid_ops,position int4_ops,undeleted bool_ops);
CREATE INDEX index_preferences_on_user_id_and_guide_id ON guides.preferences(user_id uuid_ops,guide_id uuid_ops);
CREATE UNIQUE INDEX preferences_guide_id_user_id_undeleted_unique ON guides.preferences(guide_id uuid_ops,user_id uuid_ops,undeleted bool_ops);

我们对此深感困惑,并希望有人能够帮助我们。谢谢!

解决方法

我找到了原因!在过去的几个月中,我们一直在构建许多新功能,并且已经在进行大量迁移以更改架构和更新数据。由于数据库中包含所有触发器和功能,因此暂时禁用触发器通常很有意义。我们通过“ set session_replication_role ='replica';”来实现。

结果表明,这也会禁用所有可延迟的约束,因为可延迟的约束和外键是基于触发器的。从我的问题的模式中可以看到,有问题的唯一约束设置为可延迟。

谜底解决了!

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...