mysql like模糊查询如何优化

MySQL LIKE模糊查询优化是一个常见问题,主要原因是LIKE以通配符开头的查询无法使用B-Tree索引。以下是详细的优化方案:

一、索引优化方案

1. 前缀查询优化LIKE 'prefix%'

-- 可以使用索引
SELECT * FROM users WHERE name LIKE '张%';

-- 创建索引
CREATE INDEX idx_name ON users(name);

2. 后缀查询优化LIKE '%suffix'

-- 方案1:反转字符串+前缀查询
CREATE INDEX idx_name_reverse ON users(REVERSE(name));
SELECT * FROM users WHERE REVERSE(name) LIKE REVERSE('%明');

-- 方案2:使用虚拟列(MySQL 5.7+)
ALTER TABLE users 
ADD COLUMN name_reverse VARCHAR(255) AS (REVERSE(name)) STORED,
ADD INDEX idx_name_reverse(name_reverse);

3. 全模糊查询优化LIKE '%keyword%'

方案A:全文检索(MySQL 5.6+)

-- 创建全文索引
ALTER TABLE articles ADD FULLTEXT ft_content(content);

-- 使用MATCH AGAINST(布尔模式)
SELECT * FROM articles 
WHERE MATCH(content) AGAINST('+关键词' IN BOOLEAN MODE);

-- 自然语言模式
SELECT * FROM articles 
WHERE MATCH(content) AGAINST('关键词' IN NATURAL LANGUAGE MODE);

方案B:N-gram分词(中文场景)

-- 创建N-gram全文索引
CREATE FULLTEXT INDEX ft_content_ngram ON articles(content) 
WITH PARSER ngram;

-- 查询
SELECT * FROM articles 
WHERE MATCH(content) AGAINST('关键词' IN NATURAL LANGUAGE MODE);

二、架构层面优化

1. 使用搜索引擎

  • Elasticsearch:适合全文搜索场景
  • Apache Solr:企业级搜索解决方案
  • 阿里云OpenSearch:云原生搜索服务

2. 倒排索引方案

-- 创建关键词映射表
CREATE TABLE keyword_mapping (
    keyword VARCHAR(50),
    record_id INT,
    PRIMARY KEY(keyword, record_id)
);

-- 查询时先查映射表
SELECT DISTINCT m.record_id 
FROM keyword_mapping m 
WHERE m.keyword LIKE '用户%';

三、查询技巧优化

1. 减少扫描范围

-- 不好:全表扫描
SELECT * FROM large_table WHERE content LIKE '%keyword%';

-- 较好:先缩小范围
SELECT * FROM large_table 
WHERE create_time > '2023-01-01'  -- 利用时间索引
  AND content LIKE '%keyword%';    -- 再模糊查询

2. 使用覆盖索引

-- 创建覆盖索引
CREATE INDEX idx_covering ON users(name, email, phone);

-- 只查询索引列
SELECT name, email FROM users WHERE name LIKE '张%';

3. 分页优化

-- 低效
SELECT * FROM users WHERE name LIKE '%张%' LIMIT 1000, 20;

-- 高效:使用延迟关联
SELECT * FROM users u
JOIN (
    SELECT id FROM users 
    WHERE name LIKE '%张%'
    LIMIT 1000, 20
) AS tmp ON u.id = tmp.id;

四、特殊场景优化

1. 短文本精确匹配

-- 使用HASH列
ALTER TABLE products
ADD COLUMN name_hash BINARY(16) AS (UNHEX(MD5(name))) STORED,
ADD INDEX idx_name_hash(name_hash);

-- 查询
SELECT * FROM products 
WHERE name_hash = UNHEX(MD5('商品名称'))
  AND name LIKE '%商品名称%';  -- 防止哈希冲突

2. 枚举值查询

-- 不好
SELECT * FROM logs WHERE level LIKE '%ERROR%';

-- 好:使用固定值
SELECT * FROM logs WHERE level IN ('ERROR', 'FATAL_ERROR');

五、配置参数优化

-- 调整InnoDB缓冲池
SET GLOBAL innodb_buffer_pool_size = 大小;

-- 调整全文检索参数
ft_min_word_len = 1          -- 最小词长
innodb_ft_min_token_size = 1  -- InnoDB最小词长
ngram_token_size = 1         -- N-gram分词长度

六、最佳实践建议

  1. 设计阶段
    • 尽量避免全模糊查询需求
    • 明确区分前缀、后缀、全模糊场景
    • 合理设计数据表结构
  2. 索引策略
    • 前缀查询:使用普通B-Tree索引
    • 后缀查询:使用反转字符串索引
    • 全模糊:使用全文索引或搜索引擎
  3. 查询优化
    • 使用EXPLAIN分析执行计划
    • 避免在WHERE子句中对索引列使用函数
    • 合理使用覆盖索引
  4. 监控与调优 -- 监控慢查询 SHOW VARIABLES LIKE 'slow_query_log%'; -- 分析查询性能 EXPLAIN SELECT * FROM users WHERE name LIKE '%张%';

总结建议

场景推荐方案说明
前缀查询B-Tree索引最有效,直接利用索引
后缀查询反转列索引转换后缀为前缀查询
全模糊+文本搜索全文索引MySQL内置,支持中文用N-gram
复杂搜索需求Elasticsearch功能强大,支持分词、同义词等
实时性要求高内存数据库+索引Redis、Memcached缓存热点数据

选择原则:根据数据量、查询频率、实时性要求、开发成本综合选择。小数据量用MySQL优化即可,大数据量或复杂需求考虑专业搜索引擎。


作 者:南烛
链 接:https://www.itnotes.top/archives/883
来 源:IT笔记
文章版权归作者所有,转载请注明出处!


上一篇
下一篇