下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

MySQL锁机制及优化

作者:课课家教育     来源: http://www.kokojia.com点击数:700发布时间: 2017-08-08 08:00:29

标签: 数据库MySQL数据库锁

  为了保证数据的一致完整性,任何一个数据库都存在锁定机制。锁定机制的优劣直接应想到一个数据库系统的并发处理能力和性能,所以锁定机制的实现也就成为了各种数据库的核心技术之一。本章将对MySQL中两种使用最为频繁的存储引擎MyISAM和Innodb各自的锁定机制进行较为详细的分析。

  MySQL锁定机制简介

  总的来说,MySQL各存储引擎使用了三种类型(级别)的锁定机制:行级锁定,页级锁定和表级锁定。下面我们先分析一下MySQL这三种锁定的特点和各自的优劣所在。

  行级锁

  行级锁定是目前各大数据管理软件所实现的锁定颗粒度最小的,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。

  但是由于锁定资源的颗粒度很小,所以每次获取锁和释放锁消耗的资源也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。

  表级锁

  表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免死锁问题。

  当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度较低。

  页级锁

  页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。

  在MySQL数据库中,使用表级锁定的主要是MyISAM,Memory,CSV等一些非事务性存储引擎,而使用行级锁定的主要是Innodb存储引擎和NDBCluster存储引擎,页级锁定主要是BerkeleyDB存储引擎的锁定方式。

  表级锁定

  MySQL的表级锁定主要分为两种类型,一种是读锁定,另一种是写锁定

  在MySQL中,主要通过四个队列来维护这两种锁定:两个存放当前正在锁定中的读和写锁定信息,另外两个存放等待中的读写锁定信息,如下:

  Currentread-lockqueue(lock->read)持有读锁的所有线程

  Pendingread-lockqueue(lock->read_wait)等待读锁的所有线程

  Currentwrite-lockqueue(lock->write)持有写锁的所有线程

  Pendingwrite-lockqueue(lock->write_wait)等待写锁的所有线程

  当前持有读锁的所有线程的相关信息都能够在Currentread-lockqueue中找到,队列中的信息按照获取到锁的时间依序存放。而正在等待锁定资源的信息则存放在Pendingread-lockqueue里面,另外两个存放写锁信息的队列也按照上面相同规则来存放信息。

  读锁定

  一个新的客户端请求在申请获取读锁定资源的时候,需要满足两个条件:

  资源没有被写锁定

  写锁定等待队列(Pendingwrite-lockqueue)中没有更高优先级的写锁定等待

  如果满足了上面两个条件之后,该请求会被立即通过,并将相关的信息存入Currentread-lockqueue中,否则会被迫进入等待队列Pendingread-lockqueue中等待资源的释放。

  写锁定

  一个新的客户端请求在申请获取写锁定资源的时候,被申请资源需要满足两个条件:

  没被写锁定Currentwrite-lockqueue

  没被写锁定等待(Pendingwrite-lockqueue)

  没被读锁定Currentread-lockqueue

  行级锁定

  行级锁定不是MySQL自己实现的锁定方式,而是由其他存储引擎自己所实现的,如广为大家所知的Innodb存储引擎,以及MySQL的分布式存储引擎NDBCluster等都是实现了行级锁定。

  Innodb锁定模式及其实现机制

  总的来说,Innodb的锁定机制和Oracle数据库有不少相似之处。Innodb的行级锁定同样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,Innodb也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。

  当对某个资源加锁时,如果

  -有共享锁,可以再加一个共享锁,不过不能加排他锁。

  -有排它锁,就在表上添加意向共享锁或意向排他锁。

  意向共享锁可以同时并存多个,但是意向排他锁同时只能有一个存在。所以,可以说Innodb的锁定模式实际上可以分为四种:共享锁(S),排他锁(X),意向共享锁(IS)和意向排他锁(IX)

  Innodb与Mysql锁实现机制的区别

  Oracle锁定数据是通过需要锁定的某行记录所在的物理block上的事务槽上表级锁定信息

  Innodb的锁定则是通过在指向数据记录的第一个索引键之前和最后一个索引键之后的空域空间上标记锁定信息而实现的。

  Innodb的这种锁定实现方式被称为“NEXT-KEYlocking”(间隙锁),因为Query执行过程中通过过范围查找的华,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。

  间隙锁是innodb中行锁的一种,但是这种锁锁住的却不止一行数据,他锁住的是多行,是一个数据范围。间隙锁的主要作用是为了防止出现幻读,但是它会把锁定范围扩大,有时候也会给我们带来麻烦,我们就遇到了。在数据库参数中,控制间隙锁的参数是:innodb_locks_unsafe_for_binlog,这个参数默认值是OFF,也就是启用间隙锁,他是一个bool值,当值为true时表示disable间隙锁。那为了防止间隙锁是不是直接将innodb_locaks_unsafe_for_binlog设置为true就可以了呢?不一定!而且这个参数会影响到主从复制及灾难恢复,这个方法还尚待商量。

  间隙锁的出现主要集中在同一个事务中先delete后insert的情况下,当我们通过一个参数去删除一条记录的时候,如果参数在数据库中存在,那么这个时候产生的是普通行锁,锁住这个记录,然后删除,然后释放锁。如果这条记录不存在,问题就来了,数据库会扫描索引,发现这个记录不存在,这个时候的delete语句获取到的就是一个间隙锁,然后数据库会向左扫描扫到第一个比给定参数小的值,向右扫描扫描到第一个比给定参数大的值,然后以此为界,构建一个区间,锁住整个区间内的数据,一个特别容易出现死锁的间隙锁诞生了。

