两个 SQL 语句应该 return 相同的结果,但它们不是(在 AWS Aurora DB 上)

Two SQL statements should return the same results, but they don't (on AWS Aurora DB)

这是 GpsPosition 的 table 定义:

CREATE TABLE GpsPosition 
(
    altitudeInMeters SMALLINT NOT NULL,
    dateCreated      BIGINT NOT NULL,
    dateRegistered   BIGINT NOT NULL,
    deviceId         BINARY(16) NOT NULL,
    emergencyId      BINARY(16) NULL,
    gpsFix           SMALLINT NOT NULL,
    heading          SMALLINT NOT NULL,
    horizontalUncertaintyInMeters SMALLINT NOT NULL,
    id               BINARY(16) NOT NULL,
    latestForDevice  BOOLEAN NOT NULL,
    latestForUser    BOOLEAN NOT NULL,
    latitude         DOUBLE PRECISION NOT NULL,
    longitude        DOUBLE PRECISION NOT NULL,
    numSatellites    SMALLINT NOT NULL,
    speedInKmph      SMALLINT NOT NULL,
    stale            BOOLEAN NOT NULL,
    userId           BINARY(16) NULL,
    verticalUncertaintyInMeters SMALLINT NOT NULL,

    PRIMARY KEY (id)
);

ALTER TABLE GpsPosition 
    ADD CONSTRAINT GpsPosition_deviceId_fkey 
        FOREIGN KEY (deviceId) REFERENCES Device(id) 
            ON UPDATE CASCADE ON DELETE CASCADE;

ALTER TABLE GpsPosition 
    ADD CONSTRAINT GpsPosition_emergencyId_fkey 
        FOREIGN KEY (emergencyId) REFERENCES Emergency(id) 
            ON UPDATE CASCADE ON DELETE SET NULL;

ALTER TABLE GpsPosition 
    ADD CONSTRAINT GpsPosition_userId_fkey 
        FOREIGN KEY (userId) REFERENCES User(id) 
            ON UPDATE CASCADE ON DELETE SET NULL;

ALTER TABLE GpsPosition 
    ADD CONSTRAINT deviceId_dateCreated_must_be_unique 
        UNIQUE (deviceId, dateCreated);

CREATE INDEX i2915035553 ON GpsPosition (deviceId);
CREATE INDEX deviceId_latestForDevice_is_non_unique ON GpsPosition (deviceId, latestForDevice);
CREATE INDEX i3210815937 ON GpsPosition (emergencyId);
CREATE INDEX i1689669068 ON GpsPosition (userId);
CREATE INDEX userId_latestForUser_is_non_unique ON GpsPosition (userId, latestForUser);

请注意 GpsPosition 中的 userId 是一个存储为 binary(16) 的 UUID。

此 SQL 代码在 AWS AuroraDB 引擎版本 5.7.12 上执行。

我希望下面的查询 return 得到相同的结果,但第一个 return 有很多结果,第二个 return 没有结果。知道为什么吗?

select *
from GpsPosition
where exists (select *
              from User
              where id = GpsPosition.userId and
                    id = UNHEX( '3f4163aab2ac46d6ad15164222aca89e' )
             );

select *
from GpsPosition
where userId = UNHEX( '3f4163aab2ac46d6ad15164222aca89e' );

请注意,如您所料,以下 SQL 语句 return 是一行:

select *
from User 
where id = UNHEX( '3f4163aab2ac46d6ad15164222aca89e' );

我完全看不出语义上的对等。

带有 exists 的那个正在检查另一个 table 中是否存在一行。如果不存在这样的匹配行,则外部查询不会 return 任何内容。

这与仅 return 在单个 table 中匹配行非常不同。

观察到两个查询 return 对特定数据集的相同结果并不能使它们在语义上等同。他们必须保证 return 在 任何 适当的查询数据上得到相同的结果。例如,2 + 2 = 2 * 2,但这不会进行加法和乘法"semantically equivalent."

我还应该补充一点,欺骗数据库优化器并不难,即使两个表达式保证是等价的。

所以我的团队花了几个月的时间试图理解这个问题和许多其他不一致(比如这篇文章中的这个)我们能够在 AWS Aurora DB 5.7 上重现但无法在 MySQL 5.7 或与此相关的任何其他内容。

作为这项工作的一部分,我们聘请了 AWS 支持人员,但这非常没有帮助。他们确认他们可以通过在我们所做的同一数据库上执行相同的查询来重现不一致,但随后表示他们无法将该数据复制到另一个数据库并仍然重现问题,这似乎让他们满意以标记支持案件已解决。现在承认,这是一个非常隐蔽的缺陷,因为它很难重现,而且断断续续且罕见,但当它被击中时,它会在受影响的数据集中可靠地重现。一旦你确实遇到了这个缺陷,那么,依赖于数据库的应用程序将无法再在那些受影响的区域正常运行 ;)

虽然我们不认为缺陷仅限于级联删除,但似乎 "more reliably" 产生此缺陷的方法是删除具有级联删除的表中的行。同样,这似乎会产生缺陷 "more reliably",但即便如此,它也非常罕见且难以产生。但是,我们可以通过 运行 一个巨大的自动化测试套件在一个紧密的循环中生成它。同样,一旦您确实遇到了这个缺陷,受影响的数据将可靠地再现不一致 - 很难遇到这个缺陷。

那么在所有分析结束时我们得出了什么结论?

1) 首先,Thorsten Kettner(请参阅他在上面发表的评论)是正确的——这是 RDBMS 服务器本身的缺陷。我们无权访问 AWS AuroraDB 源代码或底层基础设施,因此我们无法将此缺陷归因于更具体的问题,但它可能是 RDBMS 服务器中的缺陷,可能是数据持久层中的缺陷,也可能是数据持久层中的缺陷其他地方。

2) 基于上面的 (1),我们认为 AWS Amazon 5.7.x 还不够成熟,无法用于生产应用程序。尽管它在 99.9999% 的时间里工作正常,但那 0.0001% 导致开发和生产数据库服务器做错事和 return 不正确的结果,这对我们来说是绝对不能接受的。我们还检测到表的完整性约束没有得到可靠遵守的情况,导致非常奇怪的孤立行,这些行本应在模式定义中作为级联删除的一部分被删除,这同样是我们绝对不能接受的。

3) 我们无法在 AWS MySQL 5.6、AWS MySQL 5.7、兼容 MySQL 5.6 的 AWS AuroraDB、non-AWS Windows MySQL 5.6,或 non-AWS MySQL 5.7。简而言之,我们认为无论出现什么问题,都是与 MySQL 5.7 兼容的 AWS AuroraDB 特有的。我们对兼容 MySQL 5.6 的 AWS AuroraDB 进行了大量测试,无法重现任何这些不一致的缺陷,因此我们认为目前兼容 MySQL 5.6 的 AuroraDB 已经成熟,适合生产使用.