mysql 的悲观锁和乐观锁
mysql 的悲观锁和乐观锁
如果客户端在同一时间内访问同一条数据对数据进行操作时,这时候就会产生数据不一致的问题,我们可以使用mysql的加锁机制,这样可以在一定程度上解决并发访问的问题,mysql中有两种常见的锁,分别为悲观锁和乐观锁,悲观锁和乐观锁是一种机制不是指具体的锁。
一:悲观锁:
悲观锁,正如其名,它指的是对数据被外界修改持悲观的态度,认为数据是不安全的,总有人在修改,在需要操作数据时,会先将数据锁住,然后在操作数据。
悲观锁通过常用的select … for update操作来实现悲观锁,悲观锁的流程(一锁二查三更新):
举个栗子:
假设商品表中有一个字段quantity表示当前该商品的库存量。假设有一件Dulex套套,其id为100,quantity=8个;如果不使用锁,那么操作方法
/step1: 查出商品剩余量
select quantity from items where id=100;
//step2: 如果剩余量大于0,则根据商品信息生成订单
insert into orders(id,item_id) values(null,100);
//step3: 修改商品的库存
update Items set quantity=quantity-1 where id=100;
这种写法在高并发环境下可能出现问题,导致库存为负数的情况(产品多卖)
如下:
//step1: 查出商品状态
select quantity from items where id=100 for update;
//step2: 根据商品信息生成订单
insert into orders(id,item_id) values(null,100);
//step3: 修改商品的库存
update Items set quantity=quantity-2 where id=100;
需要注意的是: 当我执行select quantity from items where id=100 for update后。如果我是在第二个事务中执行select quantity from items where id=100(不带for update)仍能正常查询出数据,不会受第一个事务的影响。另外,MySQL还有个问题是select...for update语句执行中所有扫描过的行都会被锁上,因此在MySQL中用悲观锁务必须确定走了索引,而不是全表扫描,否则将会将整个数据表锁住。
悲观锁并不是适用于任何场景,它也存在一些不足,因为悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。如果加锁的时间过长,其他用户长时间无法访问,影响了程序的并发访问性,同时这样对数据库性能开销影响也很大,特别是对长事务而言,这样的开销往往无法承受,这时就需要乐观锁。
2. 乐观锁
实现:
//step1: 查询出商品信息
select (quantity,version) from items where id=100;
//step2: 根据商品信息生成订单
insert into orders(id,item_id) values(null,100);
//step3: 修改商品的库存
update items set quantity=quantity-1,version=version+1 where id=100 and version=#{version};
service具体实现:
int tryTimes = 0;
while (true) {
tryTimes++;
if (更新数据库的返回值int != 0) {
// 说明更新成功,直接退出
break;
}
if (tryTimes == 200) {
// 达到最大重试次数,退出
break;
}
try {
// 休息一段时间后再重试
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
LOGGER.info("tryTimes: {}", tryTimes);
}