MySQL的全文搜寻FullText方案分析

近段时间一直在对全文搜寻引擎的方案进行调查,核心问题就是现行使用的Select...Like%$1%的模糊搜寻语句在高并行的场景下性能与效能非常差,虽然我已经做了相应的调优,但无奈终究不是长久之计。

实际上在Web 领域经历了15 年的发展,PHP 从版本4 时代的龟速发展到现在的PHP7 的高效,再加上其他语言例如Go 与Rust 之类的高效语言的出现,在Web 开发领域中的瓶颈已经从以前的Web 程式变为SQL。当然在15 年前非常有意思的一个现象是,因为当时InnoDB 还未出生并无事务(Transaction)这么一说,且因当时的Web 程式码效能普遍低于SQL,所以当时很多程式开发都滥用了存储过程来帮助Web 程式提高效能。但这都是15 年前的事情了,现在SQL 早已成为效能瓶颈,就别再给SQL 增加负担了。

虽然现在已经有很多的NoSQL 非关系型SQL 的问世,无奈关系型SQL 还是在如今占据大半江山。然而在15 年中关系型SQL 并未有多大的改进,关系型SQL 的理论已经发展到顶,很难再有非常大的改观。

以下的调查均建立在主要以搜寻(Select)为主的场景中。

如果是以搜寻为主的场景中,通常我们都会使用MyISAM 作为存储引擎。我们假设在一个有100 万数据条目的表中执行一个较为复杂的搜寻语句:

SELECT `column` FROM `table` USE INDEX(field_index) WHERE 1=1 AND (`field` LIKE '%keyword1%' OR `field` LIKE '%keyword2%') AND (`field` LIKE '%keyword3%' OR `field` LIKE '%keyword4%') AND (`field` LIKE '%keyword5%' OR `field` LIKE '%keyword6%') LIMIT 50;

类似如此的语句一执行,有多少个关键词就要扫多少次全表,就算MyISAM 效能再好也都要占用将近30% 的CPU 资源并使用将近1 秒的时间。

很明显我们已经尽力了。这种情况下我们很难再进行调优获得更好的效能。但很刚好,这种大多使用搜寻的使用场景下,却是搜寻引擎的强项。

一般在现在,常见的针对全文搜寻的方案差不多有以下几种:

  • MySQL 内建的FullText 全文搜寻索引
  • MySQL 外挂的Bigram FullText Plugin
  • 各种全文搜寻引擎

以上三种方案我们先来讨论前两种方案。


  • MySQL 内建的FullText 全文搜寻索引

首先,这个内建的FullText 全文搜寻索引对CJK 语言(中国语Chinese,日本语Japanese,韩国语Korean)的支持在低于5.7 版本的MySQL 中几乎等于没有。在MySQL5.7 后引用了N -GRAM 演算法去断词所以对CJK 有了较好的支持。然而此索引虽然支持了全文搜寻,但在关键问题上依旧无明显改善,CPU 占用依旧居高不下,且搜寻时间依旧很长。本身此机能是依靠MySQL 自身建立一个FullText 索引来进行搜寻,然而有一个问题就是在修改数据的时候,又要重新更新索引。这里的性能损失就会非常的离谱。


  • MySQL 外挂的Bigram FullText Plugin

这个外挂插件一开始就引用了N -GRAM 演算法去断词所以对CJK 有较好的支持。然而一个很严重的问题是,如果你的数据条目超过20W 或表超过200MB,那更新索引的时候将会占用大量的CPU 资源,并且能肉眼感觉出这更新速度十分龟速。在20W 条目下,建立索引就已经超过10 分钟。实时更新索引的时候CPU 占用更是居高不下。


很明显以上两种亲和MySQL 的方案都无法实际使用,所以我的目光直接开始搜寻各种全文搜寻引擎。

目前常见的搜寻引擎我大致归类为以下三派:

  • Sphinx Search 派系
  • Apache Lucene 派系
  • 野路子N -GRAM 派系

由于野路子N -GRAM 派系的程式大多娱乐成分较高,所以基本上就不会考虑。接下来着重介绍一下Sphinx Search 派系和Apache Lucene 派系。

