【问题标题】:How to speed up MySQL query: order by count如何加速 MySQL 查询:按计数排序
【发布时间】:2012-09-17 17:43:44
【问题描述】:

我已经尽我所能来加快这个查询的速度,但仍然需要大约 2.5 秒。

该表是 images_tags(约 400 万行): 这是表格说明:

Field       Type               Null     Key     Default
image_ids   int(7) unsigned    NO       PRI     NULL
tags_id     int(7) unsigned    NO       PRI     NULL

这里是索引:

Table         Non_unique  Key_name      Seq_in_index  Column_name  Collation  Cardinality  Sub_part  Packed  Null  Index_type
images_tags   0           PRIMARY       1             image_ids    A          NULL         NULL      NULL          BTREE
images_tags   0           PRIMARY       2             tags_id      A          4408605      NULL      NULL          BTREE
images_tags   1           image_ids     1             image_ids    A          734767       NULL      NULL          BTREE

这里是查询:

select image_ids
from images_tags
where tags_id in (1, 2, 21, 846, 3175, 4290, 6591, 9357, 9594, 14289, 43364, 135019, 151295, 208803, 704452)
group by image_ids
order by count(*) desc
limit 10

这里是查询解释:

select_type  table        type   possible_keys  key                 key_len  ref   rows     Extra
SIMPLE       vids_x_tags  index  join_tags_id   join_vids_id_unique  8       NULL  4408605  Using where; Using index; Using temporary; Using filesort

目标是获得与这些标签最匹配的 10 张图像。 我尝试过处理这些变量,但几乎没有改善:

  • max_heap_table_size
  • tmp_table_size
  • myisam_sort_buffer_size
  • read_buffer_size
  • sort_buffer_size
  • read_rnd_buffer_size
  • net_buffer_length
  • preload_buffer_size
  • key_buffer_size

有什么方法可以大大加快这个查询的速度吗?大约有 700K 图像并且它一直在增长,所以我不想将结果缓存超过一两天,并且必须为每个图像完成,所以重新缓存很多查询是不可能的。

【问题讨论】:

  • count(*)?确定吗?不应该是count(image_ids)吗?
  • @Burhan:因为image_ids 没有空值,所以是一样的。
  • 请添加 EXPLAIN 输出。
  • There are about 700K images and it's always growing, so I wouldn't want to cache the result for more than a day or 2 and re-caching that many results would be impossible. - 缓存中不是只有 10 个结果吗?如果信息的流通性不重要;您可以在停机时生成缓存,存储 10 个 ID 并仅在显示中检索它们。
  • @ricka - 每天 700K 次?为什么?您需要生成前 10 名;您可以通过每天运行查询 一次 来生成它一次,然后存储结果。那是你的前 10 名。除非我错过了为标签生成前 10 名图像的内容。

标签: mysql performance tags aggregate


【解决方案1】:

在查询的EXPLAIN 输出中,您会看到key 列与possible_keys 列表中的任何项目都不匹配。这意味着虽然数据是从索引中获取的(在许多情况下,它比实际表小,因为它跨越的列更少),但引擎仍然必须遍历所有行。

如果你想正确地使用索引来加速这个查询,你应该添加一个带有标签的标签作为它的第一个(并且可能是唯一的)组件。

顺便说一下,image_ids 上的索引没有什么用处,因为主键也可以用来提供该信息。通常,多行索引可用于加速查询,这些查询为所有这些列或从第一列开始的连续列集提供显式值(或范围)。换句话说,双列索引也将像单列索引一样用于其第一列,但其第二列本身并没有太大用处,这就是您在这里所拥有的。

作为在tags_id 上添加键并在image_ids 上删除键的替代方法,您可以保持image_ids 上的键不变,并颠倒主键的列顺序。然后主键也可以用于回答仅标记查询。如果您更频繁地通过标签而不是图像来查询表格,那么我建议您使用这种方法。

【讨论】:

    【解决方案2】:

    在这种链接(连接、多对多)表中,在(a, b)(b, a) 上都有两个复合索引几乎总是有用的。您只有其中一个(主索引),而没有另一个。

    如果表中没有其他列,则根本不需要任何其他索引。

    因此,您应该添加 (tags_id, image_ids) 索引并删除多余的 (image_ids) 索引:

    ALTER TABLE images_tags
      DROP INDEX image_ids,
      ADD INDEX tag_image_IDX           -- choose a name for the index
        (tags_id, image_ids) ;
    

    关于特定查询的索引效率取决于很多因素,主要取决于图像和标签的分布(IN 列表中的 15 个标签有多受欢迎?)

    【讨论】:

    • 我运行了该代码,它似乎缩短了大约 100 毫秒。如果没有限制,则返回 304538 行。
    猜你喜欢
    • 2019-02-20
    • 2014-10-07
    • 1970-01-01
    • 1970-01-01
    • 2013-04-17
    • 1970-01-01
    • 2015-06-24
    • 2011-08-05
    • 1970-01-01
    相关资源
    最近更新 更多