【问题标题】:Optimizing a query for optional fields from another table优化对另一个表中的可选字段的查询
【发布时间】:2012-11-01 22:05:53
【问题描述】:

我有一个名为 items 的 innodb 表,它为一个电子商务网站提供支持。搜索系统允许您搜索可选/附加字段,以便您可以例如仅搜索仅超过 2000 年的维修电脑或汽车。

这是通过名为 items_fields 的附加表完成的。 它的设计非常简单:

+------------+------------------------+------+-----+---------+----------------+
| id         | int(11)                | NO   | PRI | NULL    | auto_increment |
| field_id   | int(11)                | NO   | MUL | NULL    |                |
| item_id    | int(11)                | NO   | MUL | NULL    |                |
| valueText  | varchar(500)           | YES  |     | NULL    |                |
| valueInt   | decimal(10,1) unsigned | YES  |     | NULL    |                |
+------------+------------------------+------+-----+---------+----------------+

还有一个名为 fields 的表,其中仅包含字段名称和类型。

返回搜索结果的主要查询如下:

SELECT items...   
FROM items   
WHERE items... AND (  
      SELECT count(id)  
      FROM items_fields    
      WHERE items_fields.field_id = "59" AND items_fields.item_id = items.id AND
      items_fields.valueText = "Damaged")>0  
ORDER by ordering desc LIMIT 35;

在大规模(每天仅 400 万次以上的搜索查询)中,我需要进一步优化这些高级搜索。目前,高级搜索查询的平均时间约为 100 毫秒。

如何加快查询速度?您还有其他优化建议吗?两个表都是innodb,服务器堆栈绝对很棒,但是我仍然要解决这个查询:)

【问题讨论】:

  • 您是否只想要在项目字段中具有特定字段的所有项目的列表?你能发布一个你想要的结果集的例子吗?
  • 另外,您是否总是对您正在寻找的字段/值对的“属性”表进行查询?另外,对于 item_fields 表,我至少会在 (field_id, valueText) 和 (field_id, valueInt) 上有一个索引

标签: mysql optimization count large-scale


【解决方案1】:

添加和索引 (item_id, field_id, valueText),因为这是您的搜索。

摆脱内部选择!!! MySQL 最高 5.5 无法使用内部选择优化查询。据我所知,MariaDB 5.5 是目前唯一支持内部选择优化的 MySQL 替代品。

 SELECT i.*, f2.* as damageCounter FROM items i  
    JOIN items_fields f ON f.field_id = 59
                       AND f.item_id = i.id
                       AND f.valueText = "Damaged"
   JOIN item_fields f2 ON f2.item_id = i.id
   ORDER by i.ordering desc 

   LIMIT 35;

第一次连接将限制返回的集合。第二次加入将获取满足第一次加入的项目的所有 item_fields。在第一个和最后一个连接之间,您可以添加更多连接条件,这些条件将根据附加点过滤掉结果。例如:

   SELECT i.*, f3.* as damageCounter FROM items i  
    JOIN items_fields f ON f.field_id = 59
                       AND f.item_id = i.id
                       AND f.valueText = "Damaged"
   JOIN items_fields f2 ON f2.field_id = 22
                       AND f2.item_id = i.id
                       AND f.valueText = "Green"
   JOIN item_fields f3 ON f3.item_id = i.id
   ORDER by i.ordering desc 

   LIMIT 35;

这将返回一个结果集,其中包含字段 59 的值为“Damaged”,字段 22 的值为“Green”及其所有 item_fields。

【讨论】:

  • 是的,但我也可以,例如找到字段 id 为 59 且记录损坏的所有项目,然后是另一个字段,比如里程记录为“150000”(公里)等等......所以基本上有无穷无尽的可能性。在这种情况下,我只需要构建更多的 AND (SELECT count(id) FROM items_fields WHERE items_fields.field_id = "59" AND items_fields.item_id = items.id AND items_fields.valueText = "Damaged")>0 MySql 服务器,我我使用的是 5.5 percona,所以不用担心 Inner Join。
  • @emirb 哦,我明白了。您不关心计数,您只是将其用作真/假字段。您可以将多个 AND 串在一起。
  • @emirb 查看我的更新,让我知道它是否适合您的情况
  • @emirb 另外,如果您搜索的是索引(item_id、field_id、valueText),您将从中受益匪浅
  • @emirb 只需确保它是 (item_id, field_id...) 而不是 (field_id, item_id...)。顺序很重要,我猜 item_id 的基数远高于 field_id
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 2023-03-16
相关资源
最近更新 更多