【问题标题】:MySQL efficient select / order on a range query with multiple / single column indicesMySQL 对具有多个/单列索引的范围查询的高效选择/排序
【发布时间】:2009-12-23 11:40:15
【问题描述】:

我正在尝试对包含 300 万条记录的表进行最有效的选择。

首先是一些详细信息

表格:

CREATE TABLE IF NOT EXISTS `activities_index` (
  `id` int(9) NOT NULL auto_increment,
  `activity_id` int(6) NOT NULL,
  `activity_status_id` int(2) NOT NULL,
  `activity_source_id` int(6) default NULL,
  `account_id` int(6) default NULL,
  `owner_account_id` int(4) default NULL,
  `date` date NOT NULL,
  `is_event` int(1) NOT NULL,
  `name` varchar(255) collate utf8_unicode_ci NOT NULL,
  `content` longtext collate utf8_unicode_ci,
  `location_name` varchar(255) collate utf8_unicode_ci default NULL,
  `location_content` longtext collate utf8_unicode_ci,
  `meta_keywords` varchar(255) collate utf8_unicode_ci default NULL,
  `thumb_filename` varchar(255) collate utf8_unicode_ci default NULL,
  `popular` int(1) NOT NULL default '0',
  `price` float default NULL,
  `city_id` int(9) default NULL,
  `province_id` int(4) default NULL,
  `country_id` int(4) default NULL,
  `activity_location_id` int(6) NOT NULL,
  `lat` decimal(10,6) default NULL,
  `lng` decimal(10,6) default NULL,
  `activity_modified` datetime default NULL,
  `activity_created` datetime NOT NULL,
  `activity_location_modified` datetime default NULL,
  `activity_location_created` datetime NOT NULL,
  `modified` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `is_event_idx` (`is_event`),
  KEY `activity_id_idx` (`activity_id`),
  KEY `status_city_idx` (`activity_status_id`, `city_id`),
  KEY `date_idx` (`date`),
  FULLTEXT KEY `txt_fields_idx` (`name`,`location_name`,`meta_keywords`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=14865 ;

查询:

SELECT SQL_NO_CACHE * FROM `activities_index` WHERE 
date BETWEEN '2009-12-23' AND '2010-1-23' AND
activity_status_id = 1 AND
city_id IN ('86', '84', '87', '2381', '453', '137', '1561', '1116', '1614', '2456', '512', '305', '443', '1182', '2229')
ORDER BY date
LIMIT 25

关于我的索引选择:
主要问题是 DATE 的范围选择。为什么我不使用基于我认为正确的多列索引,如果我错了,请纠正我: MySQL 在范围之后不使用任何索引。所以索引 (DATE, ACTIVITY_STATUS_ID, CITY_ID) 是没用的。 只有在使用正确的前缀时,索引表上的 order by 才是正确的。所以多列索引(CITY_ID, ACTIVITY_STATUS_ID, DATE) 不会给出正确的排序结果,因为我们要对列 DATE 上的数据进行排序。

说明:
在对查询进行 EXPLAIN 时,可能的键顺序是 CITY_IDX,DATE_STATUS_IDX,而不是我认为将该顺序翻转到 DATE_IDX,CITY_IDX 在按 DATE 排序时会更有效。

id  select_type  table  type  possible_keys  key  key_len  ref  rows  Extra<br />
1  SIMPLE  activities_index  range  city_idx,date_idx  city_idx  5  NULL  1363  Using where; Using filesort

我的问题:
如何翻转可能键的顺序?
有没有更好的方法来解决这个问题:在有 300 万条记录的表上进行有效选择?
我的思维方式正确吗?

【问题讨论】:

  • 您的表定义在 city 上没有单个索引,但解释输出似乎有。该输出是否可能在不同版本的表上?

标签: mysql indexing


【解决方案1】:

据我所知,sql-query-analyzer 从右到左解析查询 - 所以他遇到的第一个索引是 city-one,因为它是最右边的。也许您可以通过更改 in 和 between-clause 的位置来翻转索引。 您需要表格中的所有信息吗?如果没有,您可以通过仅选择您需要的列来获得一些速度。

【讨论】:

  • 啊。这似乎行不通。在应用程序中,我们定义了特定的字段。我认为如果我不包含这些字段会更容易阅读。
【解决方案2】:

我现在在想一些完全不同的东西。由于 city_ids 是 base_city + range 的结果,因此可以仅使用日期加上 where 子句中的算法来定义 base_city -> 活动的距离。这大约需要 0.009 秒才能完成。缺点是我们有时仍然使用 city_ids 的用法。唔。

SQL_NO_CACHE *
FROM `activities_index` AS idx
WHERE 
ROUND(
((acos(sin((52.220818*pi()/180)) * sin(( idx.lat *pi()/180)) + cos((52.220818*pi()/180)) * cos(( idx.lat *pi()/180)) * cos(( (6.891140 -  idx.lng )*pi()/180 )))) 
*180/pi()) *60*1.1515*1.609344
) < 15 AND idx.date BETWEEN '2009-12-23' AND '2010-1-23'
ORDER BY idx.date
LIMIT 25

【讨论】:

    【解决方案3】:

    关于index mergining 的一些有趣信息。不幸的是,您的查询是列出的缺陷之一(单范围扫描)的完美示例。

    您回复中的查询是否更好取决于很多在给定日期范围内您有多少行,因为您肯定不会从该算法中获得任何优化。但是,如果日期范围可以充分缩小行,那可能是最有效的。

    注意:EXPLAIN 输出中可能键的顺序不重要。您的措辞也听起来好像您将EXPLAIN 输出解释为说它正在使用date 进行范围选择。它不是。它正在对 city_id 进行范围选择(它将扫描每一行,其中 city_id 值介于您的 IN() 子句中的最小值和最大值之间。这样做的效率将在很大程度上取决于您的值的分布。

    您是否尝试过运行ANALYZE TABLE activities_index 来查看查询速度和/或EXPLAIN 的输出是否发生变化。 MySQL 经常尝试根据列类型来预测值分布,但实际上分析表会给出一个真实的分布来使用,这可以让它更好地选择最佳键。

    【讨论】:

      猜你喜欢
      • 2021-01-19
      • 2013-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-16
      • 1970-01-01
      • 2013-10-05
      相关资源
      最近更新 更多