锁的分类

按锁粒度划分:表级锁行级锁页级锁

按锁使用方式划分:共享锁排他锁

按思想上划分:悲观锁乐观锁

共享锁和排他锁

共享锁(shared lock),也称读锁(read lock),线程之间相互不阻塞,多个客户端在同一时刻读取同一资源,互不干扰。

排他锁(exclusive lock),也称写锁(write lock),顾名思义,就是排他的,一个写锁会阻塞其他线程的读锁和写锁。

锁粒度

表级锁

表级锁是 MySQL 中最基本的锁策略,并且是开销最小的策略。它会锁定整张表,其他线程无法对表进行写操作(插入、删除、修改等),需要先获得写锁,这会阻塞其他线程对该表的所有读写操作。只有没有写锁时,其他线程才能获得读锁,读锁之间不相互阻塞的。

行级锁

行级锁可以最大程度地支持并发处理,它仅对所选择的记录进行加锁,其他线程可以对同一表中的其他记录进行操作。

页级锁

页级锁是介于行级锁和表级锁之间的一种锁。表级锁速度快,但冲突多;行级锁速度慢,但冲突少。所以取了折衷的页级,一次锁定相邻的一组数据。

InnoDB 既支持行级锁,也支持表级锁,默认是行级锁。

简单总结:

表级锁:开销小,加锁快,锁粒度大,冲突大,并发度最低,不会出现死锁。

行级锁:开销大,加锁慢,所粒度最小,冲突小,并发度最高,会出现死锁。

页级锁:开销、加锁、锁粒度介于表级锁和行级锁之间,并发度一般,会出现死锁。

悲观锁和乐观锁

无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。

悲观锁

悲观锁指的是数据被外界修改持保守态度,每次读取数据时都认为数据会被修改。因此,在整个数据处理过程中,将数据处于锁定状态,直到锁被释放。

悲观锁的实现,往往是依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正的保证数据访问的排他性,否则,即使在本系统实现了加锁机制,也无法保证外部系统不会修改数据)。

注意:要使用悲观锁,必须关闭 MySQL 的自动提交属性,MySQL 默认使用 autocommit 模式,当执行一个更新操作后,MySQL 会立即将结果提交。

以下是 MySQL 实现悲观锁的方式:

使用命令设置 MySQL 为非 autocommit 模式:

set autocommit=0;

处理业务:

-- 1.开启事务
begin;
-- 2.查询
SELECT goods_name,goods_number,stock FROM goods WHERE id = 1 FOR UPDATE;
-- 3.生成订单
INSERT INTO order(id, goods_id) VALUES(null,1);
-- 4.修改商品库存
UPDATE goods SET stock = 1 WHERE id = 1;
-- 5.提交事务
commit;

在上述的 SQL 中,使用了 SELECT ... FOR UPDATE 来实现悲观锁,当发生并发时,同一时刻只有一个线程可以开启事务并获取 id=1 这条记录的锁,其他事务必须等本次事务提交之后才能执行。

值得注意的是,MySQL InnoDB 默认是行级锁,行级锁都是基于索引的,如果 SQL 语句中没有用到索引是不会使用行级锁的,会使用表级锁将整张表锁住。

乐观锁

乐观锁假设认为数据一般情况下不会发生冲突,所以在数据进行提交更新时才会对数据的冲突与否进行检测,如果发送冲突,则返回错误信息给用户,让用户决定如何去做。

乐观锁的实现不需要依赖数据库的锁机制,实现方式有两种:数据版本和时间戳。

以下是使用数据版本实现的乐观锁:

SELECT version,goods_name,goods_number,stock from goods where id = 1 
UPDATE goods SET stock = stock - 1,version = version + 1 WHERE goods_id = 1 AND version=<旧版本号>

如以上的 SQL 中,乐观锁每次在执行数据修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改并对版本号 +1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号之后增加不会减少。

另外一种方式就是时间戳,时间戳天然具有顺序递增性,实现方式类似数据版本 ,这里不再赘述。

两者使用场景:

  1. 悲观锁依赖数据库锁机制,效率比较低,适合于写入频繁、强一致性的场景。
  2. 乐观锁效率高,但更新失败的概率比较高,适用于一些并发量比较大的场景。

参考文献

[1]. 《高性能MySQL》

[2]. 数据库中的乐观锁与悲观锁

[3]. 高并发问题处理研究:Select for update使用解析:悲观锁与乐观锁、行锁与表锁

文章目录