Mysql学习(锁篇)

数据库的乐观锁和悲观锁?
MySQL 中有哪几种锁,列举一下?
MySQL中InnoDB引擎的行锁是怎么实现的?
MySQL 间隙锁有没有了解,死锁有没有了解,写一段会造成死锁的 sql 语句,死锁发生了如何解决,MySQL 有没有提供什么机制去解决死锁

锁,是计算机协调多个进程或者线程并发访问某一资源的机制

在数据库中,除了传统的计算资源的争用外,数据也是一种供需要用户共享的资源。数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问边得有序所设计的一种规则。

使用锁可以对有限的资源进行保护,解决隔离和并发的矛盾

锁的分类

从对数据操作的类型分类:

  • 读锁(共享锁): 针对同一份数据,多个读操作可以同时进行,不会互相影响
  • 写锁(排他锁):当前写操作没有完成前,她会阻断其他写锁和读锁。

从对数据操作的粒度分类

  • 表级锁: 开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低
  • 行级锁:开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度也最高
  • 页面锁:开销和枷锁时间解与表锁和行锁之间,会出现死锁,锁定粒度解与表锁和行锁之前,并发度一般
  行锁 表锁 页锁
MyISAM    
BDB  
InnoDB  
Memory  

 

MyISAM表锁

myISAM的表锁有两种模式:

  •    表共享读写:不会阻塞其他用户对同意表的读请求,但会阻塞对一个表的写请求
  •    表独占写锁:会阻塞其他用户对同一表的读和写操作

MyISAM表的读操作和写操作之间,以及写操作之间是串行的,当一个线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作,其他线程的读写操作都会等待,直到锁被释放为止。

默认情况下,写锁比读锁具有更高的优先级,当一个锁释放时,这个锁会优先给写锁队列中等候的获取锁请求,然后再给读锁对联中等候的获取锁请求

InnoDB行锁

InnoDB实现了一下两种类型的行锁:

  • 共享锁(s):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
  • 排他锁(x):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁

为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁

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

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

索引失效会导致行锁变表锁。比如 vchar 查询不写单引号的情况

加锁机制

乐观锁与悲观锁时两种并发控制的思想,可用于解决丢失更新问题

乐观锁会“乐观地”假定大概率不会发生并发更新冲突,访问、处理数据过程中不加锁,只在更新数据时再根据版本号或时间戳判断是否有冲突,有则处理,无则提交事务。用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式

悲观锁会“悲观地”假定大概率会发生并发更新冲突,访问、处理数据前就加排他锁,在整个数据处理过程中锁定数据,事务提交或回滚后才释放锁。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

锁模式(InnoDB有三种行锁的算法)

  • 记录锁(Record Locks):单个行记录上的锁,对索引项枷锁,锁定符合条件的行,其他事务不能i需改和删除加锁项。
select * from table where id - 1 for update;

他会在id  = 1 的记录上加上记录锁,以阻止其他事务插入,更新,删除id= 1 这一行。

在通过主键索引与唯一索引对数据行进行update操作时,也会对该行数据加记录锁

-- id列为主键列或者唯一索引列

update set age = 50 where id  = 1;

  • 间隙锁(Gap Locks):当我们使用范围条件而不是相等条件检索数据,并请求共享或排它锁时,InnoDB会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内但并不存在的记录,叫做“间隙”,InnoDB也会对这个间隙加锁,这种锁机制就是所谓的间隙锁

      对索引项之间的“索引”加锁,锁定记录的范围(对第一条记录前的间隙或者最后一条记录后的间隙加锁)不包含索引本身,其他事务不能在锁范围内插入数据,这样就防止了别的事务新增幻影行,间隙锁基于非唯一索引,它锁定一段范围内的索引记录,间隙锁基于下面将会提到的Next-key locking算法,请务必牢记 :使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每条数据。

