场景
第一种方法 悲观锁
悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
简而言之,悲观锁主要用于保护数据的完整性。当多个事务并发执行时,某个事务对数据应用了锁,则其他事务只能等该事务执行完了,才能进行对该数据进行修改操作。
update goods set num = num - 1 WHERE id = 1001 and num > 0
假设现在商品只剩下一件了,此时数据库中 num = 1;
但有100个线程同时读取到了这个 num = 1,所以100个线程都开始减库存了。
但你会最终会发觉,其实只有一个线程减库存成功,其他99个线程全部失败。
需要注意的是,FOR UPDATE
生效需要同时满足两个条件时才生效:
- 数据库的引擎为 innoDB
- 操作位于事务块中(BEGIN/COMMIT)
悲观锁采用的是「先获取锁再访问」的策略,来保障数据的安全。但是加锁策略,依赖数据库实现,会增加数据库的负担,且会增加死锁的发生几率。此外,对于不会发生变化的只读数据,加锁只会增加额外不必要的负担。在实际的实践中,对于并发很高的场景并不会使用悲观锁,因为当一个事务锁住了数据,那么其他事务都会发生阻塞,会导致大量的事务发生积压拖垮整个系统。
第二种办法 乐观锁
select version from goods WHERE id= 1001
update goods set num = num - 1, version = version + 1 WHERE id= 1001 AND num > 0 AND version = @version(上面查到的version);
这种方式采用了版本号的方式,其实也就是CAS的原理。
假设此时version = 100, num = 1; 100个线程进入到了这里,同时他们select出来版本号都是version = 100。
然后直接update的时候,只有其中一个先update了,同时更新了版本号。
那么其他99个在更新的时候,会发觉version并不等于上次select的version,就说明version被其他线程修改过了。那么我就放弃这次update
第三种方法 redis消息队列
在秒杀的情况下,高频率的去读写数据库,会严重造成性能问题。所以必须借助其他服务, 利用redis的单线程预减库存。比如商品有100件。那么我在redis存储一个k,v。例如
每一个用户线程进来,key值就减1,等减到0的时候,全部拒绝剩下的请求。
那么也就是只有100个线程会进入到后续操作。所以一定不会出现超卖的现象。
第四种办法 redis分布式锁
$expire = 10;//有效期10秒
$key = 'lock';//key
$value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期
$lock = $redis->setnx($key, $value);
//判断是否上锁成功,成功则执行下步操作
if(!empty($lock))
{
//下单逻辑...
}
第四种方法,可以参考本书的这个章节:Redis实现分布式锁
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。