一、前言
最近项目中需要对退网资源进行扣减,由于项目中并没有分布式锁也没有引入Seta等一系列原因,所以采用CAS乐观锁解决高并发资源扣业务问题。
二、方案
(1)针对需要扣减的数据,采用如下步骤进行实现
1:先锁定当前行
可以使用select XXX from X for update方式,我们业务上通过更新当前网元的最后更新时间(因为方法都在同一个事务中,公司内部框架帮我们处理好了)来锁定当前资源行。
2:查询数据库中网元版本号是否和当前网元版本号一致,如果不一致,直接报错返回。否则,继续执行下面步骤。
3:根据版本号,更新当前网元资源,并且设置版本号+1。
UPDATE node_sources SET amount = $newAmount, version= version+1
WHERE nodeId = $nodeId AND version=$version_old AND amount = $oldAmount
4:记录网元交付流水和操作日志,方便对资源变化情况追溯。
(2)采用乐观锁解决并发问题的另一种方式
List<Long> lineIds = new ArrayList<>();
List<String> partNumList = new ArrayList<>();
// 对lineIds自然排序
Collections.sort(lineIds);
// 乐观锁,同一个事务中,更新表
assetLineDao.lockAssetLine(lineIds);
// 对partNumList自然排序
Collections.sort(partNumList);
// 乐观锁,锁定行表,此处需要通过排序锁表,因此需要循环调用dao
// 假设获取到的数据
List<AssetLine> tempAssetLineVoList = new ArrayList<AssetLine>();
List<Long> assetLineIds =
tempAssetLineVoList.stream().map(AssetLineVo::getLineId).sort().collect(Collectors.toList());
for (Long assetLineId : assetLineIds) {
assetLineSpartDao.lockAssetLineSpart(assetLineId);
}
List<AssetLine> assetLineList = new ArrayList<AssetLine>();
List<AssetLineSpart> assetLineSpartList = new ArrayList<AssetLineSpart>();
for(){
AssetLine assetLine = new AssetLine();
// 假设业务数据赋值
assetLineList.add(assetLine);
// 假设获取到了assetLineId
Long assetLineId;
for(){
AssetLineSpart assetLineSpart = new AssetLineSpart();
assetLineSpart.setAssetLineId(assetLineId);
// 假设业务数据赋值
assetLineSpartList.add(assetLineSpart);
}
}
// 更新asset_line表data_version
assetLineDao.update(assetLineList);
// 更新asset_line_spart表编码量
assetLineSpartDao.update(assetLineSpartList);
SQL语句
update asset_line_t set
last_update_time = now()
where in in
<foreach collection="lineIds" item="lineId" open="(" separator="," close=")">
#{lineId}
</foreach>
update asset_line_spart_t set
last_update_time = now()
where asset_line_id = #{assetLineId}
part_num in
<foreach collection="partNumList" item="partNum" open="(" separator="," close=")">
#{partNum}
</foreach>