Sphinx Search 派系

Sphinx Search 是一款俄国人出品的全文搜寻程式,使用C ++ 程式码编写。特点就是十分高效,速度也非常快。同时Sphinx Search 衍生出了一堆改进品,可谓是此派系的鼻祖。然而,Sphinx Search 在3.0 版本之后选择了闭源,Sphinx Search 本身就开始走向衰败开始鲜有人问津,但从3.0 以前的版本衍生出来的各种搜寻引擎依旧在各自领域发光发热。

Sphinx Search 派系差不多有以下成员:

  • Sphinx Search

优点:性能很好,资源占用非常低。

缺点:局限性挺大,只适合搜寻操作,只适合与MySQL,MSSQL 等关系型SQL 搭配使用。默认对cjk 的支持很差,配置文件略麻烦,且现在已经闭源。

  • MantiCore Search

优点:由于是Sphinx Search 闭源前Fork 过来的项目,所以继承了Sphinx 的所有优点,并自行添加了cjk 的支持,虽然可以直接使用sphinx search 的配置文件,但也支持了一套新写法,较为简单化。性能很好,资源占用非常低。

缺点:还是由于是Sphinx Search 闭源前Fork 过来的项目,所以还是继承了一部分的缺点。例如局限性挺大,只适合搜寻操作,只适合与MySQL,MSSQL 等关系型SQL 搭配使用。

  • Senna

优点:由于是日本人对Sphinx Search 的CJK 支持感到不满而Fork 过来的改良品,所以Sphinx 的优点他也有。也支持了CJK。性能很好。

缺点: Sphinx 系列同样的缺点,局限性大。且最重要的是已经很久没人更新了。

  • Groonga

其实说其是Sphinx Search 派系的,但已经与SphinxSearch 有了天壤之别。倒不如说这已经开始产生独立的一个派系了。

优点:效能好,对CJK 支持很好。

缺点:还是有着一定的局限性。

  • Mroonga

Mroonga 已经是作为MySQL 系列的插件直接与SQL 有着更好的联动了,但实际上内核使用的是Groonga。可以这么理解,Mroonga 是Groonga 的MySQL 外挂包装版,本身作为一个FullText Index 的全兼容替代品,能兼容MySQL 的大部分操作。通过调整能完美取代LIKE 的模糊查询并且效能非常好。

优点:在MariaDB 10.0.15 版本后自带了Mroonga 引擎,且能轻松的安装进MySQL 与MariaDB 或者Percona 中。本身作为一个MySQL 外挂,甚至还能作为一种存储引擎,与MySQL 的结合非常紧密。使用了之后根本无需操心数据同步的问题。主要也是日本人负责开发与维护,所以对CJK 支持的很好,性能也同样很优秀。

缺点:如果不对搜寻语句进行调优的话,搜寻效能甚至不如MyISAM。

  • PostgreSQL

著名开源拖拉机,也是这一票Sphinx 派系中最能打的,但PGSQL 并不是一开始就是Sphinx 的成员,最开始的时候PGSQL 使用了Apache Lucene 内核作为全文搜寻引擎,但经过了几个版本后更换为类似于Sphinx 的内核。虽然他是个SQL,全文搜寻引擎只是个附属品,但如果条件允许的话直接使用PostgreSQL 以上问题你都不用考虑了。

优点:性能很好,稳定性也很好。CJK 的支持也有。

缺点:好像真的找不到什么缺点。


以上就是Sphinx 派系的常见成员。真正要我推荐的话我建议在MantiCore Search,Mroonga 与PostgreSQL 三者中进行考虑。由于MariaDB 10.0.15 以后的版本都自带了Mroonga 引擎,所以为了省事的话我建议直接使用MariaDB 10.0.15 以后的版本或者PostgreSQL。之后我会专门出文对MantiCore Search 与Mroonga 进行详解。


Apache Lucene 派系

