连接表中的全文搜索性能

时间:2016-11-10 12:55:31

标签: mysql performance join full-text-search

我有三张桌子:

CREATE TABLE `dp_organisation` (
  `OrganisationId` bigint(32) NOT NULL AUTO_INCREMENT,
  `Name` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `ShortName` text COLLATE utf8mb4_unicode_ci,
  PRIMARY KEY (`OrganisationId`),
  FULLTEXT KEY `fulltext` (`Name`,`ShortName`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `dp_organisation_member` (
  `OrganisationId` bigint(32) NOT NULL,
  `UserId` bigint(32) NOT NULL,
  PRIMARY KEY (`OrganisationId`,`UserId`),
  UNIQUE KEY `UserId` (`UserId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `dp_user` (
  `UserId` bigint(32) NOT NULL AUTO_INCREMENT,
  `Alias` varchar(125) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `Firstname` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `Surname` text COLLATE utf8mb4_unicode_ci,
  `Email` varchar(125) COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`UserId`),
  FULLTEXT KEY `fulltext` (`Alias`,`Firstname`,`Surname`,`Email`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

dp_organisation包含所有组织,而dp_users包含所有用户。 dp_organisation_member是用户和组织之间的关系。每个用户至多是一个组织的成员。

现在我想搜索匹配某些字符串的用户。我想在搜索时检查用户的信息和用户组织的信息,因此应使用dp_usersdp_organisation上的全文索引。我创建了以下查询来实现此目的:

SELECT *
FROM dp_user u
LEFT JOIN dp_organisation_member m ON m.`UserId` = u.`UserId`
LEFT JOIN dp_organisation o ON o.`OrganisationId` = m.`OrganisationId`
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo')
OR MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo')

但查询执行得非常糟糕。为了测试,我尝试了以下内容,只搜索用户的信息:

SELECT *
FROM dp_user u
LEFT JOIN dp_organisation_member m ON m.`UserId` = u.`UserId`
LEFT JOIN dp_organisation o ON o.`OrganisationId` = m.`OrganisationId`
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo')

它的运行速度提高了大约30倍。

如果我只搜索组织的信息:

SELECT *
FROM dp_user u
LEFT JOIN dp_organisation_member m ON m.`UserId` = u.`UserId`
LEFT JOIN dp_organisation o ON o.`OrganisationId` = m.`OrganisationId`
WHERE MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo')

查询再次变慢。

要检查dp_organisation中的全文索引是否有任何问题,我撤消了查询以从dp_organisation中选择并加入dp_user

SELECT *
FROM dp_organisation o
LEFT JOIN dp_organisation_member m ON m.`OrganisationId` = o.`OrganisationId`
LEFT JOIN dp_user u ON u.`UserId` = m.`UserId`
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo')
OR MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo')

上述查询很慢,只搜索用户信息的那个:

SELECT *
FROM dp_organisation o
LEFT JOIN dp_organisation_member m ON m.`OrganisationId` = o.`OrganisationId`
LEFT JOIN dp_user u ON u.`UserId` = m.`UserId`
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo')

但是,仅在组织信息中搜索的查询速度很快(大约快25倍):

SELECT *
FROM dp_organisation o
LEFT JOIN dp_organisation_member m ON m.`OrganisationId` = o.`OrganisationId`
LEFT JOIN dp_user u ON u.`UserId` = m.`UserId`
WHERE MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo')

因此,在主表中进行全文搜索时,我似乎只获得了良好的性能,而不是加入该表的那些。在连接表中进行全文搜索时,我该怎么做才能获得良好的性能?

2 个答案:

答案 0 :(得分:0)

在查询中组合FTS和JOIN会导致速度变慢,因为mysql通常每个表只使用一个索引。当您在表上执行FTS时,mysql使用该表上的全文索引,因此无法使用索引进行连接。

在其他新闻中,dp_organisation_member表上的索引并没有多大意义。您已将user_id字段设为唯一。这意味着用户只能属于一个组织,这实际上意味着dp_organisation_member表是多余的。你已经过规范化了。您可以删除此表并将组织ID添加到dp_user并消除其中一个连接。

答案 1 :(得分:0)

我建议为初学者切换到InnoDB。从5.6.4开始,FULLTEXT可用。有一个few differences需要注意。

当优化程序可以在MATCH和其他类型的过滤器之间进行选择时,它会执行FULLTEXT,而不会执行另一种过滤。

由于WHERE MATCH... OR MATCH...

OR很糟糕。 FULTEXT在这里表现不佳。将其转换为( SELECT ... MATCH ) UNION ( SELECT ... MATCH )是一种可能的解决方法。

LEFT JOIN喜欢过滤'左边'表先。因此,该表可以使用FULLTEXT,但不能使用'权利'表。一般情况下,除非您需要,否则请勿使用LEFT