5.MySQL中的锁

什么是锁?MySQL 中提供了几类锁?
  锁是实现数据库并发控制的重要手段,可以保证数据库在多人同时操作时能够正常运行。MySQL 提供了全局锁、行级锁、表级锁。其中 InnoDB 支持表级锁和行级锁,MyISAM 只支持表级锁。



详解锁



全局锁:

  对整个数据库实例加锁,MySQL提供了一个加全局读锁的命令:【Flush tables with read lock】-FTWRL。当需要让整个库处于只读状态,可使用这个命令。其他线程进行:增删改、数据定义(建表、修改表)和更新类事务的提交语句都会被阻塞。
该锁的典型使用场景是:全库逻辑备份,即把整库所有表都select出来存成文本。
  通过FTWRL将整库只读,可能会导致两个问题:

  • 主库上备份时,备份期间都不能执行更新,业务会停摆。
  • 从库上备份时,备份期间从库不能执行主库同步过来的binlog,会导致主从延迟。

  备份加锁是为了避免系统备份得到的库与主库不是同一个逻辑时间点,数据不一致。
如何处理逻辑备份时,整个数据库不能插入的情况?
  通过MySQL自带的逻辑备份工具mysqldump,使用参数-single-transaction时,导数据之前会启动一个事务(隔离级别是可重复读),来确保拿到一致性视图,由于MVCC的支持,这个过程中数据是可以正常更新的。但是这种备份方式需要引擎能支持这个隔离级别,如MyISAM不支持事务,则备份需要用FTWRL命令。
FTWRL设置的全库只读与set global readonly = true(设置数据库只读)的区别:

  • 在有些系统,readonly的值会被用来做其他逻辑,如用来判断一个库是主库还是备库。因此修改global变量的方式影响更大。
  • 异常处理机制上存在差异,如果用FTWRL命令后客户端发生异常断开,那么MySQL会自动释放全局锁,整库回到可以正常更新的状态。而设置readonly后,如果客户端异常断开,数据库将一直保存readonly状态,这样会导致整库长时间处于不可写状态,风险较高。


表级锁:
  MySQL里表级锁有两种:表锁和元数据锁(meta data lock,MDL)
表锁:
  语法:lock tables ...read/wirte。可用unlock tables主动释放锁,客户端断开时也会主动释放。
  注意:lock tables会限制别的线程读写,也会限制本线程接下来的操作对象。如果A线程执行lock table t1 read,t2 write。则其他线程写t1,读t2都会被阻塞,同时,线程A在释放表锁之前,也只能执行读t1,读写t2的操作,写t1也不行,也不能访问其他表。
  对于 InnoDB 这种支持行锁的引擎,一般不使用 lock tables 命令来控制并发,毕竟锁住整个表的影响面还是太大。
MDL:
  MDL在访问一个表时会被自动加上 ,其作用是保证读写正确性。在并发情况下维护数据的一致性。MySQL5.5引入MDL,当对一个表做增删改查时,加MDL读锁,当对表结构进行变更时,加MDL写锁。
  读锁之间不互斥,保证多个线程同时对一张表。写锁之间是互斥的,用来保证变更表结构操作的安全性,因此,两个线程同时给一个表加字段,其中一个要等到另一个执行完才能开始执行。
  注意:事务中MDL锁,在语句执行开始时申请,但在语句结束后不会立即释放,而是等到整个事务提交后再释放。在DML与DDL操作交互时,如果客户端有查询重试机制,就容易产生session爆满,导致内存增高。
  例子:sessionA对表t1进行查询,此时表t1会加MDL读锁,此时sessionC发起DDL操作请求,但A的MDL读锁为释放,导致C申请MDL写锁被阻塞,后面如果有其他的session发起查询申请MDL读锁,都会被堵塞,当客户端发起重试时,查询过多。会导致大量session被阻塞,导致内存升高。



行锁:
  行级锁是 MySQL 中粒度最小的一种锁,他能大大减少数据库操作的冲突。InnoDB 的行级锁有共享锁(S LOCK)和排他锁(X LOCK)两种。共享锁允许事物读一行记录,不允许任何线程对该行记录进行修改。排他锁允许当前事物删除或更新一行记录,其他线程不能操作该记录。
  共享锁:SELECT ... LOCK IN SHARE MODE,MySQL 会对查询结果集中每行都添加共享锁,前提是当前线程没有对该结果集中的任何行使用排他锁,否则申请会阻塞。
  排他锁:select * from t where id=1 for update,其中 id 字段必须有索引,MySQL 会对查询结果集中每行都添加排他锁,在事物操作中,任何对记录的更新与删除操作会自动加上排他锁。前提是当前没有线程对该结果集中的任何行使用排他锁或共享锁,否则申请会阻塞。