MySQL锁机制及优化_数据库_MySQL_数据库锁_课课家教育

  在没有并发,或是极少并发的情况下,这样会可能会正常执行,在Mysql中,事务最终都是穿行执行,但是在高并发的情况下,执行的顺序就极有可能发生改变,变成下面这个样子:

 在没有并发,或是极少并发的情况下,这样会可能会正常执行,在Mysql中,事务最终都是穿行执行,但是在高并发的情况下,执行的顺序就极有可能发生改变,变成下面这个样子:

  这个时候最后一条语句:insertintotask_queuevalues(30,25);执行时就会爆出死锁错误。因为删除taskId=20这条记录的时候,20--41都被锁住了,他们都取得了这一个数据段的共享锁,所以在获取这个数据段的排它锁时出现死锁。

  这种问题的解决办法:前面说了,通过修改innodb_locaks_unsafe_for_binlog参数来取消间隙锁从而达到避免这种情况的死锁的方式尚待商量,那就只有修改代码逻辑,存在才删除,尽量不去删除不存在的记录。

  除了间隙锁给Innodb带来性能的负面影响之外,通过索引实现锁定的方式还存在其他几个较大的性能隐患:

  当Query无法利用索引的时候,会放弃行级别锁定而改用表级别的锁定

  当Quuery使用的索引并不包含所有过滤条件的时候,间隙锁会锁定不包含的记录,而不是具体的索引键

  当Query在使用索引定位数据的时候,如果使用的索引键一样但访问的数据行不同的时候(索引只是过滤条件的一部分),一样会被锁定。

  Innodb事务隔离级别下的锁定及死锁

  在Innodb的事务管理和锁定机制中,有专门检测死锁的机制,会在系统中产生死锁之后的很短时间内就检测到该死锁的存在。

  当Innodb检测到系统中产生了死锁之后,Innodb会通过相应的判断来选这产生死锁的两个事务中较小的事务来回滚,而让另外一个较大的事务成功完成。

  但是有一点需要注意的就是,当产生死锁的场景中涉及到不止Innodb存储引擎的时候,Innodb是没办法检测到该死锁的,这时候就只能通过锁定超时限制来解决该死锁了。另外,死锁的产生过程的示例将在本节最后的Innodb锁定示例中演示。

  Innodb锁定机制示例

  下面我们以InnodDB引擎来说明产生死锁的情况

  行锁定演示

Innodb锁定机制示例    下面我们以InnodDB引擎来说明产生死锁的情况    行锁定演示

  无索引升级为表锁定

 无索引升级为表锁定

  间隙锁带来的插入问题

