什么时候应该使用复合索引?
什么时候应该使用复合索引?
何时应该在数据库中使用复合索引?
使用复合索引会对性能产生什么影响?
为什么要使用复合索引?
例如,我有一个“homes”表:
CREATE TABLE IF NOT EXISTS `homes` (
`home_id` int(10) unsigned NOT NULL auto_increment,
`sqft` smallint(5) unsigned NOT NULL,
`year_built` smallint(5) unsigned NOT NULL,
`geolat` decimal(10,6) default NULL,
`geolng` decimal(10,6) default NULL,
PRIMARY KEY (`home_id`),
KEY `geolat` (`geolat`),
KEY `geolng` (`geolng`),
) ENGINE=InnoDB ;
是否有必要为“geolat”和“geolng”同时使用复合索引,即将:
KEY `geolat` (`geolat`),
KEY `geolng` (`geolng`),
替换为:
KEY `geolat_geolng` (`geolat`, `geolng`)
如果是的话:
为什么?
使用复合索引会对性能产生什么影响?
更新:
由于许多人已经指出它完全取决于我执行的查询,下面是最常见的查询:
SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
更新2:
使用以下数据库模式:
CREATE TABLE IF NOT EXISTS `homes` (
`home_id` int(10) unsigned NOT NULL auto_increment,
`primary_photo_group_id` int(10) unsigned NOT NULL default '0',
`customer_id` bigint(20) unsigned NOT NULL,
`account_type_id` int(11) NOT NULL,
`address` varchar(128) collate utf8_unicode_ci NOT NULL,
`city` varchar(64) collate utf8_unicode_ci NOT NULL,
`state` varchar(2) collate utf8_unicode_ci NOT NULL,
`zip` mediumint(8) unsigned NOT NULL,
`price` mediumint(8) unsigned NOT NULL,
`sqft` smallint(5) unsigned NOT NULL,
`year_built` smallint(5) unsigned NOT NULL,
`num_of_beds` tinyint(3) unsigned NOT NULL,
`num_of_baths` decimal(3,1) unsigned NOT NULL,
`num_of_floors` tinyint(3) unsigned NOT NULL,
`description` text collate utf8_unicode_ci,
`geolat` decimal(10,6) default NULL,
`geolng` decimal(10,6) default NULL,
`display_status` tinyint(1) NOT NULL,
`date_listed` timestamp NOT NULL default CURRENT_TIMESTAMP,
`contact_email` varchar(100) collate utf8_unicode_ci NOT NULL,
`contact_phone_number` varchar(15) collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`home_id`),
KEY `customer_id` (`customer_id`),
KEY `city` (`city`),
KEY `num_of_beds` (`num_of_beds`),
KEY `num_of_baths` (`num_of_baths`),
KEY `geolat` (`geolat`),
KEY `geolng` (`geolng`),
KEY `account_type_id` (`account_type_id`),
KEY `display_status` (`display_status`),
KEY `sqft` (`sqft`),
KEY `price` (`price`),
KEY `primary_photo_group_id` (`primary_photo_group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=8 ;
使用以下SQL:
EXPLAIN SELECT homes.home_id,
address,
city,
state,
zip,
price,
sqft,
year_built,
account_type_id,
num_of_beds,
num_of_baths,
geolat,
geolng,
photo_id,
photo_url_dir
FROM homes
LEFT OUTER JOIN home_photos ON homes.home_id = home_photos.home_id
AND homes.primary_photo_group_id = home_photos.home_photo_group_id
AND home_photos.home_photo_type_id = 2
WHERE homes.display_status = true
AND homes.geolat BETWEEN -100 AND 100
AND homes.geolng BETWEEN -100 AND 100
EXPLAIN返回:
id select_type table type possible_keys key key_len ref rows Extra
----------------------------------------------------------------------------------------------------------
1 SIMPLE homes ref geolat,geolng,display_status display_status 1 const 2 Using where
1 SIMPLE home_photos ref home_id,home_photo_type_id,home_photo_group_id home_photo_group_id 4 homes.primary_photo_group_id 4
我不太理解如何阅读EXPLAIN命令。这看起来是好的还是坏的。目前,我没有为geolat和geolng使用复合索引。我应该使用吗?
当查询需要使用多个字段进行连接、过滤和选择时,应该使用复合索引。复合索引的格式如下所示:
index( column_A, column_B, column_C )
这种索引将对使用这些字段进行连接、过滤和选择的查询产生帮助。它还将对使用该复合索引的左侧子集的查询产生帮助。所以上述索引也将满足需要的查询:
index( column_A, column_B, column_C ) index( column_A, column_B ) index( column_A )
但对于需要的查询,它将没有帮助(至少不是直接的,如果没有更好的索引可能部分帮助)。注意到缺少了column_B。
在您的原始示例中,对于两个维度的复合索引,大多数情况下将对同时查询这两个维度或仅查询左侧维度的查询产生帮助,但不对仅查询右侧维度的查询产生帮助。如果您始终查询两个维度,那么使用复合索引是正确的选择,不管哪个维度放在第一位(很可能是这样)。
马克,我已更新我的原始帖子(更新2)。这是我的实际查询。我的实际数据库模式。以及EXPLAIN命令返回的结果。因此,根据这些信息,我应该使用复合索引吗?我仍然不清楚。提前谢谢。
马克,你回答中的复合索引是否满足index(column_C)?
-1因为复合索引不能帮助WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???
。它将在第一个字段之后停止。来自"Question Overflow"的答案解释了为什么。
我真正想知道的是:复合索引相比于每个列上的单独索引的好处是什么?
MySQL在查询中每个表只能使用一个索引(有例外情况,例如索引合并)。这意味着查询中的表必须对所有的where条件、表连接、group-by和order-by使用单个索引。因此,每个列上的单独索引可能不总是有效,但复合索引可以实现这一点。
什么时候应该使用复合索引?
当你有以下三个查询时:
查询I:
SELECT * FROM homes WHERE `geolat`=42.9 AND `geolng`=36.4
查询II:
SELECT * FROM homes WHERE `geolat`=42.9
查询III:
SELECT * FROM homes WHERE `geolng`=36.4
如果你为每列分别建立索引,这三个查询都可以使用索引。但是在MySQL中,如果你有一个复合索引(geolat
,geolng
),只有查询I和查询II(使用复合索引的第一部分)使用索引。在这种情况下,查询III需要进行全表搜索。
在MySQL参考手册的多列索引章节中,清楚地解释了如何使用多列索引,所以我不想重新输入手册。
从MySQL参考手册页面可以看到:
多列索引可以被看作是一个包含通过连接索引列值创建的值的排序数组。
如果你为geolat和geolng列分别建立索引,你的表中会有两个不同的索引,可以独立搜索。
INDEX geolat ----------- VALUE RRN 36.4 1 36.4 8 36.6 2 37.8 3 37.8 12 41.4 4 INDEX geolng ----------- VALUE RRN 26.1 1 26.1 8 29.6 2 29.6 3 30.1 12 34.7 4
如果你使用复合索引,你只有一个索引同时包含这两列:
INDEX (geolat, geolng) ----------- VALUE RRN 36.4,26.1 1 36.4,26.1 8 36.6,29.6 2 37.8,29.6 3 37.8,30.1 12 41.4,34.7 4
RRN是相对记录编号(简单来说,可以看作ID)。前两个索引是分开生成的,第三个索引是复合索引。从中可以看出,你可以基于geolng在复合索引上进行搜索,因为它是按照geolat进行索引的,但是你也可以通过geolat或者"geolat AND geolng"进行搜索(因为geolng是第二级索引)。
此外,还可以查看MySQL如何使用索引的手册章节。
实际上,我并没有这些查询。我的查询在原帖中。我的查询是返回特定地理网格(例如邻居/城市/县)内的房屋。我知道空间索引,我不想计算距离。我只是想知道在这种情况下使用复合索引是否有意义。
Eyazici,我已经更新了原始帖子(更新2)。这是我的实际查询。这是我的实际数据库架构。以及EXPLAIN命令返回的结果。所以,根据这些信息,我应该使用复合索引吗?我仍然不清楚。提前感谢。
@"实际上,我并没有这些查询。"实际上,你有这些查询,我使用简单的WHERE条件来解释基本逻辑。在使用条件(即WHERE)的情况下,MySQL会尽可能使用索引。"x BETWEEN a AND b"类似于"x>a AND x应该为你的场景使用每列单独的索引。
我不明白。为什么在我总是执行包含这两列的查询时,我应该使用分开的索引?
我已经修改了我的帖子,并引用了为什么你应该为你的目的分开索引的原因。复合索引不会被单独存储,它们被存储在同一个地方(这是与分开索引的唯一区别)。
所以看起来我应该使用复合(拼接)的地理纬度/经度索引。是吗?
由于你在条件中使用了"AND",所以"是的,你应该使用复合索引"。
我很遗憾,现在更加困惑了:( 三个评论之前,你说我应该使用"分开"的索引。而你最后一条评论说我应该使用"复合"的索引(一次)。
是的,很抱歉,这是对你查询的误解。为你给出的目的,使用不同的索引没有意义。
所以实际上,我应该使用地理纬度和经度的复合索引。对吗?
是的,如果你的条件只是"geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???",那么你应该使用复合索引。
不,当遇到"范围"(比如BETWEEN
)时,不会考虑索引的其他字段!所以复合索引没有更好的效果。
当应该使用复合索引?
复合索引的作用可能存在误解。许多人认为,只要where
子句涵盖了索引列(在这种情况下是geolat
和geolng
),就可以使用复合索引来优化搜索查询。让我们深入探讨一下:
我相信您的房屋坐标数据可能是随机的小数,如下所示:
home_id geolat geolng 1 20.1243 50.4521 2 22.6456 51.1564 3 13.5464 45.4562 4 55.5642 166.5756 5 24.2624 27.4564 6 62.1564 24.2542 ...
由于geolat
和geolng
的值几乎不会重复。复合索引geolat
和geolng
会是这样的:
index_id geolat geolng 1 20.1243 50.4521 2 20.1244 61.1564 3 20.1251 55.4562 4 20.1293 66.5756 5 20.1302 57.4564 6 20.1311 54.2542 ...
因此,复合索引的第二列基本上是无用的!使用复合索引的查询速度可能与仅使用geolat
列的索引相似。
正如Will所提到的,MySQL提供空间扩展支持。空间点存储在单个列中,而不是两个单独的lat
和lng
列。空间索引可以应用于这样的列。然而,根据我的个人经验,效率可能被高估了。可能空间索引并没有解决二维问题,而只是通过使用具有二次分裂的R树加快了搜索速度。
这种折衷方案是,空间点消耗更多的内存,因为它使用八字节的双精度数来存储坐标。如果我错了,请纠正我。