Apache Lucene 实际上仅为一个Java 程式码中的一个Class,Apache Lucene 无法自行工作,都需要另行包装后才能使用。同时此Class 还有几个衍生品例如Lucene.NET 与Apache Lucy 的封装,能让Lucene 使用在.Net 与C 程式码中。但在这里我们只讨论两个使用Lucene 为内核的搜寻程式Solr 与Elasticsearch。但可以直接就在此告诉大家,Solr 与ElasticSearch 都无法与Sphinx Search 派系的产品类似与SQL 的结合非常紧密。所以数据的录入与同步是个需要详细考虑的问题。但也正由于这点,也让Solr 与ElasticSearch 不被局限于关系型SQL 的那一套理论中。

但由于,我本人是Java 程式码歧视主义者,所以我并不推荐此派系中的任何一个。除非他们使用其他程式码重构。

  • Apache Solr

优点: Solr 的优点应该说是长年来的企业应用带来的稳定性非常高。

缺点:由于使用了Java 进行开发,在系统资源占用上存在非常大的浪费。记忆体要求较高,建议给其分配32GB 的记忆体,但不要超过32GB。可扩展性与配置难度难度高于ElasticSearch。生态与ElasticSearch 相比也较差。

  • ElasticSearch

优点:社区生态应该是本文中的所有产品里最好的。同时分析功能完全,虽然原生对CJK 的支持较差但能通过加置各种外挂实现各种功能。RESTful API 的完整支持可以让你轻松的构建出一个API 伺服器。

缺点:还是由于使用了Java 进行开发,在系统资源占用上存在非常大的浪费。记忆体要求较高,建议给其分配32GB 的记忆体,但不用超过32GB,为什么呢?因为多了会造成CPU 效能严重下降。当然这问题并不是ES 的问题,归根到底都是JVM 的记忆体管理策略留下的疑难杂症。同时是优点也是缺点的RESTful API 支持,交互只能使用RESTful API,所以必须重新为ES 编写相应的程式码。如果不是新的专案的话,迁移至ES 的工作量实在有点大。再来,社区版本的ES 明确写明不要拿来作为本地存储SQL 使用,因为可靠性并不行。那非要使用的话怎么办?也不是没办法,请购买商业版本。最后,就是ES 最令人头疼的问题,如果你是同时使用SQL 与ES 的话,那SQL 与ES 的数据同步基本简单不了。以前有多种方案来实现SQL 与ES 的数据同步,但都有各种各样的问题,可靠性非常差。那么怎么办?我的建议是,在程式操作SQL 的同时,也对ES 进行同样的操作。


以上就是Apache Lucene 派系的两名大将。这个派系的共同点就是,与SQL 的关系基本没有。不管是Solr 亦或者是ElasticSearch 他们对数据存储的实现都类似于NoSQL,但远远没有要求其的可靠性。当然与SQL 关系不紧密固然也有好处,比如在做Data Analyzer 能想出更多简单的办法。虽然我对ES 也很有研究,但我并不会推荐各位一上来就考虑ES。除非是没有其他任何最优选项的时候,再来考虑ES。


总结

最开始我的调查分析目的就是要寻找一个能取代LIKE 模糊查询的方案,按照我们都喜欢减少工作量的做法来看,我肯定只会从SphinxSearch 派系中的产品进行选择。

但如果,你使用的SQL 为NoSQL 类型的话,同时你的产品又以API 为主的MicroServices 风格,那不妨可以考虑一下使用ElasticSearch。

然而如果你与我一样,使用的是关系型SQL 并且只是想寻找一个LIKE 模糊查询的取代方案,那我可以给你一个以上工具的性能排行大致为什么样:

MantiCore Search ≈ Sphinx Search ≈ PostgreSQL ≈ Senna > Groonga ≈ Mroonga > ElasticSearch > Solr > BigGram > FullText Index

当然,我个人比较推荐MantiCore Search 与PostgreSQL 和Mroonga,因为他们其实真的很棒。之后我会专门出文详解MantiCore Search 与Mroonga,为什么是这两个呢?因为他们两个的文真的太少了,不管什么语言地区对其有深入研究的文章几乎都看不到。至于ES 和PostgreSQL 的话,生态还挺好的,大家基本搜搜都能找到自己想要的内容。

 

https://deepskyfire.com/sub/13.html

 

posted @ 2022-08-31 16:28  kjcy8  阅读(786)  评论(0编辑  收藏  举报