MySQL中的锁

MySQL 中的锁分类

锁粒度:也就是锁的级别高低
锁的使用方式细分为:共享锁,独占锁(排他锁)
按照锁粒度来划分:行锁,表锁,页锁
思想上的锁:悲观锁,乐观锁
InnoDB中的行锁又细分为:
Record Lock(当前索引行记录锁)、Gap Lock(间隙锁)、Next-key Lock(行锁和间隙锁的结合)

1.行锁

MySQL中锁粒度最小的一种锁,只能针对当前操作行进行加锁,开销大,加锁慢,会出现死锁,锁冲突的概率低,并发度高

共享锁用法(同一个时刻锁可以被多个线程获取):
假设事务 A 对数据 B 进行加共享锁,则事务 A 可以对数据 B 进行读取但是不能修改,其他事务能对 B 进行读操作,不能进行写操作,直到事务 A 释放锁为止
加锁格式:

select ... lock in share mode;

独占锁用法(同一个时刻一个锁只能被一个线程所获取):
假设事务 A 对数据 B 进行加独占锁写锁,那么事务 A 可以修改数据 B 也可以读取数据 B ,但是其他事务不能读取也不能修改数据 B
加锁格式:

select ... for update
2.表锁

表锁是 MySQL 中粒度最大的一种锁,对当前操作的整张表进行加锁,开销小,加锁快,不会出现死锁,锁冲突的概率高,并发度低

共享锁用法:

LOCK TABLE table_name [ AS alias_name ] READ

独占锁用法:

LOCK TABLE table_name [AS alias_name][ LOW_PRIORITY ] WRITE

解锁方式:

unlock tables;
3.页锁

页锁是 MySQL 中粒度介于行锁和表锁粒度之间的一种锁,BDB支持页级锁,会出现死锁,并发度一般

4.乐观锁和悲观锁
乐观锁

假设在一般情况下不会发生冲突,让操作先执行,在要提交的时候进行检查,对事务中要修改的数据进行检查,如果该数据和之前开启事务获取到的数据相同,说明事务执行的中间过程没有其他事务对数据进行修改,那么就成功提交事务,如果发现不同,说明数据在中间受到过其他事务的修改,那么就返回错误信息,让用户决定下一步操作
乐观锁内部是通过 CAS 机制来实现的
实现方法:
不会使用数据库提供的锁机制,而是通过版本号或者时间戳来检查数据是否发生了修改

悲观锁

假设事务之间很容易发生锁冲突和锁竞争的关系,所以就要保持保守的态度提前做好准备,提前进行加锁,直到该锁释放或者异常退出时,其他竞争的事务才能获取到锁
悲观锁实现的过程:
对任意记录修改时,为该记录先加上排他锁,如果加排他锁失败,那么说明其他线程正在执行该任务,那么根据具体的响应做出下一步的操作,如果加排他锁成功,那么就执行任务,其他线程此时不能对该锁对象操作,直到任务结束或者异常退出
悲观锁优点和问题:
优点很明显,通过提前加锁保证了安全,但是效率很低,如果数据量很大并且需要对每一行数据进行操作时,就会造成长时间的等待,这样不仅增加了死锁的概率,还降低了并发性

MyISAM 和 InnoDB 中的锁机制

InnoDB即支持表锁又支持行锁,InnoDB 默认使用行锁,但是 MyISAM 只支持表锁

MyISAM

MyISAM 中的表锁同样有共享和独占两种使用方式,并且共享和独占锁的使用特点和之前讲述相同
(1)如果先加了共享锁,那么该事务只可以对加锁的数据进行读操作,不能进行写操作,并且此时其他事务只能对该数进行读操作,不能进行写操作
(2)如果先加了独占锁,那么该事务即可以对加锁数据进行写操作也可以进行读操作,其他事务不可以对数据进行任何操作

如何加表锁:
MyISAM 进行查询操作时,自动加共享锁(读锁),进行更新操作时,自动加独占锁(写锁)
MyISAM之所以不会产生死锁的原因,它是对整个表进行加锁,所以对表内数据进行操作时就不会产生死锁