间隙锁带来的插入问题

  死锁示例

 死锁示例

  合理利用锁机制优化MySql

  MyISAM表锁优化建议

  在优化MyISAM存储引擎锁定问题的时候,最关键的就是如何让其提高并发度。由于锁定级别是不可能改变的了,所以我们首先需要尽可能让锁定的时间变短,然后就是让可能并发进行的操作尽可能的并发。

  1、缩短锁定时间

  尽两减少大Query,将复杂Query分拆成小的Query分布进行;

  尽可能的建立足够高效的索引,让数据检索更迅速;

  尽量让MyISAM存储引擎的表只存放必要的信息,控制字段类型;

  利用合适的机会优化MyISAM表数据文件;

  2、分离能并行的操作

  可能有些人会认为在MyISAM存储引擎的表上读写锁就只能是完全的串行化,没办法再并行了。大家不要忘记了,MyISAM的存储引擎还有一个非常有用的特性,那就是ConcurrentInsert(并发插入)的特性。

  Concurrent_insert=2,无论MyISAM存储引擎的表数据文件的中间部分是否存在因为删除数据而留下的空闲空间,都允许在数据文件尾部进行

  ConcurrentInsert;concurrent_insert=1,当MyISAM存储引擎表数据文件中间不存在空闲空间的时候,可以从文件尾部进行ConcurrentInsert;

  concurrent_insert=0,无论MyISAM存储引擎的表数据文件的中间部分是否存在因为删除数据而留下的空闲空间,都不允许ConcurrentInsert。

  3、合理利用读写优先级

  表级锁定对于读和写是有不同优先级设定的,默认情况下是写优先级要大于读优先级。所以,如果我们可以根据各自系统环境的差异决定读与写的优先级。如果我们的系统是一个以读为主,而且要优先保证查询性能的话,我们可以通过设置系统参数选项low_priority_updates=1,将写的优先级设置为比读的优先级低,即可让告诉MySQL尽量先处理读请求。

  Innodb行锁优化建议

  1.尽可能让所有的数据检索都通过索引来完成,从而避免Innodb因为无法通过索引键加锁而升级为表级锁定;

  2.合理设计索引,让Innodb在索引键上面加锁尽可能准确,尽可能的缩小锁定范围,避免造成不必要的锁定而影响其他Query的执行;

  3.尽可能减少基于范围的数据检索过滤条件,避免间隙锁带来的负面影响而锁定了不该锁定的记录;

  4.尽量控制事务的大小,减少锁定的资源量和锁定时间长度;

  5.在业务环境允许的情况下,尽量使用较低级别的事务隔离,以减少MySQL因为实现事务隔离级别所带来的附加成本;

  由于Innodb的行级锁定和事务性,所以肯定会产生死锁,下面是一些比较常用的减少死锁产生概率

  1.类似业务模块中,尽可能按照相同的访问顺序来访问,防止产生死锁;

  2.在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

  3.对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

  系统锁定争用情况查询对于两种锁定级别,MySQL内部有两组专门的状态变量记录系统内部锁资源争用情况

  MySQL实现的表级锁定的争用状态变量:

c.对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;    系统锁定争用情况查询对于两种锁定级别,MySQL内部有两组专门的状态变量记录系统内部锁资源争用情况    MySQL实现的表级锁定的争用状态变量:

  Table_locks_immediate:产生表级锁定的次数;

  Table_locks_waited:出现表级锁定争用而发生等待的次数;

  对于Innodb所使用的行级锁定,系统中是通过另外一组更为详细的状态变量来记录的,如下:

 Table_locks_immediate:产生表级锁定的次数;    Table_locks_waited:出现表级锁定争用而发生等待的次数;    对于Innodb所使用的行级锁定,系统中是通过另外一组更为详细的状态变量来记录的,如下:

  Innodb的行级锁定状态变量不仅记录了锁定等待次数,还记录了锁定总时长,每次平均时长,以及最大时长,此外还有一个非累积状态量显示了当前正在等待锁定的等待数量。对各个状态量的说明如下:

  Innodb_row_lock_current_waits:当前正在等待锁定的数量;

  Innodb_row_lock_time:从系统启动到现在锁定总时间长度;

  Innodb_row_lock_time_avg:每次等待所花平均时间;

  Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间;

  Innodb_row_lock_waits:系统启动后到现在总共等待的次数;

  对于这5个状态变量,比较重要的主要是Innodb_row_lock_time_avg(等待平均时长),Innodb_row_lock_waits(等待总次数)以及Innodb_row_lock_time(等待总时长)这三项。尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手指定优化计划。

  此外,Innodb除了提供这五个系统状态变量之外,还提供的其他更为丰富的即时状态信息供我们分析使用。可以通过如下方法查看

  1.通过创建InnodbMonitor表来打开Innodb的monitor功能:

  2.然后通过使用“showengineinnodbstatus;”查看细节信息。

  小编结语:

  更多内容尽在课课家教育!

赞(29)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程