仅针对特定其他列值的一列索引

Index for one column only for specific other column value

我有 table 日志,其中有两个字段:action (VARCHAR 45) 和 info (VARCHAR 10000)。

此 table 记录了多项内容,其中之一是访问页面时的用户 ip。对于这种情况 action='ip', info='IP.ADD.RE.SS'.

因为 info 可以记录大量特定内容的文本,我只想创建适用于 info 字段的索引 action='ip' 只有这样我才能快速搜索 IP,并且没有 "actions".

的过度生长的索引

我已经尝试为前 15 个字符创建 INDEX,但 IP 条目仍然大约占所有内容的 1%,这对我来说似乎有点过分了。 整个解决方案都是从别人那里继承来的,不幸的是,我现在无能为力改变整个架构

任何关于如何正确操作的建议?有可能吗?

无论如何,您都是在操作列上进行过滤,因此组合索引是这里的解决方案。在两列 (action, info(15)).

上创建索引

索引中列的顺序很重要。不要反过来改。

这似乎属于 "EAV" 类别。你有一堆东西(ip、postdel 等),每一个都是可选的。有些需要索引,有些不需要。

我的建议是将键值对放在 JSON 字符串中。并为您想要索引的任何内容(在您的情况下为 IP)创建一个特殊的列。可以NULLable为了最小化(但不是完全消除'wasted'space.

另见我的blog on EAV

另请参阅 MySQL 和涉及 JSON 的 MariaDB 实现。注意:它们需要相对较新版本的 MySQL 或 MariaDB。

一些 RDBMS 产品支持您所描述的内容。它被不同的产品称为partial or filtered indexes

MySQL 没有实现这个想法(他们没有义务实现它,因为它是一个非标准功能)。已经有人要求将此作为一项新功能:https://bugs.mysql.com/bug.php?id=76631

您可以在 MySQL 5.7 中执行的一种模拟部分索引的解决方法是创建一个值为 NULL 的虚拟列,除非 action 为 'ip'。然后索引该虚拟列:

ALTER TABLE logs
  ADD COLUMN ip_info VARCHAR(12) 
    AS (CASE `action` WHEN 'ip' THEN LEFT(info, 12) END),
  ADD KEY (ip_info);

严格来说,这仍然是每一行的索引,但至少它不会将您的任何值存储在索引中,除非操作是 'ip'。

P.S.: 我没有测试过上面的例子,如果有语法错误,请见谅。