悲观锁 乐观锁 行锁 表锁 共享锁 排他锁 公平锁

image

前言

关键词:悲观锁,乐观锁,表级锁,行级锁,共享锁,排他锁,公平锁,非公平锁

image

悲观锁

每次获取数据的时候担心数据被修改, 所以每次获取数据的时候都会进行加锁, 确保自己使用过程中数据不会被别人修改, 使用完成后对数据进行解锁. 由于数据进行加锁, 期间对改数据进行读写的其他线程都会进行等待

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是以独占锁的加锁策略实现的互斥锁,同时它提供了公平和非公平两种锁获取方式。

下一期讲讲偏向锁、轻量级锁、自旋锁、重量级锁

image

创作不易,如果本篇文章能帮助到你,请给予支持,赠人玫瑰,手有余香,虫虫蟹蟹观众姥爷了

你可能感兴趣的:(mysql,sql,sqlserver,数据库,java)