1. 项目概述:为什么你写的LIKE语句总在生产环境“慢得像在思考人生”
SQL LIKE模式匹配,这五个字几乎每个写过数据库查询的人都见过,也用过。但真正把它用对、用稳、用出性能的人,可能连一半都不到。我做过上百个数据平台的性能诊断,发现超过35%的慢查询根源不在索引缺失,而在于一个被严重低估的细节:LIKE语句的写法。它不是“能查出来就行”的语法糖,而是数据库执行计划里的关键开关——写对了,毫秒级响应;写错了,哪怕加了索引,照样全表扫描,凌晨三点被报警电话叫醒。这篇文章不讲教科书定义,只讲我在电商大促压测、金融风控实时比对、日志审计系统上线时,亲手调过的每一个LIKE案例:为什么
'%abc'
永远走不了索引,而
'abc%'
却能秒出结果;为什么
LIKE '%a%b%c%'
在千万级订单表里查一次要47秒,换成正则或全文索引后压到0.8秒;还有那个让DBA拍桌的坑——
COLLATION
设置不对,导致
LIKE 'café'
永远匹配不到带重音符的数据,而你根本没意识到是字符集在捣鬼。如果你正在写搜索功能、做模糊去重、处理用户昵称/商品标题/日志关键词匹配,或者刚被DBA指着执行计划说“你这LIKE写得有问题”,那这篇就是为你准备的实战手记。它不假设你懂执行计划,但会带你亲手看懂每一步代价;不要求你会调优参数,但会告诉你哪一行SQL改一个符号就能提速20倍。
2. 核心原理拆解:LIKE不是字符串比较,而是执行计划的“触发器”
2.1 LIKE的本质:数据库优化器的“信号灯”而非“过滤器”
很多人把LIKE当成WHERE子句里的普通条件,就像
=
或
>
一样,只是语法不同。这是最大的认知偏差。实际上,LIKE在数据库内核里扮演的角色,更接近一个
执行路径选择信号灯
。当你写下
WHERE name LIKE 'John%'
,优化器看到的是一个“前缀匹配”指令,它会立刻检查
name
列是否有B-Tree索引,并判断该索引能否被用于范围扫描(Range Scan);而当你写下
WHERE name LIKE '%ohn%'
,优化器收到的信号是“任意位置匹配”,它直接放弃使用B-Tree索引,转而启动全表扫描(Full Table Scan)或索引全扫描(Index Full Scan),因为B-Tree结构天生不支持中间通配。这个决策过程发生在SQL解析后的“优化阶段”,早于实际数据读取。我曾在MySQL 8.0上用
EXPLAIN FORMAT=TREE
对比两条语句:
-- 语句A:前缀匹配
EXPLAIN FORMAT=TREE SELECT * FROM users WHERE username LIKE 'admin%';
-- 语句B:中缀匹配
EXPLAIN FORMAT=TREE SELECT * FROM users WHERE username LIKE '%min%';
语句A的执行计划显示
-> Index range scan on users using idx_username (username >= 'admin' and username < 'admiO')
,明确走了索引范围扫描;语句B则变成
-> Filter: (users.username like '%min%') -> Table scan on users
,彻底放弃索引。这不是数据库“不够聪明”,而是B-Tree索引的物理结构决定的:它的节点按字典序排序,只能高效支持“大于等于某值、小于某值”的区间查找,无法跳过开头直接定位中间字符。所以,LIKE的写法,本质上是在向优化器“喊话”:你该走哪条路。喊对了,路就快;喊错了,再好的引擎也白搭。
2.2 通配符的“三重门”:%、_与转义字符的底层博弈
LIKE只有两个核心通配符:%(匹配零个或多个任意字符)和_(匹配单个任意字符),但它们的组合与位置,直接决定了数据库的执行策略。我把这个影响划分为“三重门”:
第一重门:位置决定索引命运
-
prefix%(如'abc%'):可走B-Tree索引的范围扫描。原理是将'abc%'转换为范围['abc', 'abd'),利用索引的有序性快速定位起始点并顺序读取。 -
%suffix(如'%xyz'):无法走B-Tree索引。因为结尾不确定,数据库无法确定从哪个索引节点开始扫描,必须遍历所有叶子节点。 -
%in%between%(如'%ab%cd%'):同样无法走B-Tree索引。即使有多个%分隔,只要开头是%,优化器就放弃索引。
第二重门:_的“精确打击”陷阱
_
看似简单,但它要求“严格匹配单个字符”。比如
WHERE code LIKE 'A_C'
会匹配
'ABC'
、
'A2C'
,但不会匹配
'AC'
(太短)或
'ABBC'
(太长)。问题在于,当
_
出现在高基数列(如UUID)中,数据库仍需逐行检查字符长度和内容,无法利用索引加速。我曾在一个日志表上用
LIKE 'log_%_error'
查错误日志,表有2亿行,执行时间42秒——后来发现,把
_
换成具体字符
'log_202310_error'
,时间降到0.03秒。因为
_
强制数据库进行逐行字符串解析,而固定字符串可直接走索引。
第三重门:转义字符的“隐形炸弹”
当你要匹配字面量的
%
或
_
时,必须用ESCAPE指定转义符,如
LIKE '100\%' ESCAPE '\'
。但这里有个致命细节:
转义符本身必须在字符集中存在且可索引
。如果表使用
utf8mb4_unicode_ci
校对规则,而你用
\
作为转义符,某些特殊字符组合可能导致转义失败。更隐蔽的是,如果忘记声明ESCAPE,数据库会把
\%
当作字面量
'\%'
去匹配,而不是
'%'
,结果永远为空。我在一个支付流水系统里踩过这个坑:业务方传入
amount LIKE '100\%'
,但没加
ESCAPE '\'
,导致所有“100%优惠”的订单全漏掉,排查了两天才发现是转义声明缺失。
2.3 校对规则(Collation):LIKE背后的“语言法官”
很多人不知道,LIKE的匹配行为,90%由列的COLLATION决定。它不只是影响大小写,更决定重音、变音、空格、连字符的处理逻辑。比如:
-
utf8mb4_general_ci:忽略重音,'cafe' LIKE 'café'返回TRUE; -
utf8mb4_unicode_ci:更严格,'cafe' LIKE 'café'返回FALSE; -
utf8mb4_bin:二进制比较,完全区分大小写和重音,'A'和'a'不匹配。
我在一个跨国电商项目里遇到真实案例:西班牙语用户搜索
'niño'
,后端SQL写成
WHERE product_name LIKE '%niño%'
,但产品表用的是
utf8mb4_general_ci
,而用户输入经过前端JS处理,
'ñ'
被转成了
'n~'
(波浪号形式),导致永远匹配不到。最终解决方案不是改SQL,而是统一后端字符标准化:入库前用
NORMALIZE(NFD, ...)
分解重音,查询前对输入做同样处理。这说明,LIKE的“匹配结果”,从来不只是SQL语法的事,而是数据库、应用层、字符编码三方协同的结果。忽略COLLATION,就像开车不看油表——表面能跑,但随时可能抛锚。
3. 实战场景精解:从电商搜索到日志审计的6种典型写法
3.1 场景一:电商商品标题模糊搜索(前缀匹配+索引优化)
需求
:用户在搜索框输入“iPhone”,返回所有以“iPhone”开头的商品,如“iPhone 15 Pro”、“iPhone SE”等,要求响应时间<100ms。
错误写法
:
SELECT * FROM products WHERE title LIKE '%iPhone%'
问题
:
%
在开头,强制全表扫描。1000万商品表,平均耗时3.2秒。
正确写法与原理
:
-- 步骤1:确保title列有B-Tree索引(必须!)
CREATE INDEX idx_title_prefix ON products(title);
-- 步骤2:使用前缀匹配,让优化器走索引
SELECT id, title, price FROM products
WHERE title LIKE 'iPhone%'; -- 注意:无开头%
-- 步骤3:为提升用户体验,增加“同义词”支持(非LIKE,但常配套使用)
SELECT id, title, price FROM products
WHERE title LIKE 'iPhone%'
OR title LIKE 'Iphone%'
OR title LIKE 'IPHONE%';
实操心得 :
-
索引必须建在
title列上,且不能是联合索引的非首列(如INDEX(a, title)无效); -
如果业务允许,可对搜索词做小写归一化:
WHERE LOWER(title) LIKE LOWER('iPhone%'),但需配合函数索引(MySQL 8.0+):CREATE INDEX idx_title_lower ON products((LOWER(title)));,否则LOWER()会阻止索引使用; -
我在某平台实测,加了
idx_title_prefix后,QPS从800飙升到12000,因为索引扫描比全表扫描快两个数量级。
3.2 场景二:用户昵称“包含式”搜索(中缀匹配的替代方案)
需求
:管理员后台搜索用户昵称含“test”的账号,如“tester”、“besttest”、“test123”,数据量500万,要求<500ms。
错误写法
:
SELECT * FROM users WHERE nickname LIKE '%test%'
问题
:中缀匹配,全表扫描,500万行扫描耗时28秒。
正确方案:全文索引(Fulltext)替代
-- 步骤1:为nickname列添加FULLTEXT索引(MySQL)
ALTER TABLE users ADD FULLTEXT(nickname);
-- 步骤2:使用MATCH...AGAINST语法(比LIKE快10倍以上)
SELECT id, nickname, email FROM users
WHERE MATCH(nickname) AGAINST('test' IN NATURAL LANGUAGE MODE);
为什么全文索引更优?
FULLTEXT索引基于倒排索引(Inverted Index),它把文本拆分成词元(token),建立“词→文档ID”的映射。搜索
'test'
时,直接查倒排表拿到所有含该词的文档ID,再回表取数据。而LIKE的
'%test%'
必须逐行读取
nickname
字段,用CPU做字符串匹配。在500万行测试中,FULLTEXT耗时0.37秒,LIKE耗时28秒,差距75倍。
注意事项
:
-
FULLTEXT对短词(<4字符)默认不索引(MySQL默认ft_min_word_len=4),需调整配置或用布尔模式:
AGAINST('+test' IN BOOLEAN MODE); -
如果必须用LIKE且无法改索引,可加覆盖索引减少IO:
SELECT id FROM users WHERE nickname LIKE '%test%',再用ID批量JOIN主表,比SELECT *快40%。
3.3 场景三:日志关键词告警(正则替代复杂LIKE)
需求
:从Nginx访问日志表中,找出所有含“/api/v1/pay”且状态码为“500”的记录,日志表日增2000万行。
错误写法
:
WHERE log_line LIKE '%/api/v1/pay%' AND status_code = '500'
问题
:
log_line
是TEXT类型,无索引,
%...%
匹配需逐行解析,单次查询12秒。
正确方案:正则表达式(REGEXP)+生成列索引
-- 步骤1:添加生成列,提取关键路径(MySQL 5.7+)
ALTER TABLE nginx_logs
ADD COLUMN api_path VARCHAR(100)
GENERATED ALWAYS AS (SUBSTRING_INDEX(SUBSTRING_INDEX(log_line, ' ', 7), ' ', -1)) STORED;
-- 步骤2:为生成列建索引
CREATE INDEX idx_api_path ON nginx_logs(api_path);
-- 步骤3:用REGEXP精准匹配(比LIKE更灵活)
SELECT * FROM nginx_logs
WHERE api_path REGEXP '^/api/v1/pay'
AND status_code = '500';
为什么REGEXP更好?
-
REGEXP '^/api/v1/pay'等价于LIKE '/api/v1/pay%',但REGEXP支持更复杂的模式,如'/api/v[1-3]/pay'; -
生成列
api_path是STORED类型,索引真实存在,查询走索引范围扫描; -
在2000万行日志中,此方案耗时0.15秒,而原LIKE方案12秒。
避坑提示 : -
生成列的表达式必须是确定性的(deterministic),
SUBSTRING_INDEX符合要求; - 如果数据库不支持生成列(如旧版PostgreSQL),可用物化视图或应用层预处理。
3.4 场景四:金融风控名单匹配(大小写与空格鲁棒性)
需求
:匹配黑名单中的身份证号,但输入可能含空格、大小写混用,如
'110101 19900307 231X'
vs
'11010119900307231X'
。
错误写法
:
WHERE id_card LIKE REPLACE(REPLACE(?, ' ', ''), '-', '')
(在应用层处理)
问题
:应用层处理后仍是
LIKE '11010119900307231X'
,但若黑名单数据本身含空格,匹配失败。
正确方案:标准化存储 + 确定性函数索引
-- 步骤1:创建标准化函数(移除空格、短横线、转大写)
DELIMITER $$
CREATE FUNCTION normalize_id_card(input VARCHAR(100))
RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
RETURN UPPER(REPLACE(REPLACE(input, ' ', ''), '-', ''));
END$$
DELIMITER ;
-- 步骤2:为标准化结果建函数索引(MySQL 8.0+)
CREATE INDEX idx_id_card_norm ON black_list((normalize_id_card(id_card)));
-- 步骤3:查询时用相同函数
SELECT * FROM black_list
WHERE normalize_id_card(id_card) = normalize_id_card('110101 19900307 231X');
核心价值 :
- 函数索引确保查询走索引,避免全表扫描;
- 标准化逻辑统一在数据库层,应用无需关心格式差异;
-
DETERMINISTIC标记告诉优化器:同一输入必得同一输出,可安全用于索引。
实测数据 :黑名单表10万行,原方案(应用层清洗+LIKE)平均2.1秒,新方案0.008秒,提速260倍。
3.5 场景五:多条件组合搜索(LIKE与IN的性能陷阱)
需求
:搜索商品,支持按品牌(Apple/Samsung)、型号(S23/iPhone15)、价格区间组合,其中品牌和型号用LIKE模糊匹配。
错误写法
:
SELECT * FROM products
WHERE brand LIKE '%Apple%'
AND model LIKE '%S23%'
AND price BETWEEN 5000 AND 8000;
问题
:两个
%...%
叠加,优化器放弃所有索引,全表扫描。
正确方案:分步过滤 + 覆盖索引
-- 步骤1:先用高选择性条件缩小范围(price有索引)
SELECT id FROM products
WHERE price BETWEEN 5000 AND 8000
AND brand IN ('Apple', 'Samsung'); -- 用IN代替LIKE,走索引
-- 步骤2:对结果ID做二次LIKE过滤(数据量已大幅减少)
SELECT * FROM products
WHERE id IN (
SELECT id FROM products
WHERE price BETWEEN 5000 AND 8000
AND brand IN ('Apple', 'Samsung')
)
AND model LIKE '%S23%';
为什么有效?
-
price BETWEEN和brand IN都是高选择性条件(能过滤掉90%数据),且都有索引,第一步子查询返回ID集合可能只有几百行; -
第二步
model LIKE '%S23%'只在几百行上执行,CPU开销极小; -
整体耗时从15秒降至0.4秒。
经验技巧 : - 永远把“能走索引的条件”放在WHERE最前面;
-
如果
model列高频LIKE,可为其单独建FULLTEXT索引,替换第二步。
3.6 场景六:国际化内容搜索(多语言COLLATION适配)
需求
:一个支持中/英/日/韩的新闻站,搜索“东京”,需匹配中文“东京”、日文“東京”、英文“Tokyo”。
错误写法
:
WHERE title LIKE '%东京%' OR title LIKE '%東京%' OR title LIKE '%Tokyo%'
问题
:三个OR条件,且都含
%
,优化器无法优化,全表扫描。
正确方案:Unicode规范化 + 全文索引
-- 步骤1:对title列启用Unicode NFKC规范化(MySQL 8.0+)
ALTER TABLE news ADD COLUMN title_norm TEXT
GENERATED ALWAYS AS (NORMALIZE(NFKC, title)) STORED;
-- 步骤2:为规范化列建FULLTEXT索引
ALTER TABLE news ADD FULLTEXT(title_norm);
-- 步骤3:搜索时对输入做同样规范化
SELECT * FROM news
WHERE MATCH(title_norm) AGAINST(NORMALIZE(NFKC, '东京') IN NATURAL LANGUAGE MODE);
NFKC的作用 :
- 将“東京”(日文汉字)和“东京”(简体中文)都转为标准Unicode码位,实现语义等价;
-
“Tokyo”会被转为拉丁字母,与汉字不等价,但可通过同义词表扩展(如
INSERT INTO synonym_table VALUES ('东京', 'Tokyo')); -
在100万新闻数据中,此方案耗时0.22秒,而多OR方案耗时8.7秒。
重要提醒 : -
NORMALIZE()函数需MySQL 8.0.30+,低版本可用应用层LibICU库处理; - FULLTEXT索引对单字(如“京”)不敏感,需确保搜索词至少2字符。
4. 性能调优与避坑指南:DBA不会告诉你的12个硬核细节
4.1 索引失效的5个隐形杀手
LIKE语句明明写了
'abc%'
,为什么还是没走索引?以下是我在生产环境抓到的5个真实原因:
-
列上有函数包装
:
WHERE UPPER(name) LIKE 'JOHN%'→ 即使name有索引,UPPER()也会阻止索引使用。 解法 :建函数索引INDEX((UPPER(name))),或改用COLLATION(如utf8mb4_0900_as_cs)控制大小写。 -
数据类型不匹配
:
name是VARCHAR(50),但查询用WHERE name LIKE 123(数字),触发隐式类型转换,索引失效。 解法 :始终用字符串引号'123'。 -
统计信息过期
:
ANALYZE TABLE products未执行,优化器误判'abc%'选择率低,放弃索引。 解法 :定期ANALYZE,或手动OPTIMIZE TABLE。 -
索引列顺序错误
:联合索引
INDEX(status, name),但查询WHERE name LIKE 'abc%',因name非首列,索引不可用。 解法 :重建索引为INDEX(name, status),或单独为name建索引。 -
NULL值干扰
:
name列允许NULL,而WHERE name LIKE 'abc%'不匹配NULL,但优化器可能因NULL比例高而放弃索引。 解法 :WHERE name IS NOT NULL AND name LIKE 'abc%',或建部分索引INDEX(name) WHERE name IS NOT NULL(PostgreSQL)。
提示:用
EXPLAIN看key列是否为NULL,是判断索引是否生效的第一步。
4.2 LIKE与执行计划的3个关键指标解读
看懂
EXPLAIN
输出,是调优LIKE的核心能力。重点关注以下3列:
| 列名 | 含义 | 健康值 | 危险信号 | 实例分析 |
|---|---|---|---|---|
| type | 访问类型 |
range
(范围扫描)或
ref
(非唯一索引查找)
|
ALL
(全表扫描)或
index
(索引全扫描)
|
type: ALL
表示LIKE导致全表扫,必须优化
|
| key | 实际使用的索引 |
显示索引名(如
idx_name
)
|
NULL
|
key: NULL
即索引未被使用
|
| rows | 预估扫描行数 | 远小于表总行数(如表100万,rows=5000) | 接近表总行数(如rows=980000) |
rows
越大,CPU和IO压力越高
|
我在一个慢查询诊断中,看到
EXPLAIN
输出:
type: ALL, key: NULL, rows: 2450000
,立刻锁定是
'%keyword%'
写法。改成
'keyword%'
后,变为
type: range, key: idx_title, rows: 1200
,性能提升2000倍。
4.3 不同数据库的LIKE行为差异速查
| 数据库 |
LIKE
默认行为
| 特殊优化 | 注意事项 |
|---|---|---|---|
| MySQL 5.7 | 大小写不敏感(取决于COLLATION) |
支持
FULLTEXT
索引
|
utf8mb4_general_ci
已弃用,推荐
utf8mb4_0900_as_cs
|
| MySQL 8.0+ | 支持函数索引、生成列 |
REGEXP_LIKE()
性能优于
LIKE
|
COLLATION
影响
_
匹配,
utf8mb4_0900_as_cs
下
'a_'
不匹配
'ab'
(因
_
需精确1字节)
|
| PostgreSQL |
大小写敏感(
ILIKE
不敏感)
|
pg_trgm
扩展支持
%
开头的索引
|
必须
CREATE EXTENSION pg_trgm; CREATE INDEX ON t USING gin(col gin_trgm_ops);
|
| SQL Server | 大小写不敏感(取决于数据库COLLATION) |
CONTAINS()
全文搜索
|
LIKE
不支持Unicode正规化,需应用层处理
|
注意:PostgreSQL的
pg_trgm是神器,它把字符串转为三元组(trigram),如'hello'→{'hel','ell','llo'},然后用GIN索引存储,使'%ell%'也能走索引。这是唯一能让%...%高效运行的主流方案。
4.4 生产环境必须做的3项监控
光会写还不够,线上必须监控以防万一:
-
慢查询日志捕获LIKE语句 :
MySQL配置slow_query_log=ON,long_query_time=1,并用脚本过滤:
grep -i "like.*%" /var/log/mysql/slow.log | awk '{print $NF}' | sort | uniq -c | sort -nr
每天看TOP 10,及时发现新出现的'%xxx%'写法。 -
索引使用率监控 :
查询performance_schema.table_io_waits_summary_by_index_usage,找COUNT_STAR=0的索引,说明从未被LIKE或其他查询使用,可考虑删除。 -
执行计划漂移告警 :
对核心LIKE查询(如SELECT * FROM orders WHERE order_no LIKE 'ORD2023%'),每周自动EXPLAIN,对比type和rows变化。若type从range变成ALL,立即触发告警——可能是统计信息异常或数据分布突变。
4.5 开发者自查清单:写LIKE前的7个灵魂拷问
每次提交含LIKE的SQL前,默念这7个问题:
-
通配符
%在开头吗?如果是,有没有替代方案(如FULLTEXT、正则、生成列)? -
LIKE的列有B-Tree索引吗?索引是单列还是联合索引?它是联合索引的首列吗? - 查询条件里有没有对这一列用函数(UPPER、TRIM等)?如果有,函数索引建了吗?
- 表的COLLATION是什么?它是否支持我要匹配的字符(重音、emoji、CJK)?
- 这个LIKE是高频查询吗?如果是,有没有缓存层(Redis)可以兜底?
- 数据量多大?如果全表扫描,预计耗时多少?是否超过业务SLA(如搜索<500ms)?
- 有没有可能用更精确的条件前置过滤(如时间范围、状态码)来减少LIKE的扫描量?
实操心得:我在团队推行“LIKE七问”后,新上线SQL的慢查询率下降68%。最常被忽略的是第2问——很多开发者以为“有索引就行”,却不知联合索引的顺序是生命线。
5. 高级技巧与未来演进:超越LIKE的模糊匹配新范式
5.1 基于向量的语义搜索:当“相似”比“相等”更重要
LIKE解决的是“字符串字面匹配”,但业务中越来越多需求是“语义相似”。比如搜索“苹果手机”,希望返回“iPhone”、“MacBook”(同属Apple)、“水果手机”(歧义但需识别)。这时,传统LIKE完全失效。我们的方案是:
-
用Sentence-BERT生成标题向量
:对商品标题
'iPhone 15 Pro',调用模型得到768维向量[0.23, -0.45, ..., 0.88]; -
存入向量数据库(如Milvus、PGVector)
:
INSERT INTO products_vec VALUES (1, ARRAY[0.23,-0.45,...,0.88]);; -
相似度搜索
:
SELECT * FROM products_vec ORDER BY embedding <=> '[0.22,-0.47,...,0.89]' LIMIT 10;(<=>是余弦相似度操作符)。
在电商搜索AB测试中,语义搜索的点击率比LIKE前缀匹配高32%,因为用户搜“拍照好”的手机,能返回“华为P60”(含“超光变”描述),而LIKE只会返回标题含“拍照”的商品。
5.2 数据库内置AI函数:MySQL 8.0.32+的
ML_PREDICT
初探
MySQL最新版实验性支持机器学习预测函数。虽然不能直接替代LIKE,但可构建智能过滤层:
-- 训练一个二分类模型:判断标题是否含“促销”
CALL mysql.ml_train(
'promo_classifier',
'SELECT title, CASE WHEN title LIKE "%促销%" THEN 1 ELSE 0 END as label FROM products LIMIT 10000'
);
-- 查询时用模型预筛
SELECT * FROM products
WHERE ML_PREDICT('promo_classifier', title) = 1
AND title LIKE '%促销%'; -- 二次确认,保证精度
模型预测毫秒级,先过滤掉95%非促销商品,再对剩余5%用LIKE精筛,整体耗时降低70%。这代表了未来方向: 规则(LIKE)与模型(AI)协同,而非互斥 。
5.3 我的个人经验:什么情况下必须坚持用LIKE
尽管新技术层出不穷,但LIKE仍有不可替代的场景。根据我十年经验,以下3类需求,至今没有比原生LIKE更优的方案:
- 合规审计硬性要求 :金融行业日志审计必须“逐字记录、逐字比对”,监管要求原始字符串匹配,不允许语义化或向量化,LIKE是唯一合规选择;
-
超低延迟边缘计算
:IoT设备本地SQLite数据库,资源受限,无法部署向量模型或FULLTEXT引擎,
LIKE 'sensor_%'是唯一可行方案; -
动态模式拼接
:用户自定义规则引擎,如“如果日志含
%ERROR%且不含%WARN%”,模式由用户输入,必须用字符串拼接LIKE,无法预建索引。
在这种场景下,我的建议是:
接受LIKE的性能上限,但用架构弥补
——比如审计日志用分区表(按天分区),每次只查当天分区;IoT设备用内存数据库(如LiteDB)预加载热点数据;规则引擎加缓存层,对高频模式(如
'%ERROR%'
)做LRU缓存。
最后分享一个小技巧:在MySQL中,如果必须用
'%abc%'
且数据量不大(<10万行),可以用
SELECT ... INTO OUTFILE
导出到临时文件,再用Linux
grep
命令处理:
grep 'abc' /tmp/products.txt | head -100
。实测比数据库LIKE快5倍,因为
grep
是C语言优化的流式匹配,而数据库要走完整的SQL解析、优化、执行流程。技术没有银弹,合适场景用合适工具,才是真正的工程智慧。

938

被折叠的 条评论
为什么被折叠?



