MySQL锁总结

2020-05-28  本文已影响0人  星空怎样

[toc]

前言

锁是MySQL在服务器层和存储引擎的并发控制
加锁是消耗资源的,锁的各种操作,包括获得锁,检测锁是否已解除,释放锁等。

锁机制

MySQL不同的存储引擎支持不同的锁机制,所有的存储引擎都以自己的方式显现了锁机制,服务器层完全不了解存储引擎中的锁实现:

默认情况向下,表锁和行锁都是自动获得的,不需要额外的命令。

但是在有的情况下,用户需要明确进行锁表或者进行事务控制,以便确保整个事务的完整性,这样就需要使用事务控制和锁定语句来完成。

不同粒度锁的比较

MyISAM表锁

MyISAM表级锁模式:

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

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

这也正是MyISAM表不适合有大量更新操作和查询操作的应用,因为大量的更新操作会造成查询操作很难获取锁,从而可能永远阻塞,同时,一些需要长时间运行的查询操作,也会使写线程饿死,应用中应尽量避免出现长时间查询操作(有可能的情况下可以通过使用中间表等措施对SQL语句做一定的分解,使每一步查询都能在较短时间完成,从而减少锁冲突,如果复杂查询不可避免,应尽量安排在数据库空闲时间段执行,比如一些定期统计可以安排在夜间执行)。

MyISAM加锁方法

MyISAM在执行查询语句(SELECT)前,会自动给涉及的表加读锁,在执行更新操作(UPDATE、DELETE、INSERT)前,会自动给涉及的表加写锁,这个过程并不需要用户的干预,因此用户一般不需要直接使用LOCK TABLE命令给MyISAM表显示加锁。

在自动加锁的情况下,MyISAM总是一次获得SQL语句需要的全部锁,这也正是MyISAM表不会出现死锁的原因。

MyISAM存储引擎支持并发插入,以减少给定表的读和写操作之间的争用:

如果MyISAM表在数据文件中间没有空缺块,则行始终插入数据文件插入数据文件的末尾,在这种情况下,你 可以自由混合并发使用MyISAM表的INSERT和SELECT语句而不需要加锁可以在其他线程进行读操作,同时将行插入到MyISAM表中,文件中间有空闲块可能是从表格中间删除或更新的行产生的,如果文件中间有空缺块,则并发插入会被禁用,但是当所有空闲块都填充有新数据时,他会自动重新启用,要控制此行为,可以使用MySQL的concurrent_insert系统变量。

如果使用LOCK TABLES显示获取表锁,则可以请求READ LOCAL锁而不是READ锁,以便在锁定表时,其他会话可以使用并发插入。

查询表级锁争用情况

可以通过检查table_locks_waited和tabl_locks_immediate状态变量来分析系统上的表锁的争夺,如果table_locks_waited的值比较高,则说明存在这比较严重的表级锁争用情况。

InnoDB行级锁和表级锁

InnoDB锁模式

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

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

锁模式的兼容情况:


image

如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之如果两者不兼容,该事物就要等待锁释放。

InnoDB加锁方法

锁只有在执行commit或者rollback的时候才会释放,并且所有锁同一时刻释放

select ... lock in share mode //共享锁
select ... for update //排它锁

select for update:在执行这个select查询语句的时候,会将对应的索引访问条目加上排它锁(X锁),也就是说这个语句对应的锁就相当于update带来的效果。

所使用场景:为了让自己查到的数据确保是最新数据,并且查到后的数据只允许自己来修改的时候,需要用到for update字句。

select lock in share mode:inshare mode子句的作用就是将查找到的数据加上一个share锁,这个就是表示其他事务只能对这些数据进行简单的select操作,并不能进行DML操作。

使用场景:为了确保自己查到的数据没用被其他的事务正在修改,也就是说确保查到是最新数据,并不允许其他人修改数据,但是自己不一定能够修改数据,因为有可能其他事物也对这些数据使用了in share mode当方式上了S锁。

