美文网首页
MySQL中的乐观锁和悲观锁

MySQL中的乐观锁和悲观锁

作者: 黄二的NPE | 来源:发表于2018-04-28 15:42 被阅读13次

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

    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 查询记录的时候也是可以查询到的.

    相关文章

      网友评论

          本文标题:MySQL中的乐观锁和悲观锁

          本文链接:https://www.haomeiwen.com/subject/cnzvlftx.html