事务隔离级别以及@Transactional注解中的验证

1.概述

事务隔离级别一共有4种,分别为:READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)、SERIALIZABLE(串行化),并发随着等级的提高而降低,mysql默认隔离等级为REPEATABLE READ。

READ UNCOMMITTED会产生脏读、不可重复读、幻读。

READ COMMITTED解决了脏读,会产生不可重复读、幻读。

REPEATABLE READ解决了脏读、不可重复读,解决部分幻读问题。(默认)

SERIALIZABLE不会产生任何问题,事务没有并发,效率差。

2.结合实例

2.1 READ UNCOMMITTED

首先是update事务

    @Transactional(value = "mainDatasourceTransactionManager")
    public int update(long userId,String name) {
        logger.info("----------  update start ----------");
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        adminUserEntity.setUserId(userId);
        adminUserEntity.setName(name);
        logger.info("set name = '{}'",name);
        int i = adminUserMapper.updateById(adminUserEntity);
        logger.info("update sleeping");
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("----------  update sleep end  ----------");
        return i;
    }
    @RequestMapping(value = "/update/{userId}/{name}")
    public int update(@PathVariable(name = "userId") Long userId,@PathVariable(name = "name") String name){
        int i = adminUserService.update(userId,name);
        return i;
    }

select事务:

    @Transactional(value = "mainDatasourceTransactionManager",isolation = Isolation.READ_UNCOMMITTED)
    public AdminUserEntity getDataUnCommit(long userId) {
        AdminUserEntity adminUserEntity = adminUserMapper.selectById(userId);
        logger.info("getData [READ_UNCOMMITTED] => adminUserEntity:{}",adminUserEntity);
        return adminUserEntity;
    }
    @RequestMapping(value = "/getData/unCommit/{userId}")
    public AdminUserEntity getDataUnCommit(@PathVariable(name = "userId") Long userId){
        AdminUserEntity data = adminUserService.getDataUnCommit(userId);
        return data;
    }

更新后先让线程sleep 10秒钟,暂不提交update事务,然后我们在期间进行select查询,预期效果为可以查询到这个未提交的事务修改的数据。

启动项目,先访问http://localhost:8080/getData/unCommit/1

2018-11-13 10:07:30.106  INFO 324956 --- [nio-8080-exec-1] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [READ_UNCOMMITTED] => adminUserEntity:AdminUserEntity{userId=1, loginId='321312', name='java', phone='15251708673', roleId=null, localPassword='DBBE2055FF0198D265432967F2AC696E', passwordSuffix='null', birthday=null, sex='0001-3', token='null', enable=true}

再访问http://localhost:8080/update/1/python,再刷新http://localhost:8080/getData/unCommit/1

2018-11-13 10:10:05.063  INFO 324956 --- [nio-8080-exec-7] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update start ----------
2018-11-13 10:10:05.063  INFO 324956 --- [nio-8080-exec-7] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'python'
2018-11-13 10:10:05.312  INFO 324956 --- [nio-8080-exec-7] c.m.p.r.s.impl.AdminUserServiceImpl      : update sleeping
2018-11-13 10:10:06.099  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [READ_UNCOMMITTED] => adminUserEntity:AdminUserEntity{userId=1, loginId='321312', name='python', phone='15251708673', roleId=null, localPassword='DBBE2055FF0198D265432967F2AC696E', passwordSuffix='null', birthday=null, sex='0001-3', token='null', enable=true}
2018-11-13 10:10:15.313  INFO 324956 --- [nio-8080-exec-7] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update sleep end  ----------

和预期的一样,在update事务未提交的时候,另一个事务就已经读到了这个新的数据。

2.2 READ COMMITTED

