代理项 (INT) 键是否几乎总是比唯一自然 (VARCHAR) 键(在 MySQL 中)产生更好的性能?

Does a surrogate (INT) key almost always yield better performance than an unique natural (VARCHAR) key (in MySQL)?

我很难理解 MySQL 数据库 table.

使用什么数据类型

假设我们是一家图书出版公司,我们需要在 MySQL 数据库中创建一个包含所有图书和作者的数据库。我们有大约 500000 本书。一本书有唯一的 ISBN(例如 978-3-16-148410-0)。

所以我们有两种选择来存储我们的书:

  1. 创建一个 id VARCHAR(24) NOT NULL 自然主键列并将我们的 ISBN 存储在那里,或者
  2. 创建一个代理 id INT NOT NULL AUTO_INCREMENT 然后还有一个 isbn UNIQUE VARCHAR(24)

据我了解,普遍的共识是不要使用 VARCHAR(n) 作为主键,因为它需要更多的存储和性能来进行查找和连接,通常这对我来说很有意义。

但是,如果我们所有的操作都针对 ISBN(SELECT * FROM books WHERE isbn = ?UPDATEDELETE 等)- 为什么不使用 VARCHAR(24) 作为主键?

我很难理解,如果你有一个 immutable 自然键(比如一本书的 ISBN),并且 95% 的数据库操作无论如何都需要使用该字段,那么不应该使用 VARCHAR(24) 总是优于代理键设计?

我觉得这里有一个代理 AUTO_INCREMENT INT 键,完全没有意义。它没有任何好处。

或者在确定主键时我是否遗漏了一些基本知识。

我会使用 ISBN 作为主键。

MySQL 的默认存储引擎 InnoDB 中的主键查找比通过二级索引查找更有效。

的确,整数比 24 字符的 varchar 占用更少的存储空间 space,但在您的情况下,我假设您无论如何都必须存储 ISBN。如果您可以使用整数 而不是 ISBN,那将节省存储空间。

上面关于自然键往往会违反唯一性的评论通常是一个很好的警告。违规行为通常来自市场部。 ;-)

但是对于给定的数据集,您可以确定自然键没有重复项。如果您确实在阅读图书馆馆藏中的 ISBN 时遇到错误,图书馆员将不得不手动解决该问题。但我不希望这种情况经常发生在 500,000 本书中。

提示:使用二进制排序规则定义varchar,进行字符串比较会更快一些。例如:

CREATE TABLE Books (
 isbn varchar(24) COLLATE utf8mb4_bin,
 -- ...other columns...
 PRIMARY KEY (isbn)
) DEFAULT CHARSET=utf8mb4;

代理性能(一般性讨论)

  • “几乎总是”?号

  • AUTO_INCREMENT 甚至不用于加入 - 如果您有“自然 PK”,为什么还要麻烦它;它需要 space 而没有提供任何好处。

  • UUID/GUID -- 通常更糟。这是由于缺少“参考地点”。

  • Many-to-many 映射 table -- 总是更糟。 Best:自然PK就是ids对。辅助密钥是一对,但顺序相反。

  • Space:由于PK被默默地包含在每个二级索引中,PK越大,二级索引就越庞大。如果您只有一个二级索引,那么大小就是一个折腾。副手多了两个,space就被啃光了

  • PK上的范围扫描。如果您需要使用 BETWEEN(等),那么将该范围键作为 PK 通常是有益的。相反,如果范围扫描正在通过二级索引(非常有效),但随后必须进入数据的 BTree,这将是很多额外的工作。

  • “索引合并”。一些 DB 供应商通过收集“行标识符”执行 AND 或 OR,然后对列表进行 and'ing 或 or'ing。然后查找实际行。 InnoDB 的结构方式,这几乎不值得做。

  • 参考地点。 auto-inc 值按大致时间顺序排列数据。这可能是一个好处。或者它可能不会。获取传感器数据或股票报价。主要查询根据 sensor_id 或股票代码或作者或用户查找多行,而不是日期时间,但数据到达 time-order。实际上(总体)PRIMARY KEY(ticker, datetime) 更好。 (或者,如果可能存在重复,则 PRIMARY KEY(sensor_id, datetime, id), INDEX(id)。)我看到一个 系统 以这种方式重新安排最大 table 的 PK 时,吞吐量翻了一番。

  • 我已经创建了数百(数千?)个 table。浏览他们的 PK,我发现只有 1/3 使用 AUTO_INCREMENT 代理 PK。

底线:由于我每次键入时都会思考您的问题 CREATE TABLE,我会说“自然”键在 2/3 的情况下更好。

查看ISBN

关闭您的特定 ISBN。看看你的 tables。您有一个 'wants' 以 ISBN 作为其 PK,对吗?您多久加入一次table? table 上有多少二级索引?你可能从来没有做过像 WHERE ISBN > "..." 这样的范围查询,对吗?

如果您有 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,您可能还会有 UNIQUE(isbn)。但反之则不然。

ISBN 应该是 VARCHAR(24) CHARACTER SET ascii COLLATE ascii_bin。这对速度和 space.

都有帮助(一点点)