针对多线程的并发访问,任何一个数据库都有其锁定机制,它的优劣直接关系着数据的一致完整性与数据库系统的高并发处理性能。锁定机制也因此成了各种数据库的核心技术之一。不同数据库存储引擎的锁定机制是不同的,本文将从MySQL最常见的存储引擎MyISAM与InnoDB的锁定机制说起。

一、MyISAM的锁机制——表级锁定

MySQL表级锁定的常见类型主要分为两种,一种是读锁,一种是写锁。
谁持有读锁?谁持有写锁?谁在等待读锁资源?谁在等待写锁资源?数据库系统都是要记录的。MySQL中,主要通过如下4个队列来保存相关信息:

读锁持有队列:Current read-lock queue(lock->read)——存放所有正在锁定的读锁信息
写锁持有队列:Current write-lock queue(lock->write)——存放所有正在锁定的写锁信息
读锁等待队列:Pending read-lock queue(lock->read_wait)——存放所有等待对资源加读锁的线程信息
写锁等待队列:Pending write-lock queue(lock->write_wait)——存放所有等待对资源加写锁的线程信息

为保证数据一致完整性,多线程可以为同一份资源加多个读锁,而同一份资源只能加一个写锁,读锁与写锁也不能同时加在一份资源上。

1、读锁定

客户端请求获取读锁定资源时,如果满足如下两个要求,则请求通过,进入读锁持有队列;否则,请求失败,进入读锁等待队列。
(1)请求锁定的资源当前没有写锁定;
(2)写锁等待队列中没有优先级更高的写锁定在等待。

2、写锁定

客户端请求获取写锁定的时候:
(1)先通过写锁持有队列检查这份资源是否已经被加上写锁定,如果有,自然暂停自身线程进入写锁等待队列等待,如果没有,进行第(2)步
(2)检查写锁等待队列中是否有线程同样在等待获取这份资源的写锁定,如果有,则进入写锁等待队列等待,如果没有,进行第(3)步
(3)通过读锁持有队列检查这份资源是否已经被加上读锁定,如果有,则进行写锁等待队列等待,如果没有,可以获取写锁定,进入写锁持有队列中

请注意:对于MySQL使用者,展现出来的锁定类型只有读锁定与写锁定两种,但实际上,MySQL内部实现中却有11种枚举出来的锁定类型,因为表面与实现的差异,上述请求过程会有特例,在此不再赘述,如想深入了解,可参看简朝阳《MySQL性能调优与架构设计》。

那我们说,MyISAM在对表的操作上只能是串行处理,不能并行操作吗?并不是,MyISAM有一个很重要的机制就是并发插入(Concurrent Insert)特性,我们在下面第三部分MyISAM表级锁定优化建议再详细介绍。

二、InnoDB的锁机制——行级锁定

不光InnoDB存储引擎,MySQL的分布式存储引擎NDB Cluster都使用行级锁定。InnoDB的行级锁定同样分为两种,一种是共享锁,一种是排它锁。

1、当一个事务需要给某份资源加锁的时候,主要情况有如下

(1)如果遇到一个共享锁正锁定着资源,那么事务只能再加上一个共享锁,而不能加排它锁。
(2)如果遇到一个排他锁正锁定着资源,那么事务只能等待该锁定释放资源后他才能获得资源并添加自己的锁定。

2、InnoDB锁机制的实现与弊端

InnoDB锁机制是基于索引实现的,通过在指向数据记录的第一个索引键之前与最后一个索引键之后的空域空间(间隙或着说是范围)标记锁定信息实现,被称为间隙锁。
间隙锁的弊端:会在执行范围查询时,对范围内所有键值加锁,即使键值不存在,这会造成在加锁后无法插入锁定键值范围内的任何数据,影响性能。比如:

SELECT *
FROM user
WHERE user_id BETWEEM 1 AND 100

执行这个查询时,会对1-100范围内所有索引键值(1-100)加间隙锁,即使并不存在user_id为10的用户信息,所以在加锁后,要想插入一条user_id为10的用户信息是不可行的,这对于行级锁来说并不符合常理。InnoDB给出的解释是:为了防止幻读的出现。
当没有索引时或无法利用索引时,InnoDB会弃用行级锁,改用表级锁,并发处理性能降低。
另外,因为InnoDB的行级锁与事务处理特性,一定会产生死锁现象,对于如何降低死锁产生概率,我在第四部分InnoDB行级锁定优化建议中详述。

