前言
关键词:悲观锁,乐观锁,表级锁,行级锁,共享锁,排他锁,公平锁,非公平锁
悲观锁
每次获取数据的时候担心数据被修改, 所以每次获取数据的时候都会进行加锁, 确保自己使用过程中数据不会被别人修改, 使用完成后对数据进行解锁. 由于数据进行加锁, 期间对改数据进行读写的其他线程都会进行等待
CREATE TABLE `tb_goods_stock` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`goods_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品ID',
`nums` int(11) unsigned DEFAULT '0' COMMENT '商品库存数量',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `goods_id` (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品库存表';
将商品库存数量nums字段类型设为unsigned,保证在数据库层面不会发生负数的情况。
注意,使用悲观锁,需要关闭
mysql的自动提交功能
,将 set autocommit = 0;
注意,mysql中的行级锁是基于索引
的,如果sql没有走索引,那将使用表级锁把整张表锁住
。
1、开启事务,查询要卖的商品,并对该记录加锁。
begin;
select nums from tb_goods_stock where goods_id = {$goods_id} for update;
2、判断商品数量是否大于购买数量。如果不满足,就回滚事务
。
3、如果满足条件,则减少库存,并提交事务
。
update tb_goods_stock set nums = nums - {$num} where goods_id = {$goods_id} and nums >= {$num};
commit;
begin tran表示开始事务,commit tran表示提交事务,
rollback tran表示回滚事物
乐观锁
乐观锁,简单地说,就是从应用系统层面上做并发控制,去加锁。
实现乐观锁常见的方式:版本号version
实现方式,在数据表中增加版本号字段,每次对一条数据做更新之前,先查出该条数据的版本号,每次更新数据都会对版本号进行更新。在更新时,把之前查出的版本号跟库中数据的版本号进行比对,如果相同,则说明该条数据没有被修改过,执行更新。如果比对的结果是不一致的,则说明该条数据已经被其他人修改过了,则不更新,客户端进行相应的操作提醒。
1、查询要卖的商品,并获取版本号。
begin;
select nums, version from tb_goods_stock where goods_id = {$goods_id};
2、判断商品数量是否大于购买数量。如果不满足,就回滚事务。
3、如果满足条件,则减少库存。(更新时判断当前version与第1步中获取的version是否相同
)
update tb_goods_stock set nums = nums - {$num}, version = version + 1 where goods_id = {$goods_id} and version = {$version} and nums >= {$num};
4、判断更新操作是否成功执行,如果成功,则提交,否则就回滚。
适用场景
悲观锁
比较适合写入操作比较频繁的场景, 如果出现大量的读取操作, 每次读取的时候都会进行加锁, 这样会增加大量的锁的开销, 降低了系统的吞吐量
乐观锁
比较适合读取操作比较频繁的场景, 如果出现大量的写入操作, 数据发生冲突的可能性就会增大, 为了保证数据的一致性, 应用层需要不断的重新获取数据, 这样会增加大量的查询操作, 降低了系统的吞吐量
行锁
访问数据库的时候 针对整个行数据
自动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁。
表锁
访问数据库的时候 针对整个表数据
自动加锁。查询操作(SELECT),会自动给涉及的所有表加读锁,更新操作(UPDATE、DELETE、INSERT),会自动给涉及的表加写锁。
共享锁(读锁)和排他锁(写锁)
共享锁(S锁):共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据
,不能修改数据。
排他锁(X锁):用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。
如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
公平锁 非公平锁
锁的公平与非公平,是指线程请求获取锁的过程中,是否允许插队
。
在公平锁上,线程将按他们发出请求的顺序来获得锁;而非公平锁则允许在线程发出请求后立即尝试获取锁,如果可用则可直接获取锁,尝试失败才进行排队等待。
ReentrantLock提供了两种锁获取方式,FairSyn和NofairSync。结论:ReentrantLock是以独占锁的加锁策略实现的互斥锁,同时它提供了公平和非公平两种锁获取方式。
下一期讲讲偏向锁、轻量级锁、自旋锁、重量级锁
创作不易,如果本篇文章能帮助到你,请给予支持,赠人玫瑰,手有余香,虫虫蟹蟹观众姥爷了