在 MySQL 中搜索属性的最佳方式

Best way to search properties in MySQL

您好,有 table 个用户,我们在其中存储了一些顶级字段,例如 userid、createdOn、email

第二个 table 称为属性,用于存储附加属性的键值对。比如userid, key, value

键可以是姓名、phone、性别等

无法将其展平,因为我们可能会添加新属性并且不想更改 table。

我们在用户 table 中有超过 1000 万行,在属性 table 中有超过 1 亿行。

查询这样的系统最好的方法是什么。我们目前在 tables 和一个基本的 where 子句上进行连接。

是否有更高级的方法来处理这样的数据?我们经常需要搜索多个字段,比如 name like ‘%jo’ and gender=male and country =usa and foo=bar

您所描述的通常称为 Entity-Attribute-Value,或 EAV。对于您所描述的情况,这很常见,您拥有的潜在属性比您可以制作普通列的更多。

CREATE TABLE eav_table (
  entity INT NOT NULL,
  property VARCHAR(64) NOT NULL,
  value VARCHAR(64),
  PRIMARY KEY (entity, property),
  KEY (entity, property, value)
);

前两列是 table 的主键,因为每一对只有一行。但是为所有三列设置二级索引很有用,因为这将是查询最常读取的列。

以相等条件查询多个值是可以的。 MySQL 可以进行元组比较。

SELECT ...
FROM eav_table
WHERE entity = 1234 AND (property, value) IN (
 ('gender', 'male'),
 ('country', 'usa'),
 ('foo', 'bar')
)

如果 (entity, property, value) 上有索引,优化器将使用它,并有效地找到匹配的行。

缺点是此语法不支持 LIKE 或任何其他类型的模式。所以你需要更明确地做这些:

SELECT ...
FROM eav_table
WHERE entity = 1234 AND property = 'name' AND value LIKE '%jo'

索引仍然会提供部分帮助,因为 entityproperty 是索引的 left-most 列。但是带有前导通配符的 LIKE 模式无论如何都不能使用索引,因此它必须检查与前两列匹配的所有行,并针对该模式测试每一行。效率有点低,但至少可以缩小搜索范围。

如果您还想搜索“哪些实体拥有国家 属性 美国?”您需要另一个具有不同列的二级索引作为 left-most 列:

ALTER TABLE eav_table ADD KEY (property, value);

然后您可以搜索 property/value 并获得一组匹配的实体:

SELECT ...
FROM eav_table
WHERE (property, value) = ('country', 'usa')

如果您有正确的索引来支持您需要执行的搜索,那么即使 table 有数百万或数亿行也能很好地工作。但最终随着 table 变得越来越大,您可能不得不将其拆分为多个 table 或多个 MySQL 实例。为无限增长的数据库提前规划需要您进行一些容量规划和基准测试。