针对两阶段协议该注意:
  在InnoDB事务中,行锁是在需要的时候才加上,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。
  根据两阶段协议,当遇到事务中需要锁多个行时,要把最可能造成冲突、最可能影响并发度的锁尽量往后放。



死锁:
  是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的过程称为死锁。
常见的死锁案例:

将投资的钱拆封几份借给借款人,这时处理业务逻辑就要把若干个借款人一起锁住
    select * from xxx where id in (xx,xx,xx) for update。
批量入库,存在则更新,不存在则插入。
    解决方法 insert into tab(xx,xx) on duplicate key update xx=‘xx‘。

如何查看死锁?

  使用命令 show engine innodb status 查看最近的一次死锁。InnoDB Lock Monitor 打开锁监控,每 15s 输出一次日志。使用完毕后建议关闭,否则会影响数据库性能。
对待死锁常见的两种策略:
  通过 innodblockwait_timeout 来设置超时时间,一直等待直到超时;
  发起死锁检测,发现死锁之后,主动回滚死锁中的某一个事务,让其它事务继续执行;
如何避免死锁?

  • 为了在单个 InnoDB 表上执行多个并发写入操作时避免死锁,可以在事务开始时通过为预期要修改的每个元祖(行)使用 SELECT ... FOR UPDATE 语句来获取必要的锁,即使这些行的更改语句是在之后才执行的。
  • 在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁,而不应先申请共享锁、更新时再申请排他锁,因为这时候当用户再申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁。
  • 如果事务需要修改或锁定多个表,则应在每个事务中以相同的顺序使用加锁语句。在应用中,如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会
  • 通过 SELECT ... LOCK IN SHARE MODE 获取行的读锁后,如果当前事务再需要对该记录进行更新操作,则很有可能造成死锁。
  • 改变事务隔离级别。

InnoDB 默认是如何对待死锁的?
  InnoDB 默认是使用设置死锁时间来让死锁超时的策略,默认 innodblockwait_timeout 设置的时长是 50s。
如何开启死锁检测?
  设置 innodbdeadlockdetect 设置为 on 可以主动检测死锁,在 Innodb 中这个值默认就是 on 开启的状态。



悲观锁:
  顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它拿到锁。
乐观锁:
  用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 version 字段来实现。当读取数据时,将 version 字段的值一同读出,数据每更新一次,对此 version 值加 1。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的 version 值相等,则予以更新,否则认为是过期数据。
  示例:据库表三个字段,分别是id、value、version

select id,value,version from t where id=#{id}

  每次更新表中的value字段时,为了防止发生冲突,需要这样操作

update t
set value=2,version=version+1
where id=#{id} and version=#{version}

乐观锁有什么优点和缺点?
  因为没有加锁所以乐观锁的优点就是执行性能高。它的缺点就是有可能产生 ABA 的问题,ABA 问题指的是有一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,会误以为没有被修改会正常的执行修改操作,实际上这段时间它的值可能被改了其他值,之后又改回为 A 值,这个问题被称为 ABA 问题。



共享锁:
  共享锁又称读锁 (read lock),是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。当如果事务对读锁进行修改操作,很可能会造成死锁。
排它锁:
  排他锁 exclusive lock(也叫 writer lock)又称写锁。
  若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。
排它锁是悲观锁的一种实现:若事务 1 对数据对象 A 加上 X 锁,事务 1 可以读 A 也可以修改 A,其他事务不能再对 A 加任何锁,直到事物 1 释放 A 上的锁。这保证了其他事务在事物 1 释放 A 上的锁之前不能再读取和修改 A。排它锁会阻塞所有的排它锁和共享锁。



优化锁的建议:

  • 尽量使用较低的隔离级别。
  • 精心设计索引, 并尽量使用索引访问数据, 使加锁更精确, 从而减少锁冲突的机会。
  • 选择合理的事务大小,小事务发生锁冲突的几率也更小。
  • 给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁。
  • 不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会。
  • 尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响。
  • 不要申请超过实际需要的锁级别。
  • 除非必须,查询时不要显示加锁。 MySQL 的 MVCC 可以实现事务中的查询不用加锁,优化事务性能;MVCC 只在 COMMITTED READ(读提交)和 REPEATABLE READ(可重复读)两种隔离级别下工作。
  • 对于一些特定的事务,可以使用表锁来提高处理速度或减少死锁的可能。


参考文献:

  • 高性能MySQL第三版
  • 极客时间:MySQL45讲
  • 扛得住的MySQL数据库架构:https://coding.imooc.com/class/chapter/49.html#Anchor

原文地址:https://www.cnblogs.com/zhangbLearn/p/12096851.html

