1、乐观锁与悲观锁

1.1 乐观锁

  • 乐观锁指:在当前线程/操作数据的过程中,其它线程/操作不会修改数据,不会引起数据冲突、仅在数据提交的时候对数据进行检测

  • 适用场景:适合读取大于写入

  • 实现方式:

    • 1、使用 version 方式
    • 2、使用 timestamp (时间戳 ) 方式
  • 实现方式:

    • 查询数据的时候,将 version/timestamp 查询出来,
    • 修改数据的时候,将 version/timestamp 的值作为更新条件,并且增加 version 或 更新 timestamp
      • 如果操作成功,则表明数据在此次操作中,没发生冲突
      • 如果操作失败,则表示数据在此次操作中,发生冲突
  • 例如:

      begin;
      select * from goods where id=1;
      //根据库存,创建订单
      insert into orders (good_id,num) values (xx,1);
      //更新产品存库
      update goods set stock=stock-1,version=version+1 where id=1 and version=`old_version_value`
      commit/rollback;
    

1.2 悲观锁

  • 悲观锁指:在当前线程/操作处理数据的过程中,其它线程/操作会修改数据,引起数据冲突、所以需要对数据进行锁定,防止其它操作修改数据
  • mysql 自己已实现悲观锁
  • 使用之前请使用 set autocommit=0 关闭自动提交,并在 transaction(事务)中使用
  • 分为 共享/读锁排他/写锁
  • 默认的 DDL(insert/update/delete) 会使用 排他/写锁
  • 适用场景:适合写入大于读取
1.2.1 共享/读锁
  • 多个线程可同时对数据加上 共享/读锁

  • 使用 select .... lock in share mode 进行锁定

  • 锁定成功之后,其它线程/操作能同时对数据进行 共享/读锁 锁定,但不能进行 排他/写锁

  • 例如:

      set autocommit=0;
      begin;
      select * from goods where id=1 lock in share mode;
      commit/rollback;
    
1.2.2 排他/写锁
  • 同一时刻仅一个线程/操作能对数据加上 排他/写锁

  • 使用 select .... for update 进行锁定

  • 锁定成功之后,其它线程/操作对数据进行 共享/读锁排他/写锁 时将阻塞

  • 获取之后,可以对数据进行写入/修改

  • 例如:用户下单,修正库存

      begin;
      select * from goods where id=xx for update;
      //根据库存,创建订单
      insert into orders (good_id,num) values (xx,1);
      //更新产品存库
      update goods set stock=stock-1 where id=1;
      commit/rollback;
    

1.3 总结

  1. insert/update/delete 将对数据进行 排他/写锁
  2. select 语句默认不会对数据进行锁定
  3. 要记住锁机制一定要在事务中才能生效,事务也就要基于MySQL InnoDB 引擎。
  4. 访问量不大,不会造成压力时使用悲观锁,面对高并发的情况下,我们应该使用乐观锁。
  5. 读取频繁时使用乐观锁,写入频繁时则使用悲观锁。
  6. 乐观锁不能解决脏读的问题。

2、行锁与表锁

2.1 行锁

  • innodb 支持行锁,myisam 不支持行锁
  • 行锁基于索引,如果 SQL 锁定语句应用不到索引,不会使用行锁,将使用表锁

2.2 表锁

  • innodb/myisam 都支持表锁
  • innodb 中,如果 SQL 锁定语句应用到索引,使用 行锁,否则使用 表锁
  • myisam 中使用 sql 锁定语句对数据进行锁定时,全部使用 表锁