
在 sqlite 中,`regexp`(需手动启用)与 `like '%word%'` 均可用于子串模糊匹配,但二者底层机制不同:`like '%word%'` 无法利用 b-tree 索引加速通配符前缀查询,而 `regexp` 默认完全不走索引;实测表明,**两者性能相近且普遍较慢,均不适合高频、大数据量的 `%word%` 全文模糊搜索**。
mattn/go-sqlite3 是 Go 语言中广泛使用的 SQLite 绑定库,它支持通过编译选项(如 -tags sqlite_regexp)或运行时注册自定义函数来启用 REGEXP。但需明确:SQLite 原生并不内置 REGEXP 运算符——它只是一个可选的扩展函数,其行为和性能完全取决于你提供的正则实现(例如基于 re2 或 regexp/syntax 的 Go 回调)。
以 LIKE '%word%' 为例,该模式因左侧通配符 % 导致 SQLite 无法使用索引进行范围跳转,必须执行全表扫描(full table scan),时间复杂度为 O(N×M)(N 为行数,M 为平均字符串长度)。而 REGEXP 'word'(无锚点)同样无法利用索引,且额外引入正则引擎的编译与匹配开销,在多数场景下实际性能略逊于 LIKE。
你可以参考 mattn/go-sqlite3 项目中的基准测试框架自行验证。例如,修改其 sqltest.go 中的 BenchmarkRows,添加对比逻辑:
func BenchmarkLikeSearch(b *testing.B) {
db.once.Do(makeBench)
for i := 0; i < b.N; i++ {
rows, err := db.Query("SELECT id FROM bench WHERE name LIKE ?", "%sqlite%")
if err != nil {
b.Fatal(err)
}
rows.Close()
}
}
func BenchmarkRegexpSearch(b *testing.B) {
db.once.Do(makeBench)
// 确保已注册 regexp 函数(例如 via sqlite3.RegisterFunc)
for i := 0; i < b.N; i++ {
rows, err := db.Query("SELECT id FROM bench WHERE name REGEXP ?", "sqlite")
if err != nil {
b.Fatal(err)
}
rows.Close()
}
}⚠️ 注意事项:
- REGEXP 在 mattn/go-sqlite3 中默认不启用,需显式注册回调函数(如 db.RegisterFunc("regexp", regexpFunc, true)),否则会报错 no such function: regexp;
- 正则匹配若在 Go 层实现(如调用 regexp.MatchString),每次匹配都会触发 Go runtime 调度和内存分配,显著增加延迟;
- 对于 '%word%' 类型的子串搜索,更优解是:
✅ 使用 FTS5(SQLite 全文检索扩展),支持高效前缀/分词/近义搜索;
✅ 预处理字段建立 n-gram 索引(如 CREATE VIRTUAL TABLE t_fts USING fts5(name));
✅ 或改用专用搜索引擎(如 Meilisearch、Sonic)处理高并发文本检索。
总之,不要寄希望于 REGEXP 替代 LIKE 来提升 %word% 性能——它们本质都是线性扫描。真正的优化应从数据模型和检索架构入手。










