一开始想的解决方案:
A、把select和update合并成一条sql
B、事务,用同一个事务包裹select+update的操作。
分析:并不是包上事务就万事大吉。因为如果同时有两个事务都分别select到了相同的记录,那么一样会发生有一方的更新会失败的问题。然后我再想到把事务的隔离级别设置为serializable,但是考虑到性能显然不现实。
我们知道innodb是支持行锁的,然后我就去查看看他的几种锁:读共享锁和写独占锁。
• 读共享锁
是通过SQL的LOCK IN SHARE MODE,例:
Select * from parent where name =‘jones’LOCK IN SHARE MODE;
如果事务A先获得了读共享锁,那么事务B仍然可以做读操作。但是必须等事务A commit或者roll back之后才可以更新或者删除加了读共享锁的行数据。-----但是这种锁并解决不了我们的问题,因为我们应该要再查询的时候就查到事务A commit或者roll back后的数据才是,才是数据别更新后的最新数据。
------如果想要解决上面的并发问题,采用读共享锁是不可以解决的
• 写独占锁
通过SQL的select…for update获得,例:
Select xxx from xxx for update;
Update xxx set xxx = #{xxx};
如果事务A先获得了某行写共享锁,那么事务B就必须等待事务A commit或者roll back之后才可以访问数据。
------如果想要解决上面的并发问题,采用写独占锁是可以解决的
另外这里特别提醒下:UPDATE/DELETE SQL尽量带上WHERE条件并在WHERE条件中设定索引过滤条件,否则会锁表,性能可想而知有多差了。因为
MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。
但是写独占锁是一种悲观锁机制,所以在大佬的指导下还有一种方式,可以避免悲观锁,就是乐观锁!
·乐观锁,类似CAS机制(应该采取的方式)
其实也很简单,首先在select的SQL不作任何修改,然后在update的SQL的where条件中加上select出来的。但是避免不了ABA问题。