当我们与数据库打交道时,并发是经常要进行处理的,同时对一条记录进行读取并更新时,虽有先后顺序,但就如同我们审核任务一样,几个人拿到同一个任务,同时评分1分2分3分,最终这个任务的分数是由最后提交的给更新了。当更新完毕,再次拿到打分时,会校验已经打过分了,不允许评分。问题就出现在同时更新时。
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
Read uncommitted:读未提交的数据
Read committed: 读提交
Repeatable read:不可重复读
Serializable: 序列化
大多数据库使用Read committed,这是性能和安全的结合点,并使用加锁的方式来解决安全问题。
解决此类问题有悲观锁和乐观锁两种方式,
悲观锁:A查询出这条任务时就会拿到一把锁,这把锁是更新锁,其他人依然可以查出这条任务,但是在A更新之前的过程中,后查询出任务的BCD等不可以进行更新,更新就会报错。
乐观锁:A查询出这条任务时,拿到这条任务的一个版本号version,没更新一次version++,当A进行更新时,一旦发现数据库版本和自己拿到的版本不对,就报错,否则正常更新。
例子:
悲观锁:LockMode.UPGRADE实现加锁
Task a = (Task)session.load(Task.class, 1, LockMode.UPGRADE);
乐观锁:在需要加锁的类里加一个version字段,注解@Version
private int version;
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
常用的是乐观锁,悲观锁效率太低,一旦有人拿到任务不进行更新,那就意味着所有人都会受影响。