美文网首页
表锁和行锁

表锁和行锁

作者: 无力韬韬 | 来源:发表于2020-11-21 11:13 被阅读0次

MySQL中的锁总体可以分为悲观锁和乐观锁。
悲观锁MySQL中有自带的锁。乐观锁需要自己写程序控制来实现乐观锁的功能。

悲观锁

表级锁

表锁

表锁属于mysql服务层,手动添加。

  • 读锁:
lock table mylock read;

加读锁后,可以继续加读锁,但不能加写锁。
当一个线程拥有了读锁之后,那么当前的线程只能访问加锁了的表。
其他线程既可以访问加锁的表,也可以访问其他的表。
但是,其他线程如果想要修改加了读锁的表中的内容,必须要等当前线程释放表锁。

UNLOCK tables;
  • 写锁:
lock table mylock write;

加了写锁之后,其他进程既不能修改也不能查询加锁的表。

元数据锁

元数据锁属于MySQL服务层,自动添加。
元数据锁是自动添加,顾名思义,是用来保护表的元数据的。
在进行增删改查的操作时,加元数据读锁。在进行DDL操作时,即对表的结构进行修改的时候加元数据写锁。
读锁是为了,当一个线程正在对表进行增删改查,不允许其他进程修改表的结构。
写锁则是为了当一个线程正在修改表的结构时,其他线程既不能访问表的数据也不能修改表的结构。
元数据锁在事务提交之后才会释放。

begin; //Navicat中开启事务
select * from mylock;//Navicat中查询表中数据
mysql> alter table mylock add f int;//命令行中修改表结构,发生阻塞。
commit;//Navicat中提交事务
Query OK, 0 rows affected (2 min 37.60 sec)//命令行中提示修改完成
Records: 0  Duplicates: 0  Warnings: 0
意向锁

属于Innodb存储引擎层。内部使用。

意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。

意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

意向锁的主要作用是为了【全表更新数据】时的性能提升。否则在全表更新数据时,需要先检索该表是否某些记录上面有行锁。

行级锁(InnoDB实现)

行级锁是由Innodb存储引擎实现的。
行级锁按锁定范围来说分为三种:

记录锁

锁定索引中的一条记录。

间隙锁

锁定记录前,记录中,记录后的行。
RR隔离级(可重复读)--MySQL默认隔离级别

Next-Key锁

记录锁+间隙锁

按功能来说分为两种:

共享读锁

允许一个事务去读一行,阻止其他事务活动相同数据集的排他锁。
也就是说允许当前事务或其他事务读,不允许其他事务写。

SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE -- 共享读锁 手动添加
select * from table -- 无锁
排他写锁

允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
也就是允许当前事务写,不允许其他事务读或者写。
排他写锁对于UPDATE DELETE INSERT 语句,InnoDB会自动给涉及数据集加排他锁。
手动加:

SELECT * FROM table_name WHERE ... FOR UPDATE
两阶段锁

锁操作分为两个阶段:加锁阶段与解锁阶段
加锁阶段与解锁阶段不相交。加锁阶段只加锁不放锁。放锁阶段只放锁不加锁。
加锁和解锁不能在一个sql语句中实现。

行锁演示

InnoDB行锁是通过给索引上的索引项加锁来实现的,因此只有通过索引条件检索的数据,InnoDB才会使用行级锁,否则InnoDB将使用表锁。

show status like 'innodb_row_lock%';//查看行锁状态
  • Innodb_row_lock_current_waits:当前正在等待锁定的数量;
  • Innodb_row_lock_time:从系统启动到现在锁定总时间长度;
  • Innodb_row_lock_time_avg:每次等待所花平均时间;
  • Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间;
  • Innodb_row_lock_waits:系统启动后到现在总共等待的次数;
begin;
select * from mylock where id=1 lock in SHARE MODE;//在Navicat中开启事务,但未提交
//此时在id=1的行已经被加了行共享读锁。
命令行中可以访问id为1的行
命令行中可以修改其他行的数据
命令行中修改id=1的行等待锁释放时间超时

如果使用了没有索引的列,行锁会升级为表锁
上面是读锁,下面分析写锁。

BEGIN;
SELECT * from mylock where id=1 for update;//同样在Navicat中加上写锁。
//对于 UPDATE DELETE INSERT 语句来说,InnoDB会自动加行写锁。
//而SELECT语句则需要手动加 
直接访问是可以访问的,但是加读锁就会堵塞

select 没有锁的时候即使要查询的行已经有写锁了还是能访问。但是加上了行读锁就发生阻塞了。

间隙锁

间隙锁的产生:
主键索引的范围 ,辅助索引的等值和范围。


表结构
begin;
update news set number=3 where number=4;//Navicat中操作

此时的id间隙范围为[1,6),number范围是[2,5),类型是辅助索引的等值。

