You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化解析JSON数据的MySQL查询及聚合表性能

批量插入场景下物化视图(带触发器表)的性能优化方案

问题背景

我有一张Analytics表,结构如下:

+---------+----------+----------------+------------+---------------------+-------+
| user_id | label_id | application_id | session_id | date                | score |
+---------+----------+----------------+------------+---------------------+-------+
|       1 |      123 |              1 | ABCD123456 | 2023-06-15 09:05:18 |   123 |
|       1 |      456 |              1 | ABCD123456 | 2023-06-15 09:05:18 |   456 |
|       1 |      789 |              1 | ABCD123456 | 2023-06-15 09:05:18 |   789 |
+---------+----------+----------------+------------+---------------------+-------+

score列类型为varchar,开发中途改为存储JSON数据。为提升报表生成速度,我创建了类物化视图的表WeldingSessionsAgregated(通过触发器维护),报表速度提升10倍以上,但批量插入会话时该表操作极耗时,仅处理150条会话就导致CPU占用100%,需优化代码适配高并发场景。


优化方案

1. 添加关键索引,消除全表扫描

当前Analytics表缺少查询高频的联合索引,大量按session_id+label_id的查询会走全表扫描,直接拖慢性能。

-- 给Analytics添加会话+标签的联合索引
ALTER TABLE Analytics ADD INDEX idx_session_label (session_id, label_id);
-- 给聚合表的会话ID加主键,加速更新/删除操作
ALTER TABLE WeldingSessionsAgregated ADD PRIMARY KEY (s_id);

2. 重写求和函数,避免循环查询

原get_welding_array_sum函数每次循环都查询一次数据库,性能极低。改用MySQL原生JSON_TABLE函数一次性解析数组并求和:

DROP FUNCTION IF EXISTS get_welding_array_sum;
CREATE FUNCTION get_welding_array_sum(sid VARCHAR(256), array_name VARCHAR(256))
RETURNS DECIMAL(10,2)
BEGIN
    DECLARE result DECIMAL(10,2);
    SELECT COALESCE(SUM(val), 0.0) INTO result
    FROM Analytics a,
         JSON_TABLE(
             a.score,
             CONCAT('$.', array_name, '[*]') COLUMNS(val DECIMAL(10,2) PATH '$')
         ) jt
    WHERE a.label_id = 127 AND a.session_id = sid;
    RETURN result;
END;

3. 简化触发器逻辑,减少重复查询

原触发器拆分多个UPDATE语句且重复查询相同数据,合并逻辑并减少查询次数:

DROP TRIGGER IF EXISTS WeldingSessionsAgregatedInsertTrigger;
CREATE TRIGGER WeldingSessionsAgregatedInsertTrigger AFTER INSERT ON Analytics
FOR EACH ROW
BEGIN
    DECLARE existing_count INT;
    DECLARE org_id INT;
    DECLARE is_sealing INT;
    
    IF NEW.application_id = 7 THEN
        -- 一次性查询是否为sealing会话
        SELECT COUNT(*) INTO is_sealing FROM Analytics WHERE label_id = 134 AND session_id = NEW.session_id AND score = "sealing";
        
        IF is_sealing > 0 THEN
            DELETE FROM WeldingSessionsAgregated WHERE s_id = NEW.session_id;
        ELSE
            -- 查询聚合表中是否已存在该会话
            SELECT COUNT(*) INTO existing_count FROM WeldingSessionsAgregated WHERE s_id = NEW.session_id;
            
            IF existing_count = 0 THEN
                -- 一次性获取组织ID
                SELECT organization_id INTO org_id FROM Users WHERE id = NEW.user_id;
                INSERT INTO WeldingSessionsAgregated (
                    s_id, user_id, date, application_id, organization_id
                ) VALUES (
                    NEW.session_id, NEW.user_id, NEW.date, NEW.application_id, org_id
                );
            END IF;
            
            -- 用CASE合并UPDATE操作,避免多次触发更新
            CASE NEW.label_id
                WHEN 125 THEN UPDATE WeldingSessionsAgregated SET session_start = NEW.score WHERE s_id = NEW.session_id;
                WHEN 126 THEN UPDATE WeldingSessionsAgregated SET session_end = NEW.score WHERE s_id = NEW.session_id;
                WHEN 129 THEN UPDATE WeldingSessionsAgregated SET part_name = NEW.score WHERE s_id = NEW.session_id;
                WHEN 132 THEN UPDATE WeldingSessionsAgregated SET displacement_map = NEW.score WHERE s_id = NEW.session_id;
                WHEN 133 THEN UPDATE WeldingSessionsAgregated SET weld_preview = NEW.score WHERE s_id = NEW.session_id;
                WHEN 135 THEN UPDATE WeldingSessionsAgregated SET method = NEW.score WHERE s_id = NEW.session_id;
                WHEN 136 THEN UPDATE WeldingSessionsAgregated SET material_used = NEW.score WHERE s_id = NEW.session_id;
                WHEN 127 THEN
                    -- 一次性计算所有聚合值,减少函数调用次数
                    UPDATE WeldingSessionsAgregated
                    SET
                        speed_sum = get_welding_array_sum(NEW.session_id, "speedEntries"),
                        travel_angle_sum = get_welding_array_sum(NEW.session_id, "travelAngleEntries"),
                        work_angle_sum = get_welding_array_sum(NEW.session_id, "workAngleEntries"),
                        distance_sum = get_welding_array_sum(NEW.session_id, "distanceEntries"),
                        count = JSON_LENGTH(NEW.score, '$.speedEntries'),
                        avg_speed = get_welding_array_sum(NEW.session_id, "speedEntries") / (JSON_LENGTH(NEW.score, '$.speedEntries') + 0.00001),
                        avg_travel_angle = get_welding_array_sum(NEW.session_id, "travelAngleEntries") / (JSON_LENGTH(NEW.score, '$.travelAngleEntries') + 0.00001),
                        avg_work_angle = get_welding_array_sum(NEW.session_id, "workAngleEntries") / (JSON_LENGTH(NEW.score, '$.workAngleEntries') + 0.00001),
                        avg_distance = get_welding_array_sum(NEW.session_id, "distanceEntries") / (JSON_LENGTH(NEW.score, '$.distanceEntries') + 0.00001)
                    WHERE s_id = NEW.session_id;
            END CASE;
        END IF;
    END IF;
