mybatis乐观锁重试机制
MyBatis实现乐观锁遇到的问题
1. MyBatis缓存(一级缓存)
问题:MyBatis在查询时,会将结果放入缓存中,导致再次查询相同的Sql的结果不是数据库中最新的值
解决方案:在statement上加上flushCache="true"
<select id="getFromDb" flushCache="true" resultType="xxx"> select * from device where id = #{id,jdbcType=BIGINT} </select>
2. 事物隔离级别
问题:由于Mysql默认的事物隔离级别是repeatable read
,导致读取不到其他事物提交的最新的值
解决方案:修改为read committed
@Transactional(isolation = Isolation.READ_COMMITTED) public Device getFromDb(long id) { return deviceMapper.getFromDb(id); }
3. 事物传播机制
问题:如果2中的方法是在其他Service中调用的。而其他的Service使用了事务,并且和2中的隔离级别不一样,可能会导致2中设置的隔离级别被覆盖。
解决方案:使用子事务执行
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW) public Device getFromDb(long id) { return deviceMapper.getFromDb(id); }
例
测试类
public class ThreadHttpTest { public static String doPost(String url, Map<String, String> mapParams) { try (CloseableHttpClient httpclient = HttpClients.createDefault()) { HttpPost httpPost = new HttpPost(url); //封装请求参数 if (mapParams != null) { List<BasicNameValuePair> list = new ArrayList<>(); for (Map.Entry<String, String> entry : mapParams.entrySet()) { list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(list, "utf-8"); formEntity.setContentType("Content-Type:application/json"); httpPost.setEntity(formEntity); } try (CloseableHttpResponse response = httpclient.execute(httpPost)) { return EntityUtils.toString(response.getEntity()); } } catch (IOException e) { e.printStackTrace(); } return null; } public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(20); for (int i = 0; i < 20; i++) { threadPool.execute(() -> { String name = Thread.currentThread().getName(); String r = doPost("http://localhost:8086/t", null); System.out.println(name + ":" + r); }); } threadPool.shutdown(); } }
controller
@RequestMapping(value = "/t") @ResponseBody public String t() { productService.t(); return new Date().toString(); }
service (备注:sales就是version字段,isRecommend就是要更新的数据,只是拿手头上临时的类写的demo)
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED) @Override public void t() { String name = Thread.currentThread().getName(); //无限重试,直到成功 // int num = 0; // while (num == 0) { // Product p = this.findById(43L); // System.out.println("=====" + name + "人数:" + p.getIsRecommend()); // System.out.println("=====" + name + "版本号:" + p.getSales()); // // if (p.getIsRecommend() == 5) { // System.out.println("=====" + name + "人数已满:" + p.getIsRecommend()); // break; // } // // num = productService.updateT(43L, p.getSales()); // System.out.println("=====" + num); // if (num == 0) { // System.out.println("=====更新失败=====" + name + ":" + num); // } // if (num == 1) { // System.out.println("=====更新成功=====" + name + ":" + num); // } // } //最多尝试3次 for (int i = 0; i < 3; i++) { Product p = this.findById(43L); System.out.println("=====" + name + "人数:" + p.getIsRecommend()); System.out.println("=====" + name + "版本号:" + p.getSales()); if (p.getIsRecommend() == 5) { System.out.println("=====" + name + "人数已满:" + p.getIsRecommend()); break; } int num = productService.updateT(43L, p.getSales()); System.out.println("=====" + num); if (num == 0) { System.out.println("=====更新失败=====" + name + ":" + (i + 1) + "次"); } if (num == 1) { System.out.println("=====更新成功=====" + name + ":" + num); break; } } }
XML
<update id="updateT"> update product set is_recommend = is_recommend + 1, sales = sales + 1 where id = #{id} and sales = #{version}; </update>
日志分析
最多尝试3次日志
=====http-nio-8086-exec-13人数:0 =====http-nio-8086-exec-13版本号:0 =====http-nio-8086-exec-6人数:0 =====http-nio-8086-exec-6版本号:0 =====http-nio-8086-exec-20人数:0 =====http-nio-8086-exec-20版本号:0 =====http-nio-8086-exec-16人数:0 =====http-nio-8086-exec-16版本号:0 =====http-nio-8086-exec-9人数:0 =====http-nio-8086-exec-9版本号:0 =====http-nio-8086-exec-5人数:0 =====http-nio-8086-exec-5版本号:0 =====1 =====更新成功=====http-nio-8086-exec-9:1 =====0 =====更新失败=====http-nio-8086-exec-5:1次 =====http-nio-8086-exec-5人数:1 =====http-nio-8086-exec-5版本号:1 =====1 =====更新成功=====http-nio-8086-exec-5:1 =====0 =====更新失败=====http-nio-8086-exec-6:1次 =====http-nio-8086-exec-6人数:2 =====http-nio-8086-exec-6版本号:2 =====http-nio-8086-exec-15人数:2 =====http-nio-8086-exec-15版本号:2 =====1 =====更新成功=====http-nio-8086-exec-6:1 =====0 =====更新失败=====http-nio-8086-exec-13:1次 =====http-nio-8086-exec-13人数:3 =====http-nio-8086-exec-13版本号:3 =====http-nio-8086-exec-11人数:3 =====http-nio-8086-exec-11版本号:3 =====http-nio-8086-exec-17人数:3 =====http-nio-8086-exec-17版本号:3 =====1 =====更新成功=====http-nio-8086-exec-13:1 =====0 =====更新失败=====http-nio-8086-exec-20:1次 =====http-nio-8086-exec-20人数:4 =====http-nio-8086-exec-20版本号:4 =====http-nio-8086-exec-21人数:4 =====http-nio-8086-exec-21版本号:4 =====1 =====更新成功=====http-nio-8086-exec-20:1 =====0 =====更新失败=====http-nio-8086-exec-16:1次 =====http-nio-8086-exec-16人数:5 =====http-nio-8086-exec-16版本号:5 =====http-nio-8086-exec-16人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-15:1次 =====http-nio-8086-exec-4人数:5 =====http-nio-8086-exec-4版本号:5 =====http-nio-8086-exec-4人数已满:5 =====http-nio-8086-exec-15人数:5 =====http-nio-8086-exec-15版本号:5 =====http-nio-8086-exec-15人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-11:1次 =====http-nio-8086-exec-11人数:5 =====http-nio-8086-exec-11版本号:5 =====http-nio-8086-exec-11人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-17:1次 =====http-nio-8086-exec-17人数:5 =====http-nio-8086-exec-17版本号:5 =====http-nio-8086-exec-17人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-21:1次 =====http-nio-8086-exec-12人数:5 =====http-nio-8086-exec-12版本号:5 =====http-nio-8086-exec-12人数已满:5 =====http-nio-8086-exec-21人数:5 =====http-nio-8086-exec-21版本号:5 =====http-nio-8086-exec-21人数已满:5 =====http-nio-8086-exec-7人数:5 =====http-nio-8086-exec-7版本号:5 =====http-nio-8086-exec-7人数已满:5 =====http-nio-8086-exec-2人数:5 =====http-nio-8086-exec-2版本号:5 =====http-nio-8086-exec-2人数已满:5 =====http-nio-8086-exec-14人数:5 =====http-nio-8086-exec-14版本号:5 =====http-nio-8086-exec-14人数已满:5 =====http-nio-8086-exec-1人数:5 =====http-nio-8086-exec-1版本号:5 =====http-nio-8086-exec-1人数已满:5 =====http-nio-8086-exec-19人数:5 =====http-nio-8086-exec-19版本号:5 =====http-nio-8086-exec-19人数已满:5 =====http-nio-8086-exec-18人数:5 =====http-nio-8086-exec-18版本号:5 =====http-nio-8086-exec-18人数已满:5 =====http-nio-8086-exec-3人数:5 =====http-nio-8086-exec-3版本号:5 =====http-nio-8086-exec-3人数已满:5 =====http-nio-8086-exec-8人数:5 =====http-nio-8086-exec-8版本号:5 =====http-nio-8086-exec-8人数已满:5
无限尝试直到成功日志
=====http-nio-8086-exec-19人数:0 =====http-nio-8086-exec-19版本号:0 =====http-nio-8086-exec-3人数:0 =====http-nio-8086-exec-3版本号:0 =====http-nio-8086-exec-11人数:0 =====http-nio-8086-exec-11版本号:0 =====http-nio-8086-exec-15人数:0 =====http-nio-8086-exec-15版本号:0 =====1 =====更新成功=====http-nio-8086-exec-19:1 =====0 =====更新失败=====http-nio-8086-exec-15:0 =====http-nio-8086-exec-23人数:0 =====http-nio-8086-exec-23版本号:0 =====0 =====更新失败=====http-nio-8086-exec-11:0 =====http-nio-8086-exec-15人数:1 =====http-nio-8086-exec-15版本号:1 =====0 =====更新失败=====http-nio-8086-exec-23:0 =====http-nio-8086-exec-11人数:1 =====http-nio-8086-exec-11版本号:1 =====0 =====更新失败=====http-nio-8086-exec-3:0 =====http-nio-8086-exec-23人数:1 =====http-nio-8086-exec-23版本号:1 =====http-nio-8086-exec-22人数:1 =====http-nio-8086-exec-22版本号:1 =====http-nio-8086-exec-3人数:1 =====http-nio-8086-exec-3版本号:1 =====1 =====更新成功=====http-nio-8086-exec-11:1 =====http-nio-8086-exec-14人数:1 =====http-nio-8086-exec-14版本号:1 =====0 =====更新失败=====http-nio-8086-exec-15:0 =====http-nio-8086-exec-15人数:2 =====http-nio-8086-exec-15版本号:2 =====http-nio-8086-exec-24人数:2 =====http-nio-8086-exec-24版本号:2 =====1 =====更新成功=====http-nio-8086-exec-15:1 =====http-nio-8086-exec-25人数:2 =====http-nio-8086-exec-25版本号:2 =====0 =====更新失败=====http-nio-8086-exec-23:0 =====http-nio-8086-exec-23人数:3 =====http-nio-8086-exec-23版本号:3 =====1 =====更新成功=====http-nio-8086-exec-23:1 =====0 =====更新失败=====http-nio-8086-exec-22:0 =====http-nio-8086-exec-26人数:4 =====http-nio-8086-exec-26版本号:4 =====http-nio-8086-exec-22人数:4 =====http-nio-8086-exec-22版本号:4 =====1 =====更新成功=====http-nio-8086-exec-22:1 =====0 =====更新失败=====http-nio-8086-exec-3:0 =====http-nio-8086-exec-8人数:5 =====http-nio-8086-exec-8版本号:5 =====http-nio-8086-exec-8人数已满:5 =====http-nio-8086-exec-3人数:5 =====http-nio-8086-exec-3版本号:5 =====http-nio-8086-exec-3人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-14:0 =====http-nio-8086-exec-14人数:5 =====http-nio-8086-exec-14版本号:5 =====http-nio-8086-exec-14人数已满:5 =====http-nio-8086-exec-27人数:5 =====http-nio-8086-exec-27版本号:5 =====http-nio-8086-exec-27人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-24:0 =====http-nio-8086-exec-24人数:5 =====http-nio-8086-exec-24版本号:5 =====http-nio-8086-exec-24人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-25:0 =====http-nio-8086-exec-25人数:5 =====http-nio-8086-exec-25版本号:5 =====http-nio-8086-exec-25人数已满:5 =====0 =====更新失败=====http-nio-8086-exec-26:0 =====http-nio-8086-exec-26人数:5 =====http-nio-8086-exec-26版本号:5 =====http-nio-8086-exec-26人数已满:5 =====http-nio-8086-exec-12人数:5 =====http-nio-8086-exec-12版本号:5 =====http-nio-8086-exec-12人数已满:5 =====http-nio-8086-exec-28人数:5 =====http-nio-8086-exec-28版本号:5 =====http-nio-8086-exec-28人数已满:5 =====http-nio-8086-exec-18人数:5 =====http-nio-8086-exec-18版本号:5 =====http-nio-8086-exec-18人数已满:5 =====http-nio-8086-exec-29人数:5 =====http-nio-8086-exec-29版本号:5 =====http-nio-8086-exec-29人数已满:5 =====http-nio-8086-exec-32人数:5 =====http-nio-8086-exec-32版本号:5 =====http-nio-8086-exec-32人数已满:5 =====http-nio-8086-exec-30人数:5 =====http-nio-8086-exec-30版本号:5 =====http-nio-8086-exec-30人数已满:5 =====http-nio-8086-exec-1人数:5 =====http-nio-8086-exec-1版本号:5 =====http-nio-8086-exec-1人数已满:5 =====http-nio-8086-exec-31人数:5 =====http-nio-8086-exec-31版本号:5 =====http-nio-8086-exec-31人数已满:5