时间: 2024-10-10 07:10:35

5.MySQL中的锁的相关文章

Mysql中那些锁机制之MyISAM

说到锁机制之前,先来看看Mysql的存储引擎,毕竟不同的引擎的锁机制也随着不同. 三类常见引擎: MyIsam :不支持事务,不支持外键,所以访问速度快.锁机制是表锁,支持全文索引 InnoDB :支持事务.支持外键,所以对比MyISAM,InnoDB的处理效率差一些,并要占更多的磁盘空间保留数据和索引.锁机制是行锁,不支持全文索引 Memory:数据是存放在内存中的,默认哈希索引,非常适合存储临时数据,服务器关闭后,数据会丢失掉. 如何选择存储引擎: MyISAM:应用是以读操作和插入操作为主

MySQL中的锁

数据库中有很多锁,但锁是为了解决什么问题?具体都有哪些锁呢?这篇文章简单对MySQL中的锁做了一个总结 一.锁的设计是为了解决什么问题? 当多用户读写数据的时候,就有可能会出现同一时刻对同一条数据的读写,如果是大家都只是对同一条数据进行读,无所谓,大家读到数据都是一样的,但当有的想要读取数据,有的想要修改数据的时候,或者大家都想要修改数据的时候就会出现问题.比如有一行数据现在值是5,同一时刻有两个用户都想要修改这行数据,用户1和用户2此刻读到的数据都是5,用户1在数据5的基础上加了1变成6,并将

Mysql中那些锁机制之InnoDB

我们知道mysql在曾经.存储引擎默认是MyISAM.可是随着对事务和并发的要求越来越高,便引入了InnoDB引擎.它具有支持事务安全等一系列特性. InnoDB锁模式 InnoDB实现了两种类型的行锁. 共享锁(S):同意一个事务去读一行,阻止其它事务获得同样的数据集的排他锁. 排他锁(X):同意获得排他锁的事务更新数据,可是组织其它事务获得同样数据集的共享锁和排他锁. 能够这么理解: 共享锁就是我读的时候,你能够读,可是不能写.排他锁就是我写的时候.你不能读也不能写.事实上就是MyISAM的

MySQL中的锁(表锁、行锁)

锁是计算机协调多个进程或纯线程并发访问某一资源的机制.在数据库中,除传统的计算资源(CPU.RAM.I/O)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所在有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素.从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂. 概述 相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制. MySQL大致可归纳为以下3种锁: 表级锁:开销小,加锁快:不会出

MySQL中的锁(表锁、行锁,共享锁,排它锁,间隙锁)

本文参考: http://mysqlpub.com/thread-5383-1-1.html http://blog.csdn.net/c466254931/article/details/53463596 有很多是转载合并过来. 锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的 计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所有数据库必须解决的一 个问题,锁冲突也是影响数据库并发访问性能的一个重

关于MYSQL中的锁

根据mysql存储引擎的不同,支持的锁也不同. myisam,memory,支持表级锁. innodb,支持行级锁和表级锁,默认为行级锁. 表级锁,把整个表锁住.锁表快.不存在死锁.冲突多. 行级锁,锁指定的行.锁表慢.可能出现死锁.冲突少. 关于死锁: 表中有2条数据X,Y.有两个人都想修改这两个数据.并且A先修改X,然后修改Y.B先修改Y,然后修改X. 这两个人同时执行这个事情. 表级锁:无论A先执行还是B先执行,都会先把表锁了,另一个人只能等待,知道第一个人操作完成.所以数据是肯定安全的.

Mysql中的锁机制

原文:http://blog.csdn.net/soonfly/article/details/70238902 锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的 计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所有数据库必须解决的一 个问题,锁冲突也是影响数据库并发访问性能的一个重要因素.从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂.本章我们着重讨论MySQL锁机制 的特点,常见的锁问题

mysql中的锁机制之概念篇

锁的概念 ①.锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具. ②.在计算机中,是协调多个进程或线程并发访问某一资源的一种机制. ③.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一种供许多用户共享访问的资源. ④.如何保证数据并发访问的一致性.有效性,是所有数据库必须解决的一个问题. ⑤.锁的冲突也是影响数据库并发访问性能的一个重要因素. MySQL锁的概述 相对于其它数据库而言,MySQL的锁机制比较简单,其最 显著的特点是不同的存储引擎支持不同的

mysql中的锁的相关知识

数据库锁:数据库锁出现的原因是为了处理并发问题. 并发控制一般采用三种方法,分别是乐观锁和悲观锁以及时间戳. 乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据,就是不做任何操作.悲观锁就刚好相反,觉得自己读数据库的时候,别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度,悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有等自