三、MyISAM表级锁定优化建议

因为表级锁的锁定颗粒较大,其实现难度复杂性行都降低了,成本自然降低,但是付出了高并发处理性能较低的代价,所以表级锁的优化就从如何提高并发处理性能说起。

1、缩短锁定时间

(1)降低查询复杂度,将复杂的查询划分成几个简单的查询分步进行。
(2)建立合适的索引加快查询效率。
(3)优化表结构,只存放必要的信息,且控制字段类型与字段长度(等长最优)。

2、利用MyISAM并发插入特性(Concurrent Insert),通过设置concurrent_insert参数实现

(1)concurrent_insert=2,无论MyISAM表数据文件的中间部分是否有因为删除数据留下的空闲空间,都允许在数据文件尾部进行并发插入。
(2)concurrent_insert=1,当MyISAM表数据文件中间不存在空闲空间时,才允许在数据文件尾部进行并发插入。
(3)concurrent_insert=0,无论MyISAM表数据文件的中间部分是否有因为删除数据留下的空闲空间,都不允许在数据文件尾部进行并发插入。
如果数据被删除的可能性比较小,而且对暂时性浪费并不在乎的话,可以尝试把concurrent_insert设置为2;但当删除量不是很小,查询时需要读取更多的空域空间时,推荐设置为1。

3、合理利用读写优先级

默认情况下,写优先级要高于读优先级。
(1)当数据库系统以读为主,要优先保证查询性能时,可通过low_priority_updates=1设置读优先级高于写优先级。
(2)当数据库系统需要保证写入性能,则不用设置low_priority_updates参数。

四、InnoDB行级锁定优化建议

InnoDB的行级锁最大的优势就是增强了高并发的处理能力,缺点就是复杂性较高、易死锁,且基于索引实现有一定弊端。我们要做的就是扬长避短,合理利用InnoDB行级锁定,为此我们就应该做的:
1、尽可能让所有的数据检索都通过索引实现,因为InnoDB行级锁是基于索引实现的,没有索引或无法使用索引系统会改为使用表级锁。
2、合理设计索引,以缩小加锁范围,避免“间隙锁”造成不该锁定的键值被锁定。
3、尽量控制事务的大小,因为行级锁的复杂性会加大资源量以及锁定时间。
4、使用较低级别的事务隔离,以减少因实现事务隔离而付出的成本。
5、避免死锁,可以通过如下方式实现:
(1)类似的业务模块中,尽可能按照相同的访问顺序来访问,防止产生死锁。
(2)同一个事务中,尽量做到一次性锁定需要的所有资源。
(3)对于易产生死锁的业务部分,增大处理颗粒度,升级为表级锁以降低死锁产生的概率。

更多MySQL的锁相关知识,参阅:MySQL锁详解