MyISAM的锁调度
Mysql认为写请求一般比读请求重要,即使读请求先到等待队列,写请求后到,也是写请求优先执行。因此,MyISAM表不适合于有大量更新操作和查询操作的应用,因为大量更新操作会造成查询操作长时间阻塞。

InnoDB行锁及其实现方式

InnoDB行锁中也是存在共享锁(S)和独占锁(X)的使用方式
还存在意向共享锁(IS)和意向独占锁(IX),并且意向锁是 InnoDB 自动添加的,不需要我们来设置,要获取共享锁或者独占锁之前必须要先获取到相应的意向锁,这样就可以提前帮我们判断能不能对该操作加锁,提高了效率

InnoDB行锁实现方式:

MySQL中定义行锁只能锁索引,而不能直接锁数据,索引分为主键索引和非主键索引,如果一条 SQL 语句直接对主键索引进行操作,那么就直接锁定该主键索引,如果一条 SQL 语句对非主键索引进行操作,那么就先锁定该非主键索引,,再锁定相关主键索引
这和 InnoDB 的索引结构有关系,实现的数据结构是 B+ 树结构,
如果是通过主键 key 来构建索引,那么该主键索引的叶子节点中储存的就是该主键所在行的数据并且根据 B+ 树的性质按照顺序排列好了,这也是 InnoDB 的索引文件和数据文件是连在一起的原因,通过锁住该叶子节点的主键就能锁住该行信息,
如果是根据非主键构建索引,那么非主键索引结构的叶子节点中储存的就是对应的主键,先锁住该非主键然后再次到主键索引中寻找叶子节点中主键的位置,然后再锁定行数据,
简而言之,就是主键索引的叶子叶子节点直接就储存了该主键对应的行数据,那么就可以直接锁定行数据,但是是非主键索引的话,它的叶子叶子节点只储存了对应的主键,然后需要再次检索主键索引,找到该主键的行数据信息,最终锁定行数据
因为 InnoDB 自身的数据文件就是一个索引文件,所以 InnoDB 只能通过对索引加锁实现行锁,否则只能通过表锁来加锁

InnoDB 的行锁又细分为:Record Lock(当前索引行记录锁)、Gap Lock(间隙锁)、Next-key Lock(行锁和间隙锁的结合)

Record Lock(当前索引行记录锁):也就是只锁该索引对应的行数据记录,即锁定一条记录
Gap Lock(间隙锁):在索引的间隙上加锁,这也是为什么 Repeatable Read 的隔离级别下能够防止幻读的原因
例如:

mysql> select * from product_copy;
+----+--------+-------+-----+
| id | name   | price | num |
+----+--------+-------+-----+
|  1 | 伊利   |    68 |   1 |
|  2 | 蒙牛   |    88 |   1 |
|  6 | tom    |  2788 |   3 |
| 10 | 优衣库 |   488 |   4 |
+----+--------+-------+-----+
其中id为主键 num为普通索引
窗口A:
mysql> select * from product_copy where num=3 for update;
+----+------+-------+-----+
| id | name | price | num |
+----+------+-------+-----+
|  6 | tom  |  2788 |   3 |
+----+------+-------+-----+
1 row in set

窗口B:
mysql> insert into product_copy values(5,'kris',1888,2);
这里会等待  直到窗口A commit才会显示下面结果
Query OK, 1 row affected

但是下面是不需要等待的
mysql> update product_copy set price=price+100 where num=1;
Query OK, 2 rows affected
Rows matched: 2  Changed: 2  Warnings: 0
mysql> insert into product_copy values(5,'kris',1888,5);
Query OK, 1 row affected

该例子中:num是普通索引,虽然锁的是 num = 3 的索引,但是最终都会通过主索引层层寻找,找到 B+ 树主索引结构中叶子节点对应储存的行数据,也就是说通过叶子节点 key 对应的主键,锁住该主键所在行数据,那么此时 id=6 所在行被锁定(独占锁),对第一条记录前的间隙或最后一条记录后的间隙加锁,即锁定一个范围的记录,不包含记录本身,主键索引和唯一索引的值只有一个,所以不会出现幻读的情况

Next-key Lock:锁定一个范围的记录并包含记录本身(上面两者的结合)

Next-Key Lock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。如果一个间隙被事务T1加了锁,其它事务是不能在这个间隙插入记录的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值