隔离级别与丢失更新

ISO的隔离级别的划分并不完美,比如典型的丢失更新问题就不在其考虑范围内(以下内容以MySQL InnoDB为例)

InnoDB默认是repeatable read级别,但是默认的select仍然会导致丢失更新(lost update)

如果需要避免这个问题,需要使用select … for update 或者 select … in share mode

举个例子:

SELECT c FROM t WHERE a = 10;

int counter = getInt(); // 获取select的返回值

UPDATE t SET c = counter + 1 WHERE a = 10; // 将counter传入下一个sql语句

这个例子就会导致丢失更新问题,因为另一个事务随时都可以改写当前这条记录,比如当前任务执行update的时候,另一个事务可能已经把c更新过了

如果把语句改为:

SELECT c FROM t WHERE a = 10 FOR UPDATE;

int counter = getInt(); // 获取select的返回值

UPDATE t SET c = counter + 1 WHERE a = 10; // 将counter传入下一个sql语句

这样在select之后,update结束之前,其他事务就无法修改这个记录(当然还需要整个事务的前后加上begin … commit,或者Spring的@transaction注解)