执行SELECT语句时没有加锁,只有在执行UPDATE时才进行加锁的。所以才会出现并发操作时的更新数据不一致
a = SELECT * FROM table1 WHERE id=1;
UPDATE table1 SET num = a.num + 1 WHERE id=1;
解决的方法可以有2种:
通过事务显式的对SELECT进行加锁
使用乐观锁机制
对SELECT进行加锁的方式有两种,如下:
SELECT ... LOCK IN SHARE MODE #共享锁,其它事务可读,不可更新
SELECT ... FOR UPDATE #排它锁,其它事务不可读写
默认情况下SELECT语句是不会加锁的。并且对于上面提到的场景,必须使用排它锁。另外,上面的2种语句只有在事务之中才能生效,否则不会生效。在MySQL命令行使用事务的方式如下:
SET AUTOCOMMIT=0; BEGIN WORK; a = SELECT num FROM table1 WHERE id=2 FOR UPDATE; UPDATE table1 SET num = a.num + 1 WHERE id=2; COMMIT WORK;
只要以后更新数据时,都使用这样事务来进行操作;那么在并发的情况下,后执行的事务就会被堵塞,直到当前事务执行完成。(通过锁把并发改成了顺序执行)
乐观锁
在具体更新数据的时候更新条件中会添加版本号信息,
- 当版本号没有变化的时候说明该数据行未被更新过,并且也满足更新条件,所以会更新成功。
- 当版本号有变化的时候,则无法更新数据行,因为条件不满足,此时就需要在进行一次SQL操作。(重新查询记数据行,再次使用新的版本号更新数据)