保持update事务方法不变,新增查询方法:

    @Transactional(value = "mainDatasourceTransactionManager",isolation = Isolation.READ_COMMITTED)
    public AdminUserEntity getDataCommitted() {
        List> list = jdbcTemplate.queryForList("select * from admin_user");
        logger.info("getData [READ_COMMITTED] (1) :");
        for(Map adminUserEntity : list){
            logger.info(adminUserEntity.toString());
        }
        try {
            Thread.sleep(1000 * 20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        list = jdbcTemplate.queryForList("select * from admin_user");
        logger.info("getData [READ_COMMITTED] (2) :");
        for(Map adminUserEntity : list){
            logger.info(adminUserEntity.toString());
        }
        return null;
    }
    @RequestMapping(value = "/getData/committed")
    public AdminUserEntity getDataCommit(){
        AdminUserEntity data = adminUserService.getDataCommitted();
        return data;
    }

现在数据库中user_id为1的这条数据name是"python",然后我们访问http://localhost:8080/update/1/c++,update事务开启、线程睡眠,此时访问http://localhost:8080/getData/committed:

2018-11-13 10:19:30.247  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update start ----------
2018-11-13 10:19:30.247  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'c++'
2018-11-13 10:19:30.316  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : update sleeping
2018-11-13 10:19:32.110  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [READ_COMMITTED] (1) :
2018-11-13 10:19:32.110  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=python, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}

我们可以发现,未被提交的事务的数据未被读取,name还是为"python"。

然后这个线程也进入睡眠,当update事务结束,getData中的事务又进行了一次查询:

2018-11-13 10:19:40.318  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update sleep end  ----------
2018-11-13 10:19:52.152  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [READ_COMMITTED] (2) :
2018-11-13 10:19:52.152  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=c++, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}

此时查到了刚刚已经提交的修改产生的新数据(name为"c++"),但是,出现了不可重复读的问题(同一个事务两次一样的查询出现了不同的结果)。

2.3 REPEATABLE READ

2.3.1 demo1

update事务方法不变,新增查询方法:

    @Transactional(value = "mainDatasourceTransactionManager",isolation = Isolation.REPEATABLE_READ)
    public AdminUserEntity getDataRepeatable() {
        List> list = jdbcTemplate.queryForList("select * from admin_user");
        logger.info("getData [REPEATABLE_READ] (1) :");
        for(Map adminUserEntity : list){
            logger.info(adminUserEntity.toString());
        }
        try {
            Thread.sleep(1000 * 20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        list = jdbcTemplate.queryForList("select * from admin_user");
        logger.info("getData [REPEATABLE_READ] (2) :");
        for(Map adminUserEntity : list){
            logger.info(adminUserEntity.toString());
        }
        return null;
    }
    @RequestMapping(value = "/getData/repeatable")
    public AdminUserEntity getDataRepeatable(){
        AdminUserEntity data = adminUserService.getDataRepeatable();
        return data;
    }

现在数据库中user_id为1的这条数据name是"c++",然后我们访问http://localhost:8080/update/1/c,update事务开启、线程睡眠,此时访问http://localhost:8080/getData/repeatable:

2018-11-13 10:29:31.925  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update start ----------
2018-11-13 10:29:31.925  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'c'
2018-11-13 10:29:31.997  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : update sleeping
2018-11-13 10:29:33.047  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [REPEATABLE_READ] (1) :
2018-11-13 10:29:33.047  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=c++, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}

第一次查询到name为"c++",没有读取到未提交的数据。

2018-11-13 10:29:41.997  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update sleep end  ----------
2018-11-13 10:29:53.087  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [REPEATABLE_READ] (2) :
2018-11-13 10:29:53.087  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=c++, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}

当update事务提交,进行第二次查询,读取到的数据还是"c++",但是此时数据库中的name已经为"c"了。

事务隔离级别以及@Transactional注解中的验证_第1张图片

2.3.2 demo2

调整顺序,先开启查询事务,再开启update事务,update事务结束,查询事务结束,两次查询结果也是一致的。

步骤:先访问http://localhost:8080/getData/repeatable,再访问http://localhost:8080/update/1/go,日志如下:

