-
目录
- 当多个事务并发访问,对同一个数据进行操作
- 数据库锁是一个非常重要的技术,主要实现对数据库的并发控制
从锁的操作粒度划分
行锁
粒度最小;锁定资源导致的争用概率最小,给予尽可能大的并发处理能力;为了满足小的粒度资源锁定,每次获取和释放需要带来更多的消耗;行级锁定
表锁
粒度最大,实现简单,每一次将整个表锁定,锁定粒度大导致资源争用概率非常高,并发程度大打折扣
页锁(基于表锁和行锁之间)
- 粒度界于表锁和行锁之间,并发度一般
- 每个B+树叶子节点存放了多行记录,一个叶子节点就是一页数据,锁定一页的数据,其实锁定的是相邻数据
- 会出现死锁
MyISAM和innoDB
MyISAM:对高并发支持得特别差,只适合报表系统的多查询的场景
- 只提供表锁
- 在执行查询语句(SELECT)前,会自动给表加读锁,允许其他用户的读请求,但不允许写请求
- 在执行更新操作(UPDATE、DELETE、INSERT)前,会自动给表加写锁,不允许其他用户的读和写请求。
innoDB
- 支持表锁、行锁
- 1. InnoDB是通过给索引项加锁,来实现行锁的(因为innoDB中的为聚簇索引,索引和数据是存放在一块的,只需对索引项加锁即可)
- 2。 如果不通过索引条件进行查询,InnoDB使用表锁,行锁不生效
- 3. 行锁是针对索引加锁,不是针对记录加的锁。即使访问的是不同行,但如果它们索引相同,还是会出现锁冲突(比如:索引项中的重复值)
- 4. 即使在查询条件中使用了索引,但用不用索引来查询,取决于MySQL的判断。比如一个很小的表,为全表扫描效率更高,就不MySQL认会使用索引,此时InnoDB将使用表锁,而不是行锁
- 5. 因此在锁冲突的时候,需要检查SQL的执行计划,确定是否使用了索引。
- 6. 在执行查询语句(SELECT)前,基于MVCC(多版本控制)的方式,什么锁都不会加
- 7. 在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给行加写锁,其他用户的写请求无法完成,但通过MVCC(多版本控制)的方式,可以允许读请求。
从锁的实现方式划分
乐观锁:(只需要添加一个字段,而且由程序猿自己去控制)
- l. 乐观锁是指操作数据库,想法很乐观,认为这次的操作不会导致冲突
- 2. 在操作数据时,不加锁
- 3. 在进行更新后,再去判断是否有冲突:有冲突,更新失败;没冲突,更新成功
-
实现方式:数据库没有为我们提供这样的方式,需要程序员自己去实现
- 1. 为数据表添加一个版本(version)字段
- 2. 进行更新操作前,先查询对应的记录,获取version字段的初始值
- select version from table where id = 1
- 3. 进行更新操作时,比较version字段有没有变化
- 4. 如果version没有变化,证明更新的过程中,没有其他用户对其进行操作,则更新成功,将version字段的值加1;
- update table set name = ‘xxx’, version = version + 1 where id = 1 and version = 1
- 5. 如果version有变化,证明更新的过程中,有其他用户对其进行操作,更新失败。
悲观锁
1. 悲观锁就是在操作数据时,认为会出现数据冲突,所以在操作前,获取到锁之后才能进行后续操作
2. 悲观锁耗费较多时间,由数据库实现
InnoDB的行锁是由悲观锁实现的,包括两种:
(1)共享锁
a.又称为读锁
b. 获取到共享锁的事务可以对数据进行读取,但不能修改
c. 如果事务A对数据加上了共享锁,其他事务可以继续加共享锁,但不能加排他锁。(可以有多个共享锁)
d. 直到数据上的共享锁释放后,才可以加排他锁
e. 整个过程保证,拥有共享锁的事务都可以对数据进行读取,但共享锁释放之前不能对数据做任何修改。
在执行语句后面机上lock in share mode 就代表对资源加上共享锁
事务A: select name from table where id = 1 lock in share mode;
事务B: update table set name = ‘xxx’ where id = 1
(2)排他锁
a. 又称为写锁
b. 一个资源,同一时间,只能有一个排他锁
c. 如果事务A对数据加上排他锁,则只允许事务A读取和修改数据,其他任何事务都不能在对数据加任何类型的锁,直到事务A释放数 据上排他锁为止
d. 整个过程保证了,在事务A释放上排他锁之前,其他事务不能再读取和修改数据
e. 使用时,在需要执行的语句后面加上for update(在实际生产环境中要慎用)
select name from table where id = 1 for update;