一、全文检索的那些事儿

说到数据库搜索,大家最熟悉的肯定是WHERE column LIKE '%关键词%'这种操作。但当你数据量上了百万级别,这种查询就会慢得像老牛拉破车。这时候就该全文检索登场了——它就像给数据库装上了搜索引擎,能快速定位文本内容。

在MySQL中,全文检索主要有三种实现方式:

  1. 原生FULLTEXT索引
  2. 配合Sphinx搜索引擎
  3. 使用Elasticsearch等外部方案

我们先看个原生FULLTEXT的简单示例(技术栈:MySQL 8.0):

-- 创建带全文索引的表
CREATE TABLE articles (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(200),
    body TEXT,
    FULLTEXT (title, body)  -- 关键在这里!
) ENGINE=InnoDB;

-- 插入测试数据
INSERT INTO articles (title, body) VALUES
('MySQL优化技巧', '介绍了索引优化和查询优化方法'),
('全文检索实战', '详细讲解MySQL的全文检索功能');

-- 使用自然语言搜索
SELECT * FROM articles 
WHERE MATCH(title, body) AGAINST('优化方法' IN NATURAL LANGUAGE MODE);

这个例子展示了最基本的用法,但实际生产中会遇到各种幺蛾子,比如中文分词问题。MySQL的全文检索默认是按空格分词的,对中文很不友好,这时候就需要特殊处理。

二、三种方案的详细对比

1. 原生FULLTEXT方案

优点

  • 无需额外组件,开箱即用
  • 与MySQL深度集成,维护简单
  • 支持布尔搜索、相关性排序

缺点

  • 中文支持差(需要5.7+版本配合ngram分词)
  • 大数据量性能下降明显
  • 功能相对简单

中文解决方案示例

-- MySQL 5.7+ 中文全文检索配置
-- 先设置ngram分词长度(建议2-3)
SET GLOBAL ngram_token_size = 2;

-- 建表时需要指定解析器
CREATE TABLE chinese_articles (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    content TEXT,
    FULLTEXT (content) WITH PARSER ngram
) ENGINE=InnoDB;

-- 查询示例
SELECT *, MATCH(content) AGAINST('数据库' IN BOOLEAN MODE) as score 
FROM chinese_articles
ORDER BY score DESC;

2. Sphinx方案

这个俄罗斯人开发的搜索引擎专门为MySQL优化过,性能比原生方案强很多。

典型架构

MySQL数据 → Sphinx实时索引 → 应用查询

配置示例

# sphinx.conf 配置文件片段
source mysql_source {
    type = mysql
    sql_host = localhost
    sql_user = root
    sql_pass = 
    sql_db = test
    sql_query = SELECT id, title, content FROM articles
    sql_attr_string = title  # 作为属性存储
}

index mysql_index {
    source = mysql_source
    path = /var/data/sphinx/mysql_index
    charset_type = utf-8
    ngram_len = 2  # 中文分词
}

searchd {
    listen = 9312
}

优点

  • 查询性能极高(百万数据毫秒级响应)
  • 支持分布式
  • 灵活的分词策略

缺点

  • 需要单独维护索引(实时索引或定时重建)
  • 学习曲线较陡
  • 功能扩展需要开发插件

3. Elasticsearch方案

当数据量达到千万级别,专业的ES就是不二之选了。

典型集成方式

// Java示例:使用Spring Data Elasticsearch(技术栈:Java+ES7)
@Document(indexName = "article_index")
public class Article {
    @Id
    private Long id;
    
    @Field(type = FieldType.Text, analyzer = "ik_max_word") // 使用IK中文分词
    private String content;
    
    // getters/setters...
}

public interface ArticleRepository extends ElasticsearchRepository<Article, Long> {
    List<Article> findByContentContaining(String keyword);
}

// 使用示例
List<Article> results = articleRepository.findByContentContaining("分布式架构");

优点

  • 真正的专业级全文检索
  • 完善的中文分词支持
  • 强大的扩展性和高可用

缺点

  • 系统复杂度指数级上升
  • 资源消耗大
  • 需要专门运维

三、性能优化实战技巧

1. 索引优化

对于原生FULLTEXT,要注意:

  • 不要在所有文本字段上都建全文索引
  • 控制单个索引包含的字段数(建议不超过3个)
  • 大文本字段考虑只索引前N个字符
-- 优化后的建表语句示例
CREATE TABLE optimized_articles (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(200),
    summary VARCHAR(500),  -- 专门提取的摘要
    body TEXT,
    FULLTEXT (title, summary)  -- 只索引关键字段
) ENGINE=InnoDB;

2. 查询优化

  • 使用布尔模式提高精度
  • 合理设置最小词元长度
  • 避免在WHERE中混合全文检索和其他条件
-- 好的查询方式
SELECT * FROM articles 
WHERE MATCH(content) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE)  -- 必须包含MySQL,排除Oracle
AND publish_time > '2023-01-01';

-- 坏的查询方式(会导致全文索引失效)
SELECT * FROM articles 
WHERE MATCH(content) AGAINST('数据库') OR author = '王教授'; 

3. 混合方案设计

对于大型系统,可以采用分层策略:

高频热数据 → Elasticsearch  
低频冷数据 → MySQL FULLTEXT  

四、选型决策指南

应用场景分析

  1. 小型项目:直接用MySQL FULLTEXT,省时省力
  2. 中型Web应用:Sphinx + MySQL,平衡性能与复杂度
  3. 大型电商/内容平台:必须上Elasticsearch

避坑指南

  1. 中文环境一定要测试分词效果
  2. 注意事务一致性要求(ES是近实时)
  3. 评估索引重建的成本

未来趋势

虽然现在ES如日中天,但MySQL也在持续改进全文检索功能。8.0版本新增了invisible index降序索引等特性,配合ngram分词器已经能处理大多数中文场景。对于不想引入新组件的团队,可以持续关注MySQL的更新。

最后给个万能建议:先用最简单的方案实现需求,等真的遇到性能瓶颈再考虑升级架构。过早优化是万恶之源,这话在全文检索领域同样适用。