MySQL性能调优——锁定机制与锁优化分析的更多相关文章

  1. MySql(七):MySQL性能调优——锁定机制与锁优化分析

    针对多线程的并发访问,任何一个数据库都有其锁定机制,它的优劣直接关系着数据的一致完整性与数据库系统的高并发处理性能.锁定机制也因此成了各种数据库的核心技术之一.不同数据库存储引擎的锁定机制是不同的,本 ...

  2. MySql(十一):MySQL性能调优——常用存储引擎优化

    一.前言 MySQL 提供的非常丰富的存储引擎种类供大家选择,有多种选择固然是好事,但是需要我们理解掌握的知识也会增加很多.本章将介绍最为常用的两种存储引擎进行针对性的优化建议. 二.MyISAM存储 ...

  3. MySQL性能调优与架构设计——第7章 MySQL数据库锁定机制

    第7章 MySQL数据库锁定机制 前言: 为了保证数据的一致完整性,任何一个数据库都存在锁定机制.锁定机制的优劣直接应想到一个数据库系统的并发处理能力和性能,所以锁定机制的实现也就成为了各种数据库的核 ...

  4. MySQL性能调优与架构设计——第 18 章 高可用设计之 MySQL 监控

    第 18 章 高可用设计之 MySQL 监控 前言: 一个经过高可用可扩展设计的 MySQL 数据库集群,如果没有一个足够精细足够强大的监控系统,同样可能会让之前在高可用设计方面所做的努力功亏一篑.一 ...

  5. MySQL性能调优与架构设计——第 15 章 可扩展性设计之Cache与Search的利用

    第 15 章 可扩展性设计之Cache与Search的利用 前言: 前面章节部分所分析的可扩展架构方案,基本上都是围绕在数据库自身来进行的,这样是否会使我们在寻求扩展性之路的思维受到“禁锢”,无法更为 ...

  6. MySQL性能调优与架构设计——第13章 可扩展性设计之 MySQL Replication

    第13章 可扩展性设计之 MySQL Replication 前言: MySQL Replication 是 MySQL 非常有特色的一个功能,他能够将一个 MySQL Server 的 Instan ...

  7. MySQL性能调优与架构设计——第11章 常用存储引擎优化

    第11章 常用存储引擎优化 前言: MySQL 提供的非常丰富的存储引擎种类供大家选择,有多种选择固然是好事,但是需要我们理解掌握的知识也会增加很多.每一种存储引擎都有各自的特长,也都存在一定的短处. ...

  8. MySQL性能调优与架构设计——第6章 MySQL Server 性能的相关因素

    第6章 MySQL Server 性能的相关因素 前言 大部分人都一致认为一个数据库应用系统(这里的数据库应用系统概指所有使用数据库的系统)的性能瓶颈最容易出现在数据的操作方面,而数据库应用系统的大部 ...

  9. MySQL性能调优与架构设计——第3章 MySQL存储引擎简介

    第3章 MySQL存储引擎简介 3.1 MySQL 存储引擎概述 MyISAM存储引擎是MySQL默认的存储引擎,也是目前MySQL使用最为广泛的存储引擎之一.他的前身就是我们在MySQL发展历程中所 ...

随机推荐

  1. 【Qt编程】基于Qt的词典开发系列<三>--开始菜单的设计

    这篇文章讲讲如何实现开始菜单(或者称为主菜单)的设计.什么是开始菜单呢?我们拿常用的软件来用图例说明,大多数软件的开始菜单在左下角,如下图: 1.window 7的开始菜单 2.有道词典的主菜单 3. ...

  2. shell-like program(shell程序的基本实施部分)

    直接上代码: #include "apue.h" #include <sys/wait.h> int main(void) { char buf[MAXLINE]; / ...

  3. SVN版本控制器中各符号的含义

    SVN符号的含义 项目开发过程中,随着学习的不断深入,开始慢慢接触到版本管理控制工具,其实这个工具主要用于团队开发之中,但对于个人项目的备份也有好处,可以避免在电脑出现不可预知的故障时,最大化的保护自 ...

  4. sqlserver2008中删除了windows用户,导致无法登陆的解决方案

    打开管理工具中的"服务",找到并关闭SQL Server服务.进入命令进入SQL Server 2008的安装目录,找到sqlservr.exe文件,执行命令:sqlservr - ...

  5. rails将类常量重构到数据库对应的表中之三

    经过博文之一和之二的重构,貌似代码表现的还不错,正常运行和test都通过鸟,但是,感觉告诉我们还是有什么地方不对劲啊!究竟是哪里不对劲呢?我们再来好好看一下. 我们把数据库表中的支付方式集合直接放在实 ...

  6. 重定向和servlet生命周期

    重定向(1)什么是重定向服务器通知浏览器向一个新的地址发送请求.注:可以发送一个302状态码和一个Location消息头.(该消息头包含了一个地址,称之为重定向地址),浏览器收到之后,会立即向重定向地 ...

  7. jdk1.7 tomcat-7安装

    由于软件下载地址经常有变动,所以不能直接wget,还是直接到网上点击下载 下载jdk http://www.oracle.com/technetwork/java/javase/downloads/j ...

  8. struts2线程安全

     struts2线程安全 2012-02-16 21:07:58 分类: 系统运维 问题:Struts 2 Action对象为每一个请求产生一个实例,因此没有线程安全问题.Spring的Ioc容器管理 ...

  9. Js 实现自定义事件

    var Event = { on: function (eventName, callback) { if (!this[eventName]) { this[eventName] = []; } t ...

  10. EF中关于TransactionScope的使用

    前提条件 TransactionScope类需要引用System.Transactions; 数据库环境及需求 现在假设有两个表如图:                                 ...