简介
本文记录一次处理达梦数据库写入时序数据库性能优化的问题
场景
从以往MYSQL/KINGBASE迁移到DM过程中,发现时序数据消费速度较低,kafka形成严重积压,代码除了数据库写入层进行了修改,其余未做任何变化,那么可以快速定位到问题是写入问题。具体场景为,当时序数据消费时,按照点编码更新DM实时表的value值,若点编码不存在则插入该新编码对应的数据。
相较于普通场景,我们一般会使用先查询,后考虑插入还是更新的方式,但对于大量数据来说,需要优化插入逻辑,即一次性完成此操作:存在则更新、不存在则插入,相较于MYSQL/KINGBASE的insert into on confilict 方式,DM使用的是merge into 方法:
MERGE INTO XXXX t1
USING (
<foreach collection="list" item="item" index="index" separator="UNION ALL">
SELECT #{item.id} as id,
#{item.createDate} as create_date,
now() as update_date
FROM DUAL
</foreach>
) t2 ON (t1.id = t2.id)
when matched then
update set
t1.XXX = t2.XXXX,
t1.create_date = t2.create_date,
t1.update_date = t2.update_date
WHEN NOT MATCHED THEN
INSERT (id,XXX,create_date,update_date)
VALUES (t2.id,XXX,now(),t2.update_date)
代码可以正常运行,但是该方式数据写入存在较大问题,从日志可以看出,插入数据上千条,所耗费时间达到数据秒,不满足业务需求

解决方案
通过与达梦团队对接优化方案,最后得到了更优的做法,即写入数据先进入临时表,merge的时候使用临时表数据进行merge,从而避免在写入过程中构建大批量临时数据项
1、创建临时表(表结构与插入表一致,去除ID/唯一性等限制信息)
CREATE TEMPORARY table mqt_temp (
"ID" VARCHAR(64 CHAR),
XXXX..
"CREATE_DATE" TIMESTAMP(0),
"UPDATE_DATE" TIMESTAMP(0)
) ON COMMIT DELETE ROWS;
2、插入逻辑由一次性插入分为2个步骤,即先插入临时表,再merge
this.baseDao.insertTmp(updateDataList);
this.baseDao.merge();
3、相关mybatis语句
(1)插入临时表mybatis
<insert id="insertTmp">
INSERT INTO mqt_temp (
id, XXX, create_date, update_date
)
VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.id},
XXX....
#{item.dataTime},
#{item.createDate},
NOW()
)
</foreach>
</insert>
(2)新merge语句(使用临时表)
<insert id="updateBatch2" parameterType="java.util.List">
MERGE INTO XXX 最终插入数据表
USING (
SELECT *
FROM mqt_temp
) s ON (t.id = s.id)
when matched then
update set
t.XX = s.XXX,
t.data_time = s.data_time,
t.update_date = s.update_date
WHEN NOT MATCHED THEN
INSERT (id,XXXX, create_date, update_date)
VALUES (s.id,XXX, now(), now())
</insert>
总结
使用临时表优化后,数据插入效率提升数倍左右
