【发布时间】: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 3000SELECT * FROM connections WHERE user_id_from=x ORDER BY counter DESC LIMIT 100SELECT user_id_from, counter FROM connections WHERE user_id_to IN (x1, x2, ..., x1000) LIMIT 500SELECT * 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