2018-11-13 10:34:02.569  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [REPEATABLE_READ] (1) :
2018-11-13 10:34:02.569  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=c, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}
2018-11-13 10:34:03.764  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update start ----------
2018-11-13 10:34:03.764  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'go'
2018-11-13 10:34:03.842  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : update sleeping
2018-11-13 10:34:13.843  INFO 324956 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  update sleep end  ----------
2018-11-13 10:34:22.603  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [REPEATABLE_READ] (2) :
2018-11-13 10:34:22.603  INFO 324956 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=c, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}

数据库:

事务隔离级别以及@Transactional注解中的验证_第2张图片

2.3.3 demo3

新增insert事务方法:

    @Transactional(value = "mainDatasourceTransactionManager")
    public long insert(String name) {
        logger.info("----------  insert start ----------");
        long id = System.currentTimeMillis();
        logger.info("the id is : {}",id);
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        adminUserEntity.setUserId(id);
        adminUserEntity.setName(name);
        adminUserEntity.setPhone("110");
        logger.info("set name = '{}'",name);
        int i = adminUserMapper.insert(adminUserEntity);
        logger.info("insert sleeping");
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("----------  insert sleep end  ----------");
        return id;
    }
    @RequestMapping(value = "/insert/{name}")
    public long insert(@PathVariable(name = "name") String name){
        long i = adminUserService.insert(name);
        return i;
    }

查询方法保持不变

先访问http://localhost:8080/getData/repeatable,再访问http://localhost:8080/insert/javascript,日志如下:

2018-11-13 10:39:26.410  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [REPEATABLE_READ] (1) :
2018-11-13 10:39:26.410  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=go, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}
2018-11-13 10:39:27.638  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert start ----------
2018-11-13 10:39:27.638  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : the id is : 1542076767638
2018-11-13 10:39:27.638  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'javascript'
2018-11-13 10:39:27.713  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : insert sleeping
2018-11-13 10:39:37.713  INFO 324956 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert sleep end  ----------
2018-11-13 10:39:46.447  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : getData [REPEATABLE_READ] (2) :
2018-11-13 10:39:46.448  INFO 324956 --- [nio-8080-exec-9] c.m.p.r.s.impl.AdminUserServiceImpl      : {user_id=1, login_id=321312, name=go, phone=15251708673, local_password=DBBE2055FF0198D265432967F2AC696E, password_suffix=null, birthday=null, sex=0001-3, token=null, role_id=null, enable=true}

数据库:

事务隔离级别以及@Transactional注解中的验证_第3张图片

同一个事务中进行两次查询,在两次查询期间产生完成了一个insert事务,两次查询结果都一样,并没有查询到javascript这条记录。

难道REPEATABLE READ已经解决了幻读问题?

2.3.4 demo4

