Mysql悲观锁

Mysql悲观锁的意思是当某个线程修改数据的时候,先加锁,再进行业务操作。当其它的线程想要操作该数据的时候则会被阻塞。

Mysql悲观锁和乐观锁

在Mysql中 select for update 是典型的悲观锁的例子,在Java中关键字 synchronized  也是一种悲观锁的实现。

我们来使用 select for update  举一个Mysql悲观锁的例子。在Mysql中事务是自动提交的,所以我们需要将它关闭。

#关闭Mysql自动提交
set autocommit=0;

查看是否设置成功 使用命令 select @@autocommit;

Mysql悲观锁

从上面的截图中,我们看到设置事务为手动提交成功了。

接着,我们创建一张商品表goods,并插入一条数据。

CREATE TABLE `goods` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(60) DEFAULT NULL COMMENT '名字',
  `stock` int(10) DEFAULT NULL COMMENT '库存',
  `state` char(1) DEFAULT NULL COMMENT '状态',
  `add_time` int(10) DEFAULT NULL COMMENT '添加时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 

INSERT INTO `yxjc_test`.`goods` (`id`, `name`, `stock`, `state`, `add_time`) VALUES ('1', '苹果', '100', '1', '1671868732')

注意:这里要设置为InnoDB引擎,MyISAM select for update 会失效。

然后,我们打开mysql两个客户端分别输入:

-- 第一个客户端
begin;
select stock from goods where id =1 for update;
-- 第二个客户端
update goods set stock=stock-1 where id = 1;

效果如图所示:

Mysql悲观锁和乐观锁

Mysql悲观锁和乐观锁

我们看到第二个客户端处于阻塞状态了,在等待第一个客户端的commit完成后,才能进行update操作。

以上便是Mysql最简单的悲观锁例子,需要注意的地方:

  • 如果查询条件用了索引/主键,那么select ..... for update就会进行行锁。
  • 如果是普通字段(没有索引/主键),那么select ..... for update就会进行锁表。

Mysql乐观锁

在Mysql中,乐观锁并不是真正的锁。乐观锁是为了解决悲观锁在并发时,独占死锁的问题。

在Mysql中,我们可以使用版本号的方式来实现逻辑上的乐观锁。在上面的例子中我们增加一个字段version,如下:

CREATE TABLE `goods` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(60) DEFAULT NULL COMMENT '名字',
  `stock` int(10) DEFAULT NULL COMMENT '库存',
  `state` char(1) DEFAULT NULL COMMENT '状态',
  `add_time` int(10) DEFAULT NULL COMMENT '添加时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `yxjc_test`.`goods` (`id`, `name`, `stock`, `state`, `add_time`, `version`) VALUES ('1', '苹果', '100', '1', '1671868732', '1');

在修改库存数据的时候,我们先查询版本号和库存。

select version,stock from goods where id =1;

Mysql悲观锁和乐观锁

这里查询出来的版本号是1,库存是100。

然后我们根据id和版本号修改库存和版本号。

update goods set stock = stock-1,version=version+1 where id=1 and version=1;

Mysql悲观锁和乐观锁

这样可以确认查询出来和修改的数据是一致的。

总结

  • 读多,冲突小,用乐观锁
  • 写多,冲突大,用悲观锁

在面试的时候可能不会直接问什么是乐观锁,可能会问当并发量较大的时候如何解决性能问题,这时我们可以用乐观锁。