当前位置:首页 > 资讯列表 >Laravel 低代码表单查询性能调优:从键值存储迈向宽表读取模型

Laravel 低代码表单查询性能调优:从键值存储迈向宽表读取模型

发布时间:2026-06-30 10:00:46 分类:营销学堂

低代码表单很容易用到 K-V。字段是用户配出来的,今天叫手机号,明天叫收货地址,后天又多了一个附件。要是每改一次字段都跟着改表结构,系统很快就不好维护。

K-V 解决了字段动态变化的问题,但列表查询会变麻烦。数据量小的时候,CASE WHEN 转一下还能接受;数据多了,再叠上筛选、排序、模糊查询,SQL 就会越来越重。

K-V 继续做写入真源,查询侧单独建宽表读模型。

一、K-V 为什么会出现

动态表单里,字段变化是常态。常见存法是一张主表保存记录,一张明细表保存字段。

CREATE TABLE form_record (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    form_id BIGINT UNSIGNED NOT NULL,
    created_at DATETIME,
    updated_at DATETIME
);

CREATE TABLE form_record_field (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    record_id BIGINT UNSIGNED NOT NULL,
    field_key VARCHAR(100) NOT NULL,
    field_value TEXT NULL,
    KEY idx_record_key (record_id, field_key)
);
record_id   field_key   field_value
---------   ---------   -----------
1           name        张三
1           mobile      13800000001
1           status      paid

二、慢在哪里

列表页要的是一行一条数据,但 K-V 存出来是一行一个字段。所以查询时经常要先把字段横向展开。

SELECT
    r.id,
    MAX(CASE f.field_key WHEN 'name' THEN f.field_value ELSE '' END) AS name,
    MAX(CASE f.field_key WHEN 'mobile' THEN f.field_value ELSE '' END) AS mobile,
    MAX(CASE f.field_key WHEN 'status' THEN f.field_value ELSE '' END) AS status
FROM form_record r
LEFT JOIN form_record_field f ON f.record_id = r.id
WHERE r.form_id = 1001
GROUP BY r.id
HAVING status = 'paid';

真正麻烦的是再叠模糊查询:

HAVING name LIKE '%张%'
   AND mobile LIKE '%138%'

这时候不是某一个索引没建好,而是查询形态本身就比较吃力。

三、不要急着推翻 K-V

K-V 不是坏设计。它解决的是字段动态变化的问题。低代码表单里,这个问题绕不开。

直接把原始存储改成宽表,会遇到字段新增、字段改名、历史数据、多选附件兼容这些问题。更稳的做法是:K-V 继续作为写入真源,查询侧单独做读模型。

四、给表单建一张宽表

可以按表单维度拆宽表。比如表单 1001,单独有一张查询表:

form_1001_flat
CREATE TABLE form_1001_flat (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    record_id BIGINT UNSIGNED NOT NULL,
    form_id BIGINT UNSIGNED NOT NULL,
    created_at DATETIME,
    updated_at DATETIME,
    name LONGTEXT NULL,
    mobile LONGTEXT NULL,
    status LONGTEXT NULL,
    remark LONGTEXT NULL,
    UNIQUE KEY uk_record_id (record_id),
    KEY idx_created_at (created_at)
);

原来要聚合的查询,现在可以直接查列:

SELECT record_id, name, mobile, status
FROM form_1001_flat
WHERE status = 'paid'
  AND name LIKE '%张%'
ORDER BY created_at DESC;

五、写入和查询分开

写入时还是先进 K-V,然后异步同步到宽表。

form_record_field  ->  form_1001_flat
record_id   name   mobile        status
---------   ----   -----------   ------
1           张三   13800000001   paid

Laravel 里可以把同步放到队列里做。新增、修改、删除表单数据时,发一个轻量任务,把当前记录重新摊平成宽表行。

六、开关要留好

宽表读模型不要一上来就全量切。更稳的是按表单控制状态。

off       未开启
building 生成中
enabled  已启用
failed   失败
1. 创建宽表
2. 补齐字段列
3. 回填历史数据
4. 校验行数
5. 切换查询入口

如果校验不过,就继续走旧的 K-V 查询。读模型是为了提速,不应该影响原始数据安全。

七、字段改名怎么办

低代码里字段改名很常见。如果只靠 field_key,会分不清老字段改名,还是删除后新建了同名字段。

所以最好给字段一个稳定身份:

field_uid

字段名可以变,字段身份不变。宽表列名可以继续用当前字段名,但内部要知道它对应的是哪个字段身份。

八、最后

K-V 适合写,宽表适合查。低代码表单的数据模型,通常不能只选一个。

K-V:保存原始数据
宽表:服务列表查询
队列:负责同步和回填
开关:保证可以随时退回