# 乐观锁

乐观锁大多是基于数据版本记录机制实现,一般是给数据库表增加一个 "version" 字段。读取数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

比如下单操作:

  1. 查询出商品信息。

    select (quantity, version)
    from t_goods
    where id = #{id}
  2. 根据商品信息生成订单。

  3. 将商品数量减 1。

    update t_goods
    set quantity = quantity - 1
    where id = #{id} and version = #{version}

# 悲观锁

悲观锁依靠数据库提供的锁机制实现。MySQL 中的共享锁和排它锁都是悲观锁。数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。

# 共享锁(读锁)

共享锁指的就是对于多个不同的事务,对于一个资源共享同一个锁。对某一资源加共享锁,自身可可读该资源,其他人也可以读该资源(也可以再加共享锁,即共享锁共享多个内存),但无法修改。要想修改就必须等所有共享锁都释放完之后。语法: select * from table lock in share mode;

比如:

  1. 窗口 1,在一个未结束的事务中给一条数据加上共享锁。

    BEGIN;
    SELECT * FROM t_red_packet WHERE id = 1 LOCK IN SHARE MODE;
  2. 窗口 2,给同一条数据加上共享锁,加锁成功。

    SELECT * FROM t_red_packet WHERE id = 1 LOCK IN SHARE MODE;
  3. 窗口 1 和窗口 2,更新该行数据,提示 [Err] 1205 - Lock wait timeout exceeded; try restarting transaction 。需要等到所有共享锁释放,才可以进行 update 操作。

    UPDATE t_red_packet
    SET user_id = 2

# 排它锁(写锁)

排它锁指的就是对于多个不同的事务,对同一个资源只能有一把锁。对某一资源加排它锁,自身可以进行增删改查,其他人无法进行加锁操作,更无法进行增删改操作。语法: select * from table for update

  1. 窗口 1,在一个未结束的事务中给一条数据加上排它锁。

    BEGIN;
    SELECT * FROM t_red_packet WHERE id = 1 FOR UPDATE;
  2. 窗口 1,更新该行数据,成功。

    UPDATE t_red_packet
    SET user_id = 2
  3. 窗口 2,查询该行数据,可以查询到。

    SELECT * FROM t_red_packet WHERE id = 1
  4. 窗口 2 给该条数据加锁,提示 [Err] 1205 - Lock wait timeout exceeded; try restarting transaction

    SELECT * FROM t_red_packet WHERE id = 1 FOR UPDATE;

综上,共享锁就是大家一起来读,一起来共享锁,但谁都不要对锁着的数据进行修改,排它锁就是我自己就是想来修改,你们可以读,但你们都不能到锁,也不能对数据进行修改。

# 行锁

行锁就是给一行数据进行加锁。

# 表锁

表锁就是对一张表进行加锁。