保持insert方法不变,新增一个selectAndUpdate方法:

    @Transactional(value = "mainDatasourceTransactionManager",isolation = Isolation.REPEATABLE_READ)
    public int selectAndUpdate(String name) {
        logger.info("----------  selectAndUpdate start ----------");
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        adminUserEntity.setName(name);
        logger.info("set name = '{}'",name);
        
        //1.查询全表数量
        Integer size = adminUserMapper.selectCount(new QueryWrapper());
        logger.info("size1 :{}",size);
        try {
            Thread.sleep(20 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //2.全表更新 name字段
        int updateSize = adminUserMapper.update(new AdminUserEntity(),new UpdateWrapper().lambda().set(AdminUserEntity::getName,name));
        logger.info("selectAndUpdateSize :{}",updateSize);
        //3.查询全表数量
        List> list = jdbcTemplate.queryForList("select * from admin_user");
        logger.info("size2:{}",list.size());
        logger.info("----------  selectAndUpdate sleep end  ----------");
        return list.size();
    }
    @RequestMapping(value = "/selectAndUpdate/{name}")
    public int selectAndUpdate(@PathVariable(name = "name") String name){
        int i = adminUserService.update(name);
        return i;
    }

先访问http://localhost:8080/selectAndUpdate/nodeJs,再访问http://localhost:8080/insert/net,日志如下:

2018-11-13 10:51:05.544  INFO 326080 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  selectAndUpdate start ----------
2018-11-13 10:51:05.544  INFO 326080 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'nodeJs'
2018-11-13 10:51:05.806  INFO 326080 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : size1 :2
2018-11-13 10:51:06.084  INFO 326080 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert start ----------
2018-11-13 10:51:06.084  INFO 326080 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : the id is : 1542077466084
2018-11-13 10:51:06.085  INFO 326080 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'net'
2018-11-13 10:51:06.171  INFO 326080 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : insert sleeping
2018-11-13 10:51:16.171  INFO 326080 --- [nio-8080-exec-6] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert sleep end  ----------
2018-11-13 10:51:26.006  INFO 326080 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : selectAndUpdateSize :3
2018-11-13 10:51:26.063  INFO 326080 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : size2:3
2018-11-13 10:51:26.064  INFO 326080 --- [io-8080-exec-10] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  selectAndUpdate sleep end  ----------

可以看到,第一次查询到的数据量为2,然后等待insert结束,selectAndUpdate方法进行全表update,更新到的行数为3,第二次查询出来的全表数量也为3。

2.3.5 demo5

如果我们把selectAndUpdate方法中的全表更新放在sleep之前,也就是说,先开启A事务、查询数量、全表更新name字段,B事务插入、b事务结束,A事务再次查询、A事务结束。

具体步骤如下:

insert方法不变,新增selectAndUpdate2方法:

    @Transactional(value = "mainDatasourceTransactionManager",isolation = Isolation.REPEATABLE_READ)
    public int selectAndUpdate2(String name) {
        logger.info("----------  selectAndUpdate2 start ----------");
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        adminUserEntity.setName(name);
        logger.info("set name = '{}'",name);

        //1.查询全表数量
        Integer size = adminUserMapper.selectCount(new QueryWrapper());
        logger.info("size1 :{}",size);

        //2.全表更新 name字段
        int updateSize = adminUserMapper.update(new AdminUserEntity(),new UpdateWrapper().lambda().set(AdminUserEntity::getName,name));
        logger.info("selectAndUpdateSize :{}",updateSize);

        try {
            Thread.sleep(20 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //3.查询全表数量
        List> list = jdbcTemplate.queryForList("select * from admin_user");
        logger.info("size2:{}",list.size());
        logger.info("----------  selectAndUpdate2 sleep end  ----------");
        return list.size();
    }
    @RequestMapping(value = "/selectAndUpdate2/{name}")
    public int selectAndUpdate2(@PathVariable(name = "name") String name){
        int i = adminUserService.selectAndUpdate2(name);
        return i;
    }

当前数据库一共3条记录,name均为"nodeJs",先访问http://localhost:8080/selectAndUpdate2/ruby,再访问http://localhost:8080/insert/e,日志如下:

2018-11-13 11:02:57.494  INFO 326512 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  selectAndUpdate2 start ----------
2018-11-13 11:02:57.495  INFO 326512 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'ruby'
2018-11-13 11:02:57.718  INFO 326512 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : size1 :3
2018-11-13 11:02:57.822  INFO 326512 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : selectAndUpdateSize :3
2018-11-13 11:02:58.022  INFO 326512 --- [nio-8080-exec-8] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert start ----------
2018-11-13 11:02:58.022  INFO 326512 --- [nio-8080-exec-8] c.m.p.r.s.impl.AdminUserServiceImpl      : the id is : 1542078178022
2018-11-13 11:02:58.023  INFO 326512 --- [nio-8080-exec-8] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'e'
2018-11-13 11:03:17.868  INFO 326512 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : size2:3
2018-11-13 11:03:17.868  INFO 326512 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  selectAndUpdate2 sleep end  ----------
2018-11-13 11:03:17.991  INFO 326512 --- [nio-8080-exec-8] c.m.p.r.s.impl.AdminUserServiceImpl      : insert sleeping
2018-11-13 11:03:27.991  INFO 326512 --- [nio-8080-exec-8] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert sleep end  ----------

update事务只修改了3条数据,两次select查询到的数据量也为3,此时数据库中数据量为4:

事务隔离级别以及@Transactional注解中的验证_第4张图片

题外话:这里要注意几个时间点,insert方法sleep 10秒钟,但是insert事务开启的时间为:11:02:58,结束的时间为:11:03:27,且在selectAndUpdate2事务结束之后10秒钟后才结束,selectAndUpdate2中update的时候全表锁定(因为没有加索引条件),insert方法需等待update的锁释放。

2.4 SERIALIZABLE

保持insert方法不变,新增selectAndUpdate3方法:

    @Transactional(value = "mainDatasourceTransactionManager",isolation = Isolation.SERIALIZABLE)
    public int selectAndUpdate3(String name) {
        logger.info("----------  selectAndUpdate3 start ----------");
        AdminUserEntity adminUserEntity = new AdminUserEntity();
        adminUserEntity.setName(name);
        logger.info("set name = '{}'",name);

        //1.查询全表数量
        Integer size = adminUserMapper.selectCount(new QueryWrapper());
        logger.info("size1 :{}",size);

        //2.全表更新 name字段
        int updateSize = adminUserMapper.update(new AdminUserEntity(),new UpdateWrapper().lambda().set(AdminUserEntity::getName,name));
        logger.info("selectAndUpdateSize :{}",updateSize);

        try {
            Thread.sleep(20 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //3.查询全表数量
        List> list = jdbcTemplate.queryForList("select * from admin_user");
        logger.info("size2:{}",list.size());
        logger.info("----------  selectAndUpdate3 sleep end  ----------");
        return list.size();
    }
    @RequestMapping(value = "/selectAndUpdate3/{name}")
    public int selectAndUpdate3(@PathVariable(name = "name") String name){
        int i = adminUserService.selectAndUpdate3(name);
        return i;
    }

先清空下数据库,只剩下一条user_id = 1,name = "ruby"的记录。

访问:http://localhost:8080/selectAndUpdate3/vb,再访问:http://localhost:8080/insert/kotlin,日志如下:

2018-11-13 14:38:12.332  INFO 328996 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  selectAndUpdate3 start ----------
2018-11-13 14:38:12.332  INFO 328996 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'vb'
2018-11-13 14:38:12.547  INFO 328996 --- [nio-8080-exec-4] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert start ----------
2018-11-13 14:38:12.547  INFO 328996 --- [nio-8080-exec-4] c.m.p.r.s.impl.AdminUserServiceImpl      : the id is : 1542091092547
2018-11-13 14:38:12.547  INFO 328996 --- [nio-8080-exec-4] c.m.p.r.s.impl.AdminUserServiceImpl      : set name = 'kotlin'
2018-11-13 14:38:12.727  INFO 328996 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : size1 :1
2018-11-13 14:38:13.019  INFO 328996 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : selectAndUpdateSize :1
2018-11-13 14:38:33.114  INFO 328996 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : size2:1
2018-11-13 14:38:33.114  INFO 328996 --- [nio-8080-exec-3] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  selectAndUpdate3 sleep end  ----------
2018-11-13 14:38:33.172  INFO 328996 --- [nio-8080-exec-4] c.m.p.r.s.impl.AdminUserServiceImpl      : insert sleeping
2018-11-13 14:38:43.173  INFO 328996 --- [nio-8080-exec-4] c.m.p.r.s.impl.AdminUserServiceImpl      : ----------  insert sleep end  ----------

数据库:

时间点:14:38:12和14:38:43为insert事务的开始和结束,且结束时间为selectAndUpdate3结束后的10秒;

selectAndUpdate3方法查询到的数据始终是一样的,并且update没有覆盖掉name = 'kotlin'这条记录,由此可见,事务这里已经串行化了。

2.5 小结

RU在无锁环境下进行,RR是快照读,读到的是历史数据,所以能保证数据一致性,但是在update的时候会发生幻读。

你可能感兴趣的:(数据库,数据库,事务,spring,事务隔离等级)