【问题标题】:Performance of fulltext search in joined tables连接表中全文搜索的性能
【发布时间】:2017-03-24 12:22:42
【问题描述】:

我有三张桌子:

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')

所以看来我只有在主表中进行全文搜索时才能获得良好的性能,而不是加入该表的那些。在连接表中进行全文搜索时,如何获得良好的性能?

【问题讨论】:

    标签: mysql performance join full-text-search


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      当优化器在MATCH 和其他类型的过滤器之间进行选择时,它会选择FULLTEXT,而不是其他。

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

      LEFT JOIN 喜欢先过滤“左”表。因此,该表可以使用FULLTEXT,但不能使用“正确”表。一般来说,除非你需要,否则不要使用LEFT

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多