【问题标题】:Is this strategy for fast substring search in MySQL fast enough?这种在 MySQL 中进行快速子字符串搜索的策略是否足够快?
【发布时间】:2020-10-10 11:22:37
【问题描述】:

我有一个包含数百万行的 USER 表。我正在实现一个搜索功能,允许某人通过输入用户名来查找用户。这个自动完成功能需要非常快。鉴于在 MySQL 中,列索引使用 LIKE {string}% 加速查询,以下方法的性能是否足以在 200 毫秒内返回? (注意:这里的内存开销不是问题,用户名最多 30 个字符)。

创建一个 USERSEARCH 表,该表具有用户表的外键和 索引 ngram 用户名列:

    USERSEARCH
    
    user_id    username_ngram   
    -------------------------
    1          crazyguy23         
    1          razyguy23       
    1          azyguy23      
    1          zyguy23       
    ...       

查询将是:

    SELECT user_id FROM myapp.usersearch WHERE username_ngram LIKE {string}%
    LIMIT 10

我知道存在第三方解决方案,但出于其他原因我想暂时远离它们。这种方法在速度方面可行吗?如果数据库需要检查所有 O(30n) 行,其中 n 是用户数,我是否高估了索引的功能?

【问题讨论】:

  • 为什么要使用_n 列来表示应该在行中的可变数量的数据?
  • (关于您编辑的问题)那更好..现在您的性能测试显示了什么?如果您的服务器是奔腾 4,win98 在 CF 卡上作为硬盘,我们不能保证它会在
  • 请注意,没有 ORDER BY 的 LIMIT 是毫无意义的
  • Big-O 表示法仅在谈论算法时才有意义,而不是数据大小。只需说您有大约 100M 行(最接近 10 的幂即可,向上取整),为我们提供数据集大小的示例。
  • "用户只需要感觉得到很好的照顾。" -- 是的!

标签: mysql sql database database-design


【解决方案1】:

可能不会。 union distinct 将处理每个子查询以完成。

如果您只想要任意行,请将其表述为:

(SELECT user_id
 FROM myapp.usersearch
 WHERE username_1 LIKE {string}%
 LIMIT 10
) UNION DISTINCT
(SELECT user_id
 FROM myapp.usersearch
 WHERE username_2 LIKE {string}%
 LIMIT 10
)
LIMIT 10;

这至少可以为您节省大量使用常用前缀的时间——比如'S'

也就是说,这只是返回 10 个user_ids 的任意列表,而可能还有更多。

我不知道速度是否足以满足您的应用程序。您必须通过测试一组适当的数据来做出判断。

【讨论】:

  • 如果第一个 SELECT 找到 10 个匹配项怎么办,有没有办法让 UNION 之后的下一个 SELECT 永远不会发生?也许通过在第二个 SELECT 中插入某种变量(例如 LIMIT 10-count(*) )
  • @Rage 。 . .您也许可以使用 CTE 做到这一点,但这可能很棘手。
  • 这是完全相同的SELECT 的两个副本。也许你想要一些不同的东西?
【解决方案2】:

假设固态硬盘,那应该是非常快的,是的。

以下是一些进一步的优化:

  1. 我会在您的查询中添加DISTINCT,因为多次返回相同的 user_id 是没有意义的。尤其是在搜索非常常见的前缀时,例如单个字母。

  2. 还可以考虑仅搜索至少 3 个输入字母。 Less 往往毫无意义(因为希望您的用户名至少有 3 个字符长)并且对您的数据库造成不必要的打击。

  3. 如果您不再添加任何列(我希望您不添加,因为此表是为了快速搜索!),我们可以做得更好。交换列。制作主键(username_ngram,user_id)。这样,您就可以直接在主键上进行搜索。 (请注意结果的字母顺序的额外好处!嗯...匹配的后缀上的字母,也就是说,不是完整的用户名。)

  4. 确保您在 user_id 上有一个索引,以便在您需要更改用户名时能够为用户替换所有内容。 (为此,只需删除该 user_id 的所有行并插入全新的行。)

  5. 也许我们可以做得更好。由于这只是为了快速搜索,您可以使用READ_UNCOMMITTED 的隔离级别。如果我没记错的话,这可以避免放置任何读锁,并且应该更快。它可以读取未提交的数据,但那又如何……然后,您只需在另一个表中查询任何结果 user_ids,如果该用户仍在创建中,则可能找不到它们。你没有失去任何东西。 :)

【讨论】:

    【解决方案3】:

    我认为您需要使用 mysql 全文索引来提高性能。 您需要更改语法以使用全文索引。

    创建全文索引

    CREATE FULLTEXT INDEX ix_usersearch_username_ngram ON usersearch(username_ngram);

    mysql官方文档如何使用全文索引https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html

    【讨论】:

    • 全文索引无法满足我的需求。它索引完整的单词。如果我在 username_ngram 上有一个全文索引,那么将永远找不到字符串“guy”。
    • 默认是。阅读。如果这对你不起作用,没关系。但我认为这是可能的。 dev.mysql.com/doc/refman/8.0/en/fulltext-search-ngram.html
    猜你喜欢
    • 2013-01-20
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    • 2010-12-18
    • 1970-01-01
    • 2012-06-03
    • 2012-01-22
    相关资源
    最近更新 更多