为什么需要锁?
有一个资源正在被操作的时候,不希望被其它人操作,此时就需要通过加锁来防止这种情况的出现。
不同线程互斥的访问共享资源,才能保证共享资源被操作的顺序性,。
锁是保证并发情况下数据一致性的一种工具方法,没有锁机制的作用就会产生冲突。
典型冲突:
更新丢失:一个事务覆盖了另一个事务的更新结果。
脏读:一个事务读取到其它完成一半事务的记录。
悲观锁又名 “悲观并发控制”,Pessimistic Concurrency Control(PCC)。
顾名思义,这种方式下对数据的修改持悲观态度,认为数据被并发修改的概率比较大,采取 “先加锁再修改” 的保守策略。
适用场景:
悲观锁比较重,适用于业务一致性要求较高的场景,例如:库存、费用等。
悲观锁不适用于并发较高的场景,一个事务锁定了某行数据,其它事务必须等待该事务结束,这样就影响了业务的执行效率。
Optimistic Locking,这种方式下认为数据一般不会造成冲突,所以在数据提交更新的时候才会对数据的冲突与否进行检测。
如果发生冲突,则返回错误信息,让用户决定如何处理。
适用场景:
乐观锁锁适合用于更新频率不高的场景,一般的业务管理系统并发要求不会太高,比较适合使用乐观锁。
高并发的场景中使用乐观锁并不合适,因为会产生大量的失败,应该想其它办法解决这种大量失败问题。
对数据进行修改前,尝试为该记录加上排他锁(exclusive locking)。
加锁失败,则表明该数据正在被修改,需要等待或者抛出异常。
MySql Innodb 引擎:
//0.开始事务
begin;
//1.查询出商品库存信息(适用 for update 对数据加锁)
select quantity from items where id=1 for update;
//2.修改商品库存为2
update items set quantity=2 where id = 1;
//3.提交事务
commit;
Compare and Swap 技术,冲突检测和数据更新。
当多个线程尝试更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,可以再次尝试。
// 查询出商品库存信息,quantity = 3
select quantity from items where id=1;
// 修改商品库存为2
update items set quantity=2 where id=1 and quantity = 3;
修改的时候,如果库存数据和之前的查询到的库存数据不一致,则说明数据已经过期,已经被别人修改过了。
比如说一个线程1从数据库中取出库存数3,这时候另一个线程2也从数据库中库存数3,并且线程2进行了一些操作将库存数变成了2,紧接着又将库存数变成3,这时候线程1进行CAS操作发现数据库中仍然是3,然后线程1操作成功。尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的。
解决方法:通过一个单独的可以顺序递增的version字段。
//查询出商品信息,version = 1
select version from items where id=1
//修改商品库存为2
update items set quantity=2,version = 3 where id=1 and version = 2;
乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加不会减少。
除了 version 以外,还可以使用时间戳,因为时间戳天然具有顺序递增性。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。