提示:可以关注下 java中的锁 https://www.yuque.com/wangchao-volk4/fdw9ek/ugvg6y

一、悲观锁

当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制。

悲观锁,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)

  1. //0.开始事务
  2. begin;/begin work;/start transaction; (三者选一就可以)
  3. //1.查询出商品信息(id=1的这条数据加锁)
  4. select status from t_goods where id=1 for update;
  5. //2.根据商品信息生成订单
  6. insert into t_orders (id,goods_id) values (null,1);
  7. //3.修改商品status为2
  8. update t_goods set status=2;
  9. //4.提交事务
  10. commit;/commit work;

二、乐观锁

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
乐观锁采取了更加宽松的加锁机制。也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。乐观锁的实现:
1、CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
2、版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。
乐观锁的 缺陷:由于和CAS实现一样,所以自然就带来了ABA问题

sql的实现方式,加了个version

  1. 1.查询出商品信息
  2. select (status,status,version) from t_goods where id=#{id}
  3. 2.根据商品信息生成订单
  4. 3.修改商品status2
  5. update t_goods set status=2,version=version+1
  6. where id=#{id} and version=#{version};

三、表锁

image.png
image.png
image.png

四、行锁

image.png

1、行锁演示

image.png
image.png

2、行锁变成表锁

如下,a b 字段都加了索引,b是varchar类型的,a是int类型
image.png
1、索引类型使用不对,会从行锁变成表锁
Session1:update xxxxx set a=41 where b=4000,并且没有及时commit的话,此时就会变成表锁
session2:将会阻塞,一直等待Session1 commit之后,才会执行
image.png

3、如果锁定一行记录

  1. Begin;
  2. select * from xxxxx where a=8 for update;
  3. commit之前,这条记录无法被修改
  4. commit

image.png

五、间隙锁

看如下图片,session_1进行返回where a>1 and a<6 更新的时候,innoDB会锁住这个范围内的记录(即使现在不存在)
session_2进行insert的时候,由于此时session_1没有commit,所以session_2就会阻塞。
image.png
image.png