什么时候应该使用复合索引?
- 什么时候应该在数据库中使用组合索引?
- 什么是使用复合索引的性能分支)?
- 为什么我应该使用复合索引?
例如,我有一个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
使用复合索引是否geolng
,例如:
我replace:
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 )
将有利于使用这些字段进行连接,筛选和有时select的查询。 这也将有利于使用该组合中最左侧子列的查询。 所以上面的索引也会满足需要的查询
index( column_A, column_B, column_C ) index( column_A, column_B ) index( column_A )
但是它不会(至less不是直接的,也许它可以帮助部分,如果没有更好的指数)帮助需要的查询
index( column_A, column_C )
注意column_B是如何丢失的。
在原始示例中,两个维度的组合索引将主要受益于在两个维度或最左边的维度上查询的查询,但本身并不是最右边的维度。 如果你总是查询两个维度,那么组合索引是要走的路,首先(最有可能)并不重要。
想象一下,你有以下三个查询:
查询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需要全表search。
在手册的多列索引部分,清楚地解释了多列索引如何工作,所以我不想重新input手册。
从MySQL参考手册页面 :
可以将多列索引视为包含通过连接索引列的值创build的值的sorting数组。
如果对geolat和geolng列使用分隔索引,则表中有两个可以独立search的不同索引。
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是相对logging号(为了简化,可以说ID)。 前两个索引分别生成,第三个索引是复合索引。 正如你所看到的,你可以在geolng基础上进行search,因为它是由geolat索引的,但是可以通过geolt或“geolt AND geolng”进行search(因为geolng是二级索引)。
另外,看看MySQL如何使用索引手册部分。
关于什么是复合指数,可能会有一个误解。 许多人认为,只要where
子句包含索引列(在您的情况下是geolat
和geolng
,可以使用组合索引来优化search查询。 让我们深入研究:
我相信你在家庭坐标上的数据是随机小数,如下所示:
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提供了空间扩展支持。 空间点存储在一个列中,而不是两个单独的lng
列。 空间索引可以应用于这样的列。 但是,根据我个人的经验,效率可能被高估了。 这可能是空间索引不能解决二维问题,而只是加速使用具有二次分裂的R树的search。
这个权衡是空间点消耗更多的内存,因为它使用8个字节的双精度数字来存储坐标。 纠正我,如果我错了。
综合指数非常强大,因为它们:
- 强化结构完整性
- 在筛选的ID上启用sorting
执行结构完整性
复合索引不只是另一种types的索引, 他们可以通过强制完整性作为主键来为表提供NECESSARY结构。
Mysql的Innodb支持集群,下面的例子说明了为什么一个复合索引可能是必要的。
要创build朋友的表格(即社交networking),我们需要2列: user_id, friend_id
。
表格结构
user_id (medium_int) friend_id (medium_int) Primary Key -> (user_id, friend_id)
凭借主键(PK)是唯一的,通过创build一个组合PK,Innodb将自动检查在添加新logging时, user_id, friend_id
上user_id, friend_id
存在重复。 这是预期的行为,因为例如,没有用户应该具有比friend_id = 2
更多的1条logging(关系链接)。
没有组合PK,我们可以使用代理键来创build这个模式:
user_friend_id user_id friend_id Primary Key -> (user_friend_id)
现在,每当添加新logging时,我们将不得不检查具有组合user_id, friend_id
的先前logging是否已经存在。
因此,复合索引可以强化结构完整性。
启用筛选ID筛选
按照post的时间(时间戳或date时间)sorting一组logging是很常见的。 通常,这意味着发布给定的ID。 这是一个例子
表User_Wall_Posts(认为如果Facebook的墙上post)
user_id (medium_int) timestamp (timestamp) author_id (medium_int) comment_post (text) Primary Key -> (user_id, timestamp, author_id)
我们要查询和查找user_id = 10
所有post,并按timestamp
(date)对评论post进行sorting。
SQL QUERY
SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES
复合PK使Mysql能够使用索引对结果进行过滤和sorting; Mysql不必使用临时文件或文件夹来获取结果。 没有组合键,这是不可能的,会导致非常低效的查询。
因此,复合键是非常强大的,不仅仅是“我要searchcolumn_a, column_b
”这个简单的问题column_a, column_b
所以我将使用复合键。对于我当前的数据库模式,我只有单个键的复合键。不要忽视复合键的使用!
复合索引对于
- 0或更多“=”子句,加上
- 最多一个范围子句。
组合索引不能处理两个范围。 我在索引食谱中进一步讨论这个问题。
查找最近 – 如果这个问题真的是关于优化
WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???
那么没有索引可以真正处理这两个维度。
相反,我们必须“开箱即用”。 如果一个维度是通过分区来实现的,而另一个维度是通过仔细selectPRIMARY KEY
来实现的,那么对于非常大的lat / lng查询表,可以获得明显更好的效率。 我的博客深入了解如何在全球范围内实现“最接近”的细节。 它包括代码。
PARTITIONs
是纬度范围的条纹。 PRIMARY KEY
故意以经度开始,以便有用的行可能在同一个块中。 一个存储的例程编排凌乱的代码, order by... limit...
以及在目标周围增长“方块”,直到你有足够的咖啡店(或其他)。 它还负责大圆计算和处理date和极点。
没有黑与白,一刀切的答案。
您应该使用复合索引,当您的查询工作负载将从一个受益。
您需要分析您的查询工作负载,以确定这一点。
当查询完全可以从该索引完全满足时,复合索引就会发挥作用。
更新(响应编辑发布的问题):如果您从表中select*可能会使用组合索引,它可能不会。 您需要运行EXPLAIN PLAN来确保。
要进行空间search,您需要一个R-Treealgorithm,它可以快速search地理区域。 确切地说,你需要这份工作。
一些数据库内置空间索引。一个快速的谷歌search显示MySQL 5有他们(看你的SQL我猜你正在使用MySQL)。
我与@米奇,完全取决于你的疑问。 幸运的是,您可以随时创build和删除索引,并且可以将EXPLAIN关键字添加到查询中,以查看查询分析器是否使用索引。
如果你将查找一个精确的经 /长期对这个指数可能是有道理的。 但是你可能会在某个地方的某个特定距离内寻找房屋,所以你的查询看起来就像这样(见源代码 ):
select *, sqrt( pow(h2.geolat - h1.geolat, 2) + pow(h2.geolng - h1.geolng, 2) ) as distance from homes h1, homes h2 where h1.home_id = 12345 and h2.home_id != h1.home_id order by distance
而这个指数很可能根本没有帮助。 对于地理空间查询,你需要这样的东西。
更新:用这个查询:
SELECT * FROM homes WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???
查询分析器可以单独使用geolt上的索引,也可以单独使用geolng上的索引,也可以使用两个索引。 我不认为它会使用复合索引。 但是很容易在真实的数据集上尝试每个排列,然后(a)看看EXPLAIN告诉你什么,(b)衡量查询真正需要的时间。
当您想要优化group by
子句时,组合索引可能很有用(请参阅http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html )。 请注意:
使用GROUP BY索引的最重要的先决条件是所有的GROUP BY列引用来自同一索引的属性,并且索引按顺序存储它的键(例如,这是一个BTREE索引而不是一个HASH索引)