问题背景

现有一段查询简化如下

IPage<AdminUserDO> page = Page.of(1, 10);

LambdaQueryWrapper<AdminUserDO> query = new LambdaQueryWrapper<>();
query.eq(AdminUserDO::getFirstName, "x");

IPage<AdminUserDO> adminUserDOIPage = adminUserMapper.selectPage(page, query);

分页查询会查两次,先查count再查本页数据
这个查询很慢,就有人进行了优化,优化后简化如下

IPage<AdminUserDO> page = Page.of(1, 10, false);

LambdaQueryWrapper<AdminUserDO> query = new LambdaQueryWrapper<>();
query.eq(AdminUserDO::getFirstName, "x");

IPage<AdminUserDO> adminUserDOIPage = new Page<>();

new Thread(() -> {
    adminUserDOIPage.setRecords(adminUserMapper.selectPage(page, query).getRecords());
}).start();
new Thread(() -> {
    adminUserDOIPage.setTotal(adminUserMapper.selectCount(query));
}).start();

相当于把查count和查本页数据并发
代码上线后测试发现,快速点击查询,偶尔出现查出来的total不对,正常应该是二十几,偶尔出现几百的情况

问题分析

在日志中查看sql语句,发现total正常情况下,sql语句是对的,包含first_name = 'x’的条件
total异常情况下,没有where条件
说明就是一个偶发问题
在加上有多线程,盲猜一手并发问题,用代码进行验证,编写测试用例如下

LambdaQueryWrapper<AdminUserDO> query = new LambdaQueryWrapper<>();
query.eq(AdminUserDO::getFirstName, "x");

Runnable r = () -> {
    TenantContextHolder.setIgnore(true);
    System.out.println(adminUserMapper.selectCount(query));
};

new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();

运行结果如下
在这里插入图片描述
发现只有一次是正确带上了查询条件的,其他的都把查询条件丢了(WHERE deleted = 0是框架自动加的不是来自LambdaQueryWrapper的条件),说明LambdaQueryWrapper用过一次过后就把条件清空了?(具体原理需要看源码分析)

问题修复

将LambdaQueryWrapper clone,并发时使用副本操作

LambdaQueryWrapper<AdminUserDO> query = new LambdaQueryWrapper<>();
query.eq(AdminUserDO::getFirstName, "x");

Runnable r = () -> {
    System.out.println(adminUserMapper.selectCount(query.clone()));
};

new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();

在这里插入图片描述
问题解决

更多推荐