数据同时并发上报的时候,需要把同一个复合key的多条数据打平为一条,虽然使用了synchronized修饰了方法,但是并发插入的时候还是出现了重复插入同一个key的数据。

发布时间 2023-03-28 00:23:48作者: 勇敢-的心
【问题描述】
如果厂商同一个时间点发送几个请求到我们这边,我怎么保证请求的顺序呢?比如,第一次请求我要在一张表里面add一条数据,第二个请求我就update这张表的一个字段。
即数据同时并发上报的时候,需要把同一个复合key的多条数据打平为一条,虽然使用了synchronized修饰了方法,但是并发插入的时候还是出现了重复插入同一个key的数据。
private synchronized void saveOrUpdateDistributionBox(DistributionBoxRealTime distributionBoxRealTime){
。。。。。。
DistributionBox distributionBox = boxMapper.selectOne(new LambdaQueryWrapper<DistributionBox>().
eq(DistributionBox::getDeviceSn, deviceSn).
eq(DistributionBox::getDataTime, dataTime).last("limit 1"));
String dataValueUnit = distributionBoxRealTime.getDataValue()+distributionBoxRealTime.getDataUnit();
if(null == distributionBox) {
DistributionBox box = new DistributionBox();
box.setDeviceSn(deviceSn);
box.setDataTime(dataTime);
setData(deviceName, box, dataValueUnit);
boxMapper.insert(box);
} else {
setData(deviceName, distributionBox, dataValueUnit);
boxMapper.updateById(distributionBox);
}
}

【原因分析&解决方案】
使用
synchronized修饰方法失效的原因,可能是因为非单例调用,这里今天先不详细分析synchronized锁失效的具体原因和解决方案。
今天,我们可以换一个思路解决这个问题,可以使用mysql数据库本身的
ON DUPLICATE KEY UPDATE语法来进行新增或更新。
如果表中已经存在了符合索引的key,就更新某个字段的值,如果不存在就新增一条数据,这样可以把多条数据打平为一条数据。
<insert id="saveOrUpdateDisBox">
insert into dis_box (device_id,
data_time,
current_a,
current_b,
current_c)
values (#{deviceId},
#{dataTime},
#{phaseCurrentA},
#{phaseCurrentB},
#{phaseCurrentC})
ON DUPLICATE KEY UPDATE
<if test="currentA != null">
current_a = #{currentA}
</if>
<if test="currentB != null">
current_b = #{currentB}
</if>
<if test="currentC != null">
current_c = #{currentC}
</if>
</insert>