乐观锁和悲观锁

简介

通过此文你可以了解到的内容有:

  1. 什么是乐观锁和悲观锁

首先要了解什么是锁:mysql为了解决资源共享,所造成并发问题的处理机制。

悲观锁

在操作之前给数据添加锁,锁定后再对数据进行操作。在锁未释放之前,其他人无法对数据进行读写。

特点:

  1. 可以保证数据的完全独占性和正确性
  2. 开销大,性能不高

添加锁的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*读锁*/
lock table userinfo read;
/*写锁*/
lock table userinfo write;
/*解锁*/
unlock table;


/*行读锁,条件必须为索引列,否则会自动转换为表锁*/
select * from table where id =1;
/*提交解锁*/
commit;
/*回滚解锁*/
rollback;

实操

预备数据

1
2
3
4
5
6
7
8
9
10
11
12
drop database if exists deom;

create database demo;

drop table if exists lock_demo1;

create table lock_demo1 (
id bigint(11) primary key,
number int(2) default 0
)engin=innodb default charset=utf8;

insert into lock_demo1 values(1,1);

未添加悲观锁

会话一

1
2
3
4
5
6
7
8
9
10
set autocommit = 0;
/*①添加读锁*/
select * from lock_demo1 where id = 1;
+--------+
| number |
+--------+
| 1 |
+--------+
/*③更新数量*/
update lock_demo1 set number = number-1 where id = 1;

会话二

1
2
3
4
5
6
7
8
9
/*②查询数量*/
select * from lock_demo1 where id = 1;
+--------+
| number |
+--------+
| 1 |
+--------+
/*④更新数量*/
update lock_demo1 set number = number-1 where id = 1;

操作顺序:1—>2—>3—>4

以前情况经常出现在秒杀系统中,2个或者2个以上的用户在争夺最后一件商品,如果没有加锁则会出现第二个会话更新后,number 将等于 -1,造成业务错误。

添加悲观锁

会话一

1
2
3
4
5
6
7
8
9
/*①添加读锁*/
select * from lock_demo1 where id = 1 for update;
+--------+
| number |
+--------+
| 1 |
+--------+
/*③更新数量*/
update lock_demo1 set number = number-1 where id = 1;

会话二

1
2
3
4
5
6
7
8
9
/*②查询数量*/
select * from lock_demo1 where id = 1 for update;
+--------+
| number |
+--------+
| 1 |
+--------+
/*④更新数量*/
update lock_demo1 set number = number-1 where id = 1;

操作顺序:1—>2—>3—>4

当会话一添加了锁之后,会话二添加锁的操作将处于等待,直到会话一进行解锁。从而避免了资源争夺造成的并发问题。

乐观锁

在操作数据的时候,不会对数据进行锁定。只有在提交数据的时候,会采用一种机制来验证数据是否冲突。

特点:

  1. 并发度高
  2. 开销小
实操

预备数据

1
2
3
set aotocommit=1;

alter table lock_demo1 add column version int(2) default 0;

会话一

1
2
3
4
5
6
7
8
9
/*1*/
select version , number from lock_demo1 where id =1;
+----+--------+---------+
| id | number | version |
+----+--------+---------+
| 1 | 3 | 0 |
+----+--------+---------+
/*3*/
update lock_demo1 set number = number -1,version=version+1 where id = 1 and version = 0;

会话二

1
2
3
4
5
6
7
8
9
/*2*/
select version , number from lock_demo1 where id =1;
+----+--------+---------+
| id | number | version |
+----+--------+---------+
| 1 | 3 | 0 |
+----+--------+---------+
/*4*/
update lock_demo1 set number = number -1,version=version+1 where id = 1 and version = 0;

执行顺序:1–>2–>3–>4,此时会话2的更新会失败。

由于在提交修改的时候,添加了version字段进行验证,从而保证数据的有效性和正确性,将错误的处理情况让用户进行处理,mysql不添加锁,从而减少了资源消耗,提高了并发。

0%