select * from table where id between 1 and 10 for update

 即所在(1,10)区间内的记录行都会被锁住,所有id 为 2,3,4,5,6,7,8,9的数据行的插入都会被阻塞,但是1和10两条记录行并不会锁住。

GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。

  • 临键锁(Next-key Locks):临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间,(临键锁的主要目的,也是为了避免幻读),如果把事务的隔离级别减低为RC,临键锁则也会失效

Next-key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法,通过临键锁可以解决幻读的问题,每个数据行上的非唯一索引列上都会存在一把临建锁,当某个事务持有该数据行的临建锁时,会锁住一段左开右闭得数据,需要强调的一点时,InnoDB中行级锁时基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。

 

select for update有什么含义,会锁表还是锁行还是其他

for update 仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效,在进行事务操作时,通过 for update 语句,Mysql会对查询结果集中每行数据都添加排他锁。其他线程对该记录的更新于删除操作都会阻塞,排他锁包含行锁,表锁

InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁,假设有个表单products,里面有id跟name两个栏位,id是主键

明确指定主键,并且有此笔资料 rowlock 行锁

SELECT * FROM products WHERE id='3' FOR UPDATE;
SELECT * FROM products WHERE id='3' and type=1 FOR UPDATE;

 

明确指定主键,若查无此笔资料,无lock

select * from products where id = '-1' for update

无主键,table lock 表锁

SELECT * FROM products WHERE name='Mouse' FOR UPDATE;

主键不明确,table lock 表锁

select * from products where id<>'3' for update;

主键不明确,table lock 表锁

select * from products where id like '3' for update;

MySQL 遇到过死锁问题吗,你是如何解决的?

死锁

死锁产生:

  • 死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环
  • 当事务试图以不同的顺序锁定资源时,就可能产生死锁,多个事务同时锁定同一个资源时也可能会产生死锁。
  • 锁的行为和顺序和存储引擎祥光,以同样的顺序执行语句,有些存储引擎会产生死锁有些不会,死锁有双重愿意:真正的数据冲突,存储引擎的实现方式

检测死锁: 数据库系统实现了各种死锁检测和死锁超时的机制,InnoDB存储引擎能检测到死锁的循环依赖并立即返回一个错误。

死锁恢复:死锁发生以后,只有部分或完全管回滚其中一个事务,才能打破死锁,InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚,所以事务型应用在设计时必须考虑如何处理死锁,多数情况下只需要重新只能怪隐私所回滚的事务即可。

死锁影响性能:死锁会影响性能而不是会产生错误,i那位InnoDB会自动检测死锁状况并回滚其中一个受影响的事务,在高并发系统上,当许多线程等待同一个锁时,死锁检测可能导致速度变慢,有事当发生死锁时,禁用死锁检测可能会更有效,这时可以以来innodb_lock——wait_timeout 设置进行事务回滚

InnoDB避免死锁

  • 为了在单个InnoDB表上执行多个并发写入操作时避免死锁,可以在事务开始时通过为预期要修改的每个元组(行)使用SELECT ... FOR UPDATE语句来获取必要的锁,即使这些行的更改语句是在之后才执行的。

  • 在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁,而不应先申请共享锁、更新时再申请排他锁,因为这时候当用户再申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁

  • 如果事务需要修改或锁定多个表,则应在每个事务中以相同的顺序使用加锁语句。在应用中,如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会

  • 通过SELECT ... LOCK IN SHARE MODE获取行的读锁后,如果当前事务再需要对该记录进行更新操作,则很有可能造成死锁。

  • 改变事务隔离级别

如果出现死锁,可以用 show engine innodb status;命令来确定最后一个死锁产生的原因。返回结果中包括死锁相关事务的详细信息,如引发死锁的 SQL 语句,事务已经获得的锁,正在等待什么锁,以及被回滚的事务等。据此可以分析死锁产生的原因和改进措施。

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/Chen_leilei/article/details/112475832