【问题标题】:Should I use partitions in this case在这种情况下我应该使用分区吗
【发布时间】:2026-02-15 02:30:02
【问题描述】:

我有下表:

    CREATE TABLE `connections` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `user_id_from` int(11) NOT NULL,
 `user_id_to` int(11) NOT NULL,
 `counter` int(11) NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `to_from` (`user_id_to`,`user_id_from`),
 KEY `user_id_from` (`user_id_from`)
) ENGINE=InnoDB AUTO_INCREMENT=1559108041 DEFAULT CHARSET=utf8

它是 103GB(43GB 数据和 59GB 索引)和大约 ~1,143,663,061 行。我认为主要的性能障碍是索引大小的结果,因此解决方案可能意味着将其减少为小索引(分区)。我正在考虑添加一个 DATE 字段并按 MONTH 进行分区。我可以忍受每次只查询最近的 X 个月(X 大约是 6 个月)。我看到的缺点是这会导致桌子变得比现在大。

在我进行基准测试之前,您会建议这样做吗?您还有其他建议吗?

更新: 我在这张表上使用的查询:
SELECT * FROM connections WHERE user_id_to=x LIMIT 3000
SELECT * FROM connections WHERE user_id_from=x ORDER BY counter DESC LIMIT 100
SELECT user_id_from, counter FROM connections WHERE user_id_to IN (x1, x2, ..., x1000) LIMIT 500
SELECT * FROM connections WHERE user_id_to=x AND user_id_from IN (x1, x2, ..., x1000) LIMIT 1000

我以 user_id_to 作为主要条件并以 user_id_from 作为主要条件进行查询的原因是连接是定向的,我正在寻找相互连接(to->from && from->to)。 WHERE user_id_to 的行数可能非常高,WHERE user_id_from 大多不是那么多,这就是为什么当我 ORDER BY counter 时我没有为此添加索引。

【问题讨论】:

  • 请参阅下面有关可能删除您的索引之一的答案。此外,奇怪的是,您将拥有 _from_to 和 INT 字段而不是日期字段。保持它们在整个表中的唯一性意味着没有两个用户可以有相同的开始和结束日期,这也很奇怪。
  • "Before I benchmark..." - 您应该首先进行基准测试并确定慢的确切查询(以及它们的时间和执行计划) .代理键id 是否有specific reason?如果没有,您可以省略它,并使用{user_id_to, user_id_from} 作为主键,减少所需的存储空间。除此之外,我怀疑{user_id_from, user_id_to} 上的复合索引可能比单独的{user_id_from} 为您提供更好的服务。但所有这些都是在不知道您的查询的情况下的猜想。
  • @BrankoDimitrijevic 删除代理键的有趣想法。它没有任何具体原因,但我发现这些在某些情况下很有用(例如,当想要以块的形式迭代表时)。 {user_id_from, user_id_to} 索引不会比{user_id_from} 大得多吗?为什么你怀疑它会更好地为我服务?关于分区选项的任何想法?
  • @Noam {user_id_from, user_id_to} 可能比 {user_id_from} 单独使用 cover 的典型查询更好 - 当然,如果不知道确切的查询,这是不可能真正知道的,你必须平衡防止尺寸可能增加(见下文)。
  • @Noam 至于大小,如果你继续使用{id}作为PK,这个索引会有点大,或者如果{user_id_to, user_id_from}是PK,这个索引基本相同,因为InnoDB tables are clustered,因此二级索引隐式包含所有 PK 字段,因此 {user_id_to} 无论如何都会隐式包含 user_id_from。我认为在确定实际性能瓶颈之前考虑分区还为时过早。

标签: mysql database database-design partitioning database-performance


【解决方案1】:

您可以通过修改唯一键(取决于您的查询如何使用它们)来删除一个索引user_id_from:而不是to_from,将其设为from_to(user_id_from,user_id_to),例如开始-结束。那么您就不需要user_id_from 上的第二个索引,因为即使不需要第二部分 (user_id_to),也会使用组合索引/键 user_id_from 的第一部分。

所以你只需要:

PRIMARY KEY (`id`)
UNIQUE KEY `from_to` (`user_id_from`,`user_id_to`)

这是节省索引使用空间的一项更改。 (使用表的一小部分对其进行测试,然后查看EXPLAIN 结果)。 PS:当您继续进行此更改时,请删除 user_id_* 索引,然后创建 from_to 索引,以防您的磁盘限制在 30 GB 以内。

特别针对您的问题...
如果不需要旧数据,则以下任一方法可能更有意义:

  1. 删除旧行,例如user_id_to 超过 1 年;或者,
  2. (使用选项 1)- 创建一个类似 old_users 的表,如果需要该信息,只需添加他们的 ID 和 user_id_to 字段。
  3. connections 的旧行插入到新表中,例如connections_archive,然后从connections 中删除。假设您永远不会在应用程序的查询中查询_archive,或者仅从管理界面(即很少)在这种情况下,使用connectionsconnections_archive 对“联合”查询的性能影响是可以忍受的。李>

【讨论】:

  • 我已经用我在这个表上运行的查询更新了这个问题,如果我错了,请纠正我,但我认为你的解决方案不包括像 WHERE user_id_to=x 这样的查询,我需要。
  • 你是对的,我假设你不会有一个只使用user_id_to 而没有user_id_from 的查询。因此,我建议使用复合 {user_id_from, user_id_to} 而不是 to-from。 Branko 的一些 cmets 可能会更好地解决您的问题。
最近更新 更多