wip: 保存 Go 练习和技术笔记
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# squirrel 的坑
|
||||
|
||||
> `github.com/Masterminds/squirrel`,纯 SQL 查询构建器(query builder),2026-09 查证。
|
||||
|
||||
## 坑 1:Like 家族多 key 不排序,SQL 输出不确定(真雷)
|
||||
|
||||
`Eq` / `NotEq` / `Lt` / `Gt` 等 map 条件生成 SQL 前会对 key 做 `sort.Strings` 字典序排序(v1.1.0 起,`expr.go` 的 `getSortedKeys`),输出确定。
|
||||
|
||||
但 `Like` / `NotLike` / `ILike` / `NotILike` 的 `toSql` 是直接 `for key, val := range lk`——跟随 Go map 随机遍历序,**多 key 时每次生成的 SQL 条件顺序随机**:
|
||||
|
||||
```
|
||||
第1次: WHERE nickname LIKE ? AND mobile LIKE ? | args=[%张% %138%]
|
||||
第3次: WHERE mobile LIKE ? AND nickname LIKE ? | args=[%138% %张%] ← 顺序翻转
|
||||
```
|
||||
|
||||
### 后果
|
||||
|
||||
- 查询结果没错(AND 交换律),但 **SQL 字符串 + args 顺序在变**
|
||||
- 如果用「SQL 字符串 + 参数」生成缓存 key(hash 后当 Redis key 之类),同一查询会打出多个 key:
|
||||
- 命中率暴跌:n 个 Like 条件最多 n! 种排列 → n! 个缓存 key
|
||||
- 同一份数据存多份副本,TTL 内占内存
|
||||
- 依赖 SQL diff 的单测会随机失败
|
||||
|
||||
### 规避
|
||||
|
||||
- `Like` 只放**单个 key**
|
||||
- 多模糊条件拆成多次 `.Where(...)`(内部是 append 切片,天然保序)或用 `sq.And{...}` 显式控制顺序
|
||||
|
||||
### 上游状态
|
||||
|
||||
- 无任何 issue 报告过此事(全量搜索过,含 closed),作者从未回应
|
||||
- 当年修 Eq 排序的 PR #141 只改了 Eq/Lt 两处,紧邻的 Like 一行未动,讨论里也没人发现——纯遗漏,非有意设计(作者在 #135 明确说过支持排序)
|
||||
- 库近乎停滞:最新 release v1.5.4(2023-03),master 最后 commit 2024-02,**别指望上游修**
|
||||
|
||||
## 坑 2:Eq 排序按字典序,与联合索引列序无关(知道即可)
|
||||
|
||||
`Eq{"b":2, "a":1}` 永远生成 `WHERE a = ? AND b = ?`,与你书写的顺序无关(issue #321)。
|
||||
|
||||
对主流数据库**基本无影响**:MySQL / PostgreSQL / SQL Server / Oracle 的 CBO 都会重排谓词,索引选择看的是「条件覆盖了哪些列(最左前缀)、选择度、类型是否隐式转换」,不是 WHERE 里的书写顺序。只有对谓词顺序敏感的引擎/老优化器才会踩到。
|
||||
|
||||
## 顺带:这个库的亮点
|
||||
|
||||
- builder 是**值语义不可变**的(基于 lann/builder 持久化数据结构),每次链式调用返回新副本,可安全分叉复用,不像 GORM 那样共享 Statement、分叉前要 `Session()` 克隆
|
||||
- `Eq` 家族的确定性输出有源码保证(`expr.go` 排序逻辑),可放心用于缓存 key 场景
|
||||
- 子查询:`FromSelect(builder, alias)`,或任何 builder 作为 `Sqlizer` 嵌进 `Where/Having/Join`
|
||||
Reference in New Issue
Block a user