在 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'
索引仍然会提供部分帮助,因为 entity
、property
是索引的 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 实例。为无限增长的数据库提前规划需要您进行一些容量规划和基准测试。
您好,有 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'
索引仍然会提供部分帮助,因为 entity
、property
是索引的 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 实例。为无限增长的数据库提前规划需要您进行一些容量规划和基准测试。