乐观所和悲观锁策略
- 悲观锁:在读取数据时锁住那几行,其他对这几行的更新需要等到悲观锁结束时才能继续 。
- 乐观所:读取数据时不锁,更新时检查是否数据已经被更新过,如果是则取消当前更新,一般在悲观锁的等待时间过长而不能接受时我们才会选择乐观锁
1.悲观锁:
在mysql中实现悲观锁的方式:select * from ...for update
<select id="getForUpdate" parameterType="String" resultType="ContractVO">
select
<include refid="column_list"/>
from t_contract a
<include refid="column_join"/>
where a.id = #{0} for update
</select>
service层代码:其中getDataById查询语句sql中没有for update ,getForUpdate查询语句中带有for update
@Override
@Transactional
public void getForUpdate(String id) throws Exception {
ContractVO contractVO = dao.getDataById(id);
//dao.getForUpdate(id);
if(Integer.parseInt(contractVO.getContractType()) > 0){
contractVO.setContractType(String.valueOf(Integer.parseInt(contractVO.getContractType()) + 1));
dao.update(contractVO);
} else {
throw new Exception("更新失败");
}
}
controller层代码:
@RequestMapping(value = "testUpdate")
@ResponseBody
public ResultMsg<Object> testUpdate(String id){
ResultMsg<Object> rs = new ResultMsg<Object>();
for(int i=0;i<27;i++){
new Thread(() ->{
try {
iContractService.getForUpdate(id);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
log.error(e.getMessage());
}
}).start();
}
return rs;
}
在数据库中有一个原始值为1的字段,按照正常情况下27条线程执行完后的值应该为28.
使用postman测试,如果service中使用getDataById查询,那么数据库中得值不定。
如果使用getForUpdate查询,则执行完成后,数据库中的值为28。由此可以证明加锁成功。
2.乐观锁
乐观锁的实现机制是在表中加一个字段当做版本号,每次更新的时候去判断你获取到的这个值是不是等于数据当前存的值,如果两个值不一样,那么证明当前更新的数据不是最新的。
sql如下:
update table set content = #{content},edit = edit +1 where id = #{id} and edit = #{edit}
sercive层代码:
@Override
@Transactional(rollbackFor = Exception.class)
public void getForUpdate(String id) throws Exception {
ContractVO contractVO = dao.getDataById(id);
//dao.getForUpdate(id);
if(Integer.parseInt(contractVO.getContractType()) > 0){
contractVO.setContractType(String.valueOf(Integer.parseInt(contractVO.getContractType()) + 1));
int count = dao.update(contractVO);
if(count == 0){
throw new Exception("更新失败");
}
}
}
count为更新的行数,当更新行数为0时,抛出异常。