END; //

4. 重建物化视图时使用批量聚合查询

原重建表语句使用大量子查询,效率极低。改用GROUP BY聚合查询一次性生成数据:

DROP TABLE IF EXISTS WeldingSessionsAgregated;
CREATE TABLE WeldingSessionsAgregated AS
SELECT
    a.session_id AS s_id,
    a.user_id,
    a.date,
    a.application_id,
    MAX(CASE WHEN a.label_id = 125 THEN a.score END) AS session_start,
    MAX(CASE WHEN a.label_id = 126 THEN a.score END) AS session_end,
    MAX(CASE WHEN a.label_id = 129 THEN a.score END) AS part_name,
    MAX(CASE WHEN a.label_id = 132 THEN a.score END) AS displacement_map,
    MAX(CASE WHEN a.label_id = 133 THEN a.score END) AS weld_preview,
    MAX(CASE WHEN a.label_id = 135 THEN a.score END) AS method,
    MAX(CASE WHEN a.label_id = 136 THEN a.score END) AS material_used,
    u.organization_id,
    -- 直接计算求和值,避免调用函数
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.speedEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt),
        0.0
    ) AS speed_sum,
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.travelAngleEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt),
        0.0
    ) AS travel_angle_sum,
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.workAngleEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt),
        0.0
    ) AS work_angle_sum,
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.distanceEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt),
        0.0
    ) AS distance_sum,
    JSON_LENGTH(aa.score, '$.speedEntries') AS count,
    -- 计算平均值
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.speedEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt) / (JSON_LENGTH(aa.score, '$.speedEntries') + 0.00001),
        0.0
    ) AS avg_speed,
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.travelAngleEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt) / (JSON_LENGTH(aa.score, '$.travelAngleEntries') + 0.00001),
        0.0
    ) AS avg_travel_angle,
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.workAngleEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt) / (JSON_LENGTH(aa.score, '$.workAngleEntries') + 0.00001),
        0.0
    ) AS avg_work_angle,
    COALESCE(
        (SELECT SUM(val) FROM JSON_TABLE(aa.score, '$.distanceEntries[*]' COLUMNS(val DECIMAL(10,2) PATH '$')) jt) / (JSON_LENGTH(aa.score, '$.distanceEntries') + 0.00001),
        0.0
    ) AS avg_distance
FROM Analytics a
LEFT JOIN Analytics aa ON a.session_id = aa.session_id AND aa.label_id = 127
LEFT JOIN Users u ON a.user_id = u.id
WHERE a.application_id = 7
GROUP BY a.session_id, a.user_id, a.date, a.application_id, u.organization_id, aa.score;

-- 添加主键索引
ALTER TABLE WeldingSessionsAgregated ADD PRIMARY KEY (s_id);

内容的提问来源于stack exchange,提问作者Dimitar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:57:38