Mysql悲观锁
Mysql悲观锁的意思是当某个线程修改数据的时候,先加锁,再进行业务操作。当其它的线程想要操作该数据的时候则会被阻塞。
在Mysql中 select for update 是典型的悲观锁的例子,在Java中关键字 synchronized 也是一种悲观锁的实现。
我们来使用 select for update 举一个Mysql悲观锁的例子。在Mysql中事务是自动提交的,所以我们需要将它关闭。
#关闭Mysql自动提交
set autocommit=0;
查看是否设置成功 使用命令 select @@autocommit;
从上面的截图中,我们看到设置事务为手动提交成功了。
接着,我们创建一张商品表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;
效果如图所示:
我们看到第二个客户端处于阻塞状态了,在等待第一个客户端的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;
这里查询出来的版本号是1,库存是100。
然后我们根据id和版本号修改库存和版本号。
update goods set stock = stock-1,version=version+1 where id=1 and version=1;
这样可以确认查询出来和修改的数据是一致的。
总结
- 读多,冲突小,用乐观锁
- 写多,冲突大,用悲观锁
在面试的时候可能不会直接问什么是乐观锁,可能会问当并发量较大的时候如何解决性能问题,这时我们可以用乐观锁。