MySQL中的乐观锁和悲观锁

2018-04-28  本文已影响13人  黄二的NPE

当我们购买一件商品时,一般会有以下几个操作:

  1. 查询商品信息
  2. 将商品信息和用户信息插入订单表
  3. 更新商品信息,比如状态更新为已被购买

当单条线程执行这些操作的时候肯定不会出现问题,但是当高并发的时候,会出现商品会重复购买的情况.这时候我们就要加锁了.
goods

CREATE TABLE `goods` (
  `id` int(11) NOT NULL COMMENT '主键id',
  `name` varchar(32) DEFAULT NULL COMMENT '商品名称',
  `status` tinyint(3) DEFAULT NULL COMMENT '商品状态 0:未被购买 1: 已被购买',
  `version` int(11) DEFAULT NULL COMMENT '版本号,每次更新版本号 + 1',
  `create_time` bigint(20) DEFAULT NULL COMMENT '创建时间(精确到毫秒)',
  `update_time` bigint(20) DEFAULT NULL COMMENT '更新时间(时间戳,精确到毫秒)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品表';

orders

CREATE TABLE `orders` (
  `id` int(11) NOT NULL COMMENT '注解id',
  `goods_id` int(11) DEFAULT NULL COMMENT '商品id',
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `status` tinyint(4) DEFAULT NULL COMMENT '订单状态 0:刚创建  1:下单完成 -1:下单失败',
  `create_time` bigint(20) DEFAULT NULL COMMENT '创建时间',
  `update_time` bigint(20) DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表';


加锁可分为乐观锁和悲观锁

  • 乐观锁

乐观锁,顾名思义,它的想法比较乐观,认为每次查询都不会涉及到更新,所以只有到更新的时候再判断查询到的记录是否发生变化,如果没有发生变化就执行更新操作,如果已经发生了变化就不执行更新操作.

怎么判断数据是否发生变化

  1. 给表添加一个updateTime字段,每次更新的时候先判断updateTime是否与查询到updateTime一致,如果一致就更新数据,并且更新updateTime,否则不更新
//查询id = #id 商品 goods
SELECT id, name, status, create_time, update_time, version  FROM GOODS WHERE id = '#id';
//插入订单表
INSERT INTO ORDERS VALUES(id, goods_id, user_id,...);
//更新goods状态,这里的update_time是上面查到的goods的update_time
UPDATE GOODS SET status = 1, update_time = now()  WHERE id = '#id' AND update_time = '#update_time';
  1. 给表添加一个version字段,每次更新的时候仙判断version是否和查询到的version一致,如果一致就更新数据,并且更新version = version + 1,否则不更新(操作和update_time更新相似,只是update_time改成version)
  • 悲观锁

悲观锁,顾名思义,就是想法比较悲观,它认为每次查询都可能涉及到更新,所以在查询的时候就给记录加上锁,其它线程就不会执行更新操作了,只有等释放锁了才能执行更新操作.

怎么给记录加锁

  1. 共享锁(读锁,S锁)
SET AUTOCOMMIT = 0;
BEGIN;
SELECT id, name, status, create_time, update_time, version  FROM GOODS WHERE id = '#id' LOCK IN SHARE  MODE;
COMMIT;

在mysql事务中查询语句后面加上 lock in share mode,即可得到记录的共享锁;什么叫做共享呢?就是当共享锁还没释放的时候,其他查询用lock in share mode也可以得到记录的共享锁.那共享锁有什么用呢?共享锁被占用的记录不能被更新或者不能被select ... for update 得到排它锁,否则会被阻塞.

  1. 排它锁
SET AUTOCOMMIT = 0;
BEGIN;
SELECT id, name, status, create_time, update_time, version  FROM GOODS WHERE id = '#id' FOR UPDATE;
COMMIT;

在mysql事务中查询语句后面加上 for update 或者 更新记录的时候,即可得到记录的排它锁;什么叫做排他锁呢?排它锁被占用的记录,不允许其他线程获得他的排它锁,甚至共享锁,就是说记录不能被更新或者 其他线程的for update 到,否则会阻塞.直到排它锁释放了才能继续执行.

注意,不管是共享锁还是排它锁,当我们用不加锁的select 查询记录的时候也是可以查询到的.

上一篇 下一篇

猜你喜欢

热点阅读