【问题标题】:MySQL compound index with a unique subset具有唯一子集的 MySQL 复合索引
【发布时间】:2019-07-11 12:47:58
【问题描述】:

我有以下一对一的链接表

CREATE TABLE `foo_bar` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `foo_id` int(10) unsigned NOT NULL,
  `bar_id` int(10) unsigned NOT NULL,
  …
  PRIMARY KEY (`id`),
  …
) ENGINE=InnoDB 

其中foo_idbar_id 分别是表foobar 的外键

还有

  • (foo_id, bar_id) 上的唯一索引,用于将 foo_id 的外键映射到 foo 记录,以及查询性能,和…
  • bar_id 上的唯一索引,用于从 bar_id 到条形记录的外键映射

请注意,此设置确实允许非唯一的foo_ids,只要它们伴随着唯一的bar_id


我现在想单独在 foo_id 上强制执行唯一性,同时保留 (foo_id, bar_id) 上现有复合索引的性能优势

是否有任何方法可以创建唯一复合索引,其中对引用的列的子集强制执行唯一性,但允许将更多列添加到索引中?

在这个例子中,这看起来像([foo_id], bar_id),其中方括号表示复合索引的唯一部分

我想避免在 (foo_id, bar_id) 上单独索引的开销

在我的脑海中,我想不出任何算法上的原因为什么这是不可能的 - 只要列的唯一子集位于复合索引的开头

注意,我知道我可以使用 foo_idbar_id 作为表的主键,但应用程序需要一个单独的 ID,通过它可以链接记录单独删除,不泄露链接

基本上foo_bar.id 存储在客户端cookie 中(经过可逆加密后),任何个人数据都可以通过此ID 访问。这个想法是,如果客户请求删除,我们删除foo_bar 记录并删除他们的数据,而不破坏foobar 中的匿名或功能数据

这有额外的好处,即使 cookie 在任何地方持续存在,它也将不再可解析为任何数据

【问题讨论】:

  • 不要认为至少在 MySQL 中是可能的。但会是一个受欢迎的功能。
  • 也许我没有理解正确,但是当您在 bar_id 上已有唯一索引并且希望 foo_id 也唯一时,不需要在 (foo_id, bar_id) 上单独索引.组合的独特性已经得到保证,我怀疑你会遭受严重的性能损失。如果需要,MySQL 还可以合并索引。而且你也可以有“重复”的索引,开销并不像你想象的那么多。我知道,因为我公司的开发人员经常犯这个错误和/或混淆索引和外键。
  • 如果你也让foo_id唯一,那么链接表需要什么?不是主要用于存储一对多或多对多的映射吗?
  • " 我知道,因为我公司的开发人员经常犯这个错误和/或混淆索引和外键" @fancyPants 很可能是因为 MySQL 在创建索引时会创建索引外键也... “你也可以有“重复”的索引,开销没有你想象的那么多”这可能会导致优化器选择一个“错误的”索引并且变得更糟选择性能,因为尽量避免制作冗余索引..
  • " MySql 也在外键上创建索引?" 是的,MySQL 可以这样做/这样做.. 见Using FOREIGN KEY Constraints -> "index_name 表示外键 ID . 如果子表上已经有显式定义的索引可以支持外键,则忽略 index_name 值。否则,MySQL 会隐式创建一个外键索引,根据以下规则命名:"跨度>

标签: mysql indexing


【解决方案1】:

这是一个多:多映射表?

如果id,那么就摆脱它;它很混乱,会减慢速度。

PRIMARY KEY(foo_id, bar_id),
INDEX(bar_id, foo_id)

这些以及更多提示:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table

这有额外的好处,即使 cookie 在任何地方持续存在,它也将不再可解析为任何数据

这意味着执行以下操作之一:

  • 保留所有已创建的哈希值,但将某些哈希值标记为“已删除”。
  • 否则,请确保您永远不会两次生成相同的哈希。

在任何一种情况下,您都可以避免将旧 cookie “解析”为已死或已消失的数据。

这在某种程度上暗示了UNIQUE(或PRIMARY)在某处存在foo_idbar_id 的密钥。

如果这两个唯一性约束在同一个表中,并且出于其他原因需要单独的id,则最小索引为:

PRIMARY KEY(id),
UNIQUE(foo_id),
UNIQUE(bar_id)

PRIMARY KEY(foo_id),
UNIQUE(bar_id),
INDEX(id)   -- This is sufficient to keep `AUTO_INCREMENT` happy.

(foo_id, bar_id) 上不需要任何索引(普通或 UNIQUE),因为第一个上的唯一性约束足以满足对的唯一性和高效查找。

foo_bar.id被加密并存储在客户端cookie中...客户端请求删除,我们删除foo_bar记录

什么样的加密?单向(md5、shar256 等)?还是可逆的(aes ...)?如果是单向的,那么您需要一个将CONCAT(foo_id, bar_id)(或您正在做的任何事情)映射到foobar_id 的索引。

【讨论】:

  • 这是一个一对一的映射表,链接不是强制性的,我希望一个代理ID能够在不引用任何一个表的ID的情况下从外部引用和删除链接
  • 1:1 更没有意义。为什么有桌子?至于id的使用,你可能有foo_id和bar_id,你可以用它们来做delete。
  • 我在我的问题底部添加了进一步的解释,包括为什么我在删除时没有(也不想)访问foo_idbar_id
  • @Arth - 添加到我的答案中。最终,我可能会了解您在做什么,并能够更好地帮助您。
  • @Arth - 正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-10
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 2011-05-28
  • 2017-02-19
相关资源
最近更新 更多