start transaction ;//命令行中操作
insert into news value(2,3);#(均在间隙内,阻塞)
insert into news value(7,8);#(均在间隙外,成功)
insert into news value(2,8);#(id在间隙内,number在间隙外,成功)
insert into news value(4,8);#(id在间隙内,number在间隙外,成功)
insert into news value(7,3);#(id在间隙外,number在间隙内,阻塞)
insert into news value(7,2);# (id在间隙外,number为上边缘数据,阻塞)
insert into news value(2,2);#(id在间隙内,number为上边缘数据,阻塞)
insert into news value(7,5);#(id在间隙外,number为下边缘数据,成功)
insert into news value(4,5);#(id在间隙内,number为下边缘数据,阻塞)

只要number在间隙内,就一定会阻塞。如果number在下边缘数据并且id在间隙内的话也会阻塞。

start transaction ;
update news set number=3 where number=13 ;

此时number间隙锁范围(11,无穷)。
此时只有当id也满足间隙锁范围,number=11时才会发生阻塞。

两阶段锁

两阶段锁☞锁的产生和释放是两个阶段,锁在语句执行完毕之后不会立即释放,而是在事务提交之后才释放。

死锁

由于两阶段锁的特性,会产生死锁的问题。



由于线程1先获得了行id=1的锁,线程2获得了 id=2的锁,并且都没有提交,此时锁都没有释放。
当线程1再去修改行id=2的信息时,就会发生阻塞,同时线程2想修改行id=1的信息,同样被阻塞,两个线程都在等对方释放锁,形成了死锁。

死锁检测

发生死锁后,有两种解决策略。
一种是直接进入等待直至超时。超时的事务回滚。超时的时间限制可以通过innodb_lock_wait_timeout设置。
另一种就是死锁检测。也就是主动检测是否时锁,一旦发现死锁就回滚导致死锁的某一条事务。
死锁的检测。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
采用第一种策略没有额外的性能消耗,但是等待的时间过长影响使用体验,太短又容易误杀其他没有死锁只是在等待锁释放的事务。
第二种策略可以根据是否死锁回滚事务,发现死锁就回滚,但是寻找死锁的过程需要额外的性能消耗,每当一个事务被锁的时候,就要看看它所依赖的线程有没有被别人锁住,如此循环,最后判断是否出现了循环等待,也就是死锁。
例如:新来的线程F,被锁了后就要检查锁住F的线程(假设为D)是否被锁,如果没有被锁,则没有死锁,如果被锁了,还要查看锁住线程D的是谁,如果是F,那么肯定死锁了,如果不是F(假设为B),那么就要继续判断锁住线程B的是谁,一直走知道发现线程没有被锁(无死锁)或者被F锁住(死锁)才会终止。

相关文章

  • InnoDB引擎的行锁和表锁

    在Mysql中,Innodb数据引擎支持行锁和表锁,Myisam数据引擎只支持表锁 1 行锁和表锁 行锁是通过索引...

  • 总结Mysql中的锁

    MySQL中的锁 概述 MyISAM支持表锁,InnoDB支持表锁和行锁,默认为行锁 表级锁:开销小,加锁快,不会...

  • MySQL知识点

    Mysql中有哪几种锁? MyISAM支持表锁,InnoDB支持表锁和行锁,默认为行锁 表级锁:开销小,加锁快,不...

  • (MySQL死锁认识二)数据库中的锁

    数据库中的锁按照不同的方式区分有不同的结果,按照锁的粒度区分,可分为表级锁和行级锁 表锁和行锁 表锁意为对整张表加...

  • MySQL锁机制

    MySQL主要有表锁,行锁和页锁,页锁用得少,本文主要介绍表锁和行锁。 一、锁的分类 从对数据的操作类型来分,可以...

  • MySQL听讲(四)——数据库锁

    数据库的锁分为全局锁、表锁、行锁。 科普 锁的缩写:MDL(metadata lock):元数据锁。 全局锁 表锁 行锁

  • FOR UPDATE的使用

    利用select * for update 可以锁表/锁行。自然锁表的压力远大于锁行。所以我们采用锁行。什么时候锁...

  • MySQL--锁

    MySQL 锁 锁的类型 行锁粒度最小的锁,存在死锁。 页锁粒度在行锁和表锁之间的锁。 表锁粒度较大的锁,不存在死...

  • 校招面试题mysql锁总结

    目录 锁定义 锁分类 读锁和写锁 表锁和行锁 InnoDB共享锁和排他锁 InnoDB意向锁和排他锁 InnoDB...

  • 表锁和行锁

    MySQL中的锁总体可以分为悲观锁和乐观锁。悲观锁MySQL中有自带的锁。乐观锁需要自己写程序控制来实现乐观锁的功...

网友评论

      本文标题:表锁和行锁

      本文链接:https://www.haomeiwen.com/subject/hgujiktx.html