性能影响:select for update语句,相当于一个update语句,在业务繁忙的时候,如果事务没有及时commit或者rollback可能会造成其他事务长时间的等待,从而数据库的并发使用效率。select lock in share mode语句时一个给查询数据上了一个共享锁(S锁)功能,它允许其他事务也对改数据上S锁,但是不能够允许对改数据进行修改,如果不及时commit或者rollback也可能造成大量的事务等待。

for update和lock in share mode区别:

前一个上的是排它锁(X锁),一旦一个事务获取了这个锁,其他事务是没法在这些数据上执行for update;后者是共享锁,多个事务可以同时对相同数据执行lock in share mode。

InnoDB行锁的实现方式

InnoDB的间隙锁

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

很显然,在使用范围条件检索并锁定记录时,InnoDB这种锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待,因此,在实际开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新呢数据,避免使用范围条件。

InnoDB使用间隙锁的目的:

MySQL通过BINLOG录入执行成功的INSERT、UPDATE、DELETE等更新数据的SQL语句,并由次实现数据库的恢复和主从复制。MySQL的恢复机制(复制其实就是在Slave MySQL不断做基于BINLOG恢复)有以下特点:

由此可见,MySQL的恢复机制的要求,在一个事务未体检前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读。

InnoDB在不同隔离级别下的一致性读及锁的差异:

锁和多版本数据(MVCC)是InnoDB实现一致性读和ISO/ANSI SQL92隔离级别的手段。

因此在不同的隔离级别下,InnoDB处理SQL时采用的一致性读策略和需要的锁是不同的:

image
image

对于许多SQL,隔离级别越高,InnoDB给记录集加的锁就越严格(尤其是使用范围条件的时候),产生锁冲突的可能性也就越高,从而对并发性事务处理性能的影响也就越大。

因此,我们在应用中,应该尽量使用较低的隔离级别,以减少锁竞争的几率。

LOCK TABLES 和UNLOCK TABLES

MySQL也支持lock tables和un lock tables,这都是在服务器层(MySQL Service层)实现的,和存储引擎无关,他们有自己的用途,并不能替代事务的处理。

LOCK TABLES语法:

正确的方式见如下语句:
例如,如果需要写表t1并从表t读,可以按照如下去做:

set autocommit=0;
lock tables t1 where ,t2 read,...;
[do something with tables t1 and t2 here]; 
commit;
unlock tables;

使用LOCK TABLES的场景

给表显示加表级锁(InnoDB表和MyISAM都可以),一般为了在一定程度模拟事务操作,实现对某一时间点多个表的一致性读取。(与MyISAM默认的表锁行为类似)
在用LOCK TABLES给表显示加表锁时,必须同时获取所有涉及到表的锁,并且MySQL不支持锁升级,也就是说,在执行LOCK TABLES后,只能访问显示加锁的这些表,不能访问未加锁的表;童年故事如果加的是读锁,那么只能执行查询操作,而不能执行更新操作。

其实,在MyISAM自动加锁(表锁)的情况下也大致如此,MyISAM总是一次获得SQLy语句所需要的全部锁,这正是MyISAM表不会出现死锁的原因。

死锁

MyISAM避免死锁:

在自动加锁的情况下,MyISAM总是一次获得SQL语句所需要的全部锁,所以MyISAM表不会出现死锁。

InnoDB避免死锁

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

一些优化锁性能的建议

乐观锁和悲观锁

乐观锁

乐观苏:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性,乐观锁不能解决脏读问题。
乐观锁,顾名思义,就是很乐观,每次去拿数据的时候都会认为别人不好修改数据,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号的机制,乐观锁适用于读多的应用,这样可以提供吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供乐观锁。

悲观锁

假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

悲观锁,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会加上锁,这样别人想拿这个数据就会block直到拿到锁,传统的关系数据库里面就用到了这种锁机制,比如行锁,表锁,读锁,写锁等,都是在操作之前先上锁。

上一篇 下一篇

猜你喜欢

热点阅读