生产问题一:redis锁处理幂等性失效

伪代码:

java 复制代码
    @Transactional(rollbackFor = Exception.class)
    public void add(User user) {
        String key = "key";
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            long count = userMapper.selectCount(user);
            if (count == 0) {
                userMapper.insert(user);
            }
        } catch (Exception e) {
            log.error("add user error", e);
        } finally {
            lock.unlock();
        }
        System.out.println("插入成功");
    }

问题分析:

如果有两个线程a,b。如果a线程释放锁后,退出方法前,让出时间片,由于方法未执行完,此时事务没有提交,那么b线程在去数据库查询的时候仍然出来count为0,执行了insert操作,两个线程执行完该方法提交事务,此时数据库中会增加两条数据,幂等性失效。

注:a事务未提交,查出来count仍然为0的原因是,mysql默认的事物隔离级别是可重复读,因此无法读取为提交的数据

解决方式:

第一种:

java 复制代码
    //@Transactional(rollbackFor = Exception.class)
    public void add(User user) {
        String key = "key";
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            long count = userMapper.selectCount(user);
            if (count == 0) {
                userMapper.insert(user);
            }
        } catch (Exception e) {
            log.error("add user error", e);
        } finally {
            lock.unlock();
        }
        System.out.println("插入成功");
    }

去掉@Transactional注解,这样在a线程在insert时候就会自动提交事务,a释放锁后,b在查询时候,count不等0,不执行插入操作。

方法二:

java 复制代码
public class Test {

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;

    public void test(User user) {
        String key = "key";
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            userService.addOk(user);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void addOk(User user) {
        long count = userMapper.selectCount(user);
        if (count == 0) {
            userMapper.insert(user);
        }
    }
}

a提交事务后再释放锁后,b在查询时候,count不等0,不执行插入操作。
绝大数情况下,为了防止重复数据的产生,我们都会在表中加唯一索引,这是一个非常简单,并
且有效的方案。 虽说抛异常对数据来说没有影响,不会造成错误数据。但是为了保证接口幂等性,我们需要对该 异常进行捕获,然后返回成功。
扩展:
事务在生产实践中经常犯的错误:
1.事务范围:应该加入事务的代码未加入到事务中
2.事务大小:事务过大,是否有必要拆解小事务(如何优化),拆解后一致性问题。
3.传播范围: a.多线程之间不可传播;b.多个方法内如果异常被捕获,事务会被被标记为异常事务,不可以再次提交(虽然不影响数据,但是有报错信息)

相关推荐
蜗牛互联网4 分钟前
从Barclays扩大Claude部署看受监管软件工程的责任重分配
java·大数据·人工智能·后端·软件工程
天远API15 分钟前
零信任架构实战:基于天远股权穿透构建自动化供应链授信穿透网关
java·人工智能·架构·自动化
最强小杰27 分钟前
claude-opus-5.5 API 接入教程:anthropic-version 头、tools 定义、流式事件格式三个常见问题全部梳理,收藏备用
java·服务器·网络·ai
Cc.Y29 分钟前
Java零基础入门:方法(函数)深度掌握
java·开发语言
奋发向前wcx1 小时前
CSP-J复赛模拟赛1 王晨旭补题 2026.10.2
java·开发语言
卓怡学长1 小时前
w192基于springboot“考研情报站”微信小程序设计与实现
java·spring boot·spring·微信小程序·intellij-idea
wuminyu1 小时前
JEP491中synchronized关键字引发的平台线程钉住解决方法简介
java·linux·c语言·jvm·c++
anew___2 小时前
《从零手写操作系统 (20):ELF加载器——让OS读懂现代编译器》
java·服务器·前端
code2cat2 小时前
【随笔】MCP资源更新订阅:通知到达以后,Agent怎样刷新旧资料
java·后端·开发工具·ai agent·mcp
一只旭宝3 小时前
C++ 后端开发面试题整理
java·c++·面试