高并发下使用防重表做防重案例
工作中遇到的重复数据产生的问题:
之前提供的一个批量复制商品的接口,产生了重复的商品数据。
针对于这个问题我想到了可以加一张防重表
,在防重表中增加商品表的name和model字段作为唯一索引。
例如:
CREATE TABLE `product_unique` (`id` bigint(20) NOT NULL COMMENT 'id',`name` varchar(130) DEFAULT NULL COMMENT '名称',`model` varchar(255) NOT NULL COMMENT '规格',`user_id` bigint(20) unsigned NOT NULL COMMENT '创建用户id',`user_name` varchar(30) NOT NULL COMMENT '创建用户名称',`create_date` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',PRIMARY KEY (`id`),UNIQUE KEY `ux_name_model` (`name`,`model`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品防重表';
其中表中的id可以用商品表的id,表中的name和model就是商品表的name和model,不过在这张防重表中增加了这两个字段的唯一索引。
在添加商品数据之前,先添加防重表。如果添加成功,则说明可以正常添加商品,如果添加失败,则说明有重复数据。
防重表添加失败,后续的业务处理,要根据实际业务需求而定。
如果业务上允许添加一批商品时,发现有重复的,直接抛异常,则可以提示用户:系统检测到重复的商品,请刷新页面重试。
例如:
try {transactionTemplate.execute((status) -> {productUniqueMapper.batchInsert(productUniqueList);productMapper.batchInsert(productList);return Boolean.TRUE;});
} catch(DuplicateKeyException e) {throw new BusinessException("系统检测到重复的商品,请刷新页面重试");
}
在批量插入数据时,如果出现了重复数据,捕获DuplicateKeyException
异常,转换成BusinessException
这样运行时的业务异常。
还有一种业务场景,要求即使出现了重复的商品,也不抛异常,让业务流程也能够正常走下去。
例如:
try {transactionTemplate.execute((status) -> {productUniqueMapper.insert(productUnique);productMapper.insert(product);return Boolean.TRUE;});
} catch(DuplicateKeyException e) {product = productMapper.query(product);
}
在插入数据时,如果出现了重复数据,则捕获DuplicateKeyException
,在catch
代码块中再查询一次商品数据,将数据库已有的商品直接返回。
如果调用了同步添加商品的接口,这里非常关键的一点,是要返回已有数据的id,业务系统做后续操作,要拿这个id操作。
当然在执行execute之前,还是需要先查一下商品数据是否存在,如果已经存在,则直接返回已有数据,如果不存在,才执行execute方法。这一步千万不能少。
例如:
Product oldProduct = productMapper.query(product);
if(Objects.nonNull(oldProduct)) {return oldProduct;
}try {transactionTemplate.execute((status) -> {productUniqueMapper.insert(productUnique);productMapper.insert(product);return Boolean.TRUE;});
} catch(DuplicateKeyException e) {product = productMapper.query(product);
}
return product;
千万注意:防重表和添加商品的操作必须要在同一个事务中,否则会出问题。
顺便说一下,还需要对商品的删除功能做特殊处理一下,在逻辑删除商品表的同时,要物理删除防重表。用商品表id作为查询条件即可。
说实话,解决重复数据问题的方案挺多的,没有最好的方案,只有最适合业务场景的,最优的方案。