MySQL中的锁1-基本概念

什么是锁

在对共享资源并发访问时,锁用来保障数据的准确。通俗点理解,锁就类似于排队,Java中synchronized锁是在对象头上进行排队,分布式锁是在一个公用的存储服务上排队,而数据库中的锁是在所操作记录的对象上排队。

MySQL中锁的类型

各大主流数据库都会有锁的实现,而锁正是数据库区别于文件系统的特性之一。不仅不同数据库之间锁的实现不同,MySQL中不同存储引擎对于锁的实现也各不相同。

大体而言MySQL数据库中既有表锁,又有行锁。在Innodb存储引擎中会使用到下列这些锁:

  • Shared and Exclusive Lock
  • Intention Lock
  • Record Lock
  • Gap Lock
  • Next-Key Lock
  • Insert Intention Lock
  • AUTO-INC Locks

MyISAM中实现的锁是表锁,并发情况下的读没有问题,但是并发插入的性能很差。InnoDB实现的是行锁,锁定粒度小并发度高。MySQL默认的存储引擎是InnoDB,且官方目前打算不再继续对其他存储进行开发维护,所以这里我们讨论的锁都是基于InnoDB存储引擎。

锁都是锁在索引上的,无论是聚集索引(主键)还是二级索引

在InnoDB中行锁的模式有两种:

  • 共享锁(S Lock),允许事务读取一行数据。
  • 排他锁(X Lock),允许事务删除或者更新一行数据。

意向锁

除了行级别的锁,InnoDB中还支持表级别的锁。为了实现多粒度的锁(多粒度的锁表示在数据库中不但能实现行级别的锁,还可以实现页级别的锁,表级别的锁甚至数据库级别的锁),InnoDB还支持另外一种锁的模式,意向锁。意向锁主要是为了在一个事务中揭示下一行将被请求的锁类型。InnoDB同样支持两种意向锁:

  • 意向共享锁(IS Lock),事务想要获取表中某几行的共享锁。
  • 意向排他锁(IX Lock),事务想要获取表中某几行的排他锁。

意向锁的工作方式



MySQL加锁的方式是从上往下一层层加的。如果事务A要在记录1上加一把X锁,则步骤如下:

  1. 在记录1所在的数据库上加一把意向锁IX;
  2. 在记录1所在的表上加一把意向锁IX;
  3. 在记录1所在的页上加一把意向锁IX;
  4. 在记录1上加一把X锁。

InnoDB没有数据库级别的锁,也没有页级别的锁,InnoDB只能在表和记录上加锁,所以InnoDB的意向锁只能加在表上,即InnoDB中的意向锁都是表锁。

下面来看看共享锁、排他锁、意向共享锁、意向排他锁的兼容性


InnoDB中锁的兼容性

加锁以及查看

加锁

针对某条记录的修改和删除会隐式的加一把X锁,如果正对查询也想要加锁就需要在SQL语句中显示的指定。

查询操作通过在SQL语句的末尾处增加for update来加X锁
select * from t1 where a=1 for update;

查询操作通过在SQL语句的末尾处增加lock in share mode来加S锁
select * from t1 where a=1 lock in share mode;

锁超时

当产生锁竞争时,如果持有锁的一方迟迟不释放锁,这时请求锁的事务(或session)会一直等待锁的释放。但是这个等待锁的过程是有等待超时的,通过innodb_lock_wait_timeout变量进行设置,默认50s。也就是说等待50秒后还没有获取到锁就放弃等待,同时抛出错误ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

锁等待超时

查看

可以通过show engine innodb status\G;指令来查看加锁的情况,该指令会显示很多和innodb存储引擎相关的运行信息,其中TRANSACTIONS表示和锁相关的信息。默认显示的情况如下:

如果想要显示更详细的信息,可以将innodb_status_output_locks参数打开,在MySQL5.7中其默认是关闭的。

打开之后我们可以看到关于锁更详细的信息

-- Record lock : 表示是锁住的记录
-- heap no 2 PHYSICAL RECORD: n_fields 5 : 表示锁住记录的heap no 为2的物理记录,由5个列组成
-- compact format : 表示这条记录存储的格式(Dynamic其实也是compact的格式)
-- info bits : 0 -- 表示这条记录没有被删除; 非0 -- 表示被修改或者被删除(32)

在MySQL5.7之后的版本中,我们可以通过sys库下的innodb_lock_waits表来查看更详细的信息,注意只有当产生了锁等待时该表中才会有记录。

select * from innodb_lock_waits\G;
*************************** 1. row ***************************
                wait_started: 2018-07-15 12:06:27   -- 开始的时间
                    wait_age: 00:00:09          -- 等待的时间                    
               wait_age_secs: 9             -- 等待的秒数
                locked_table: `test`.`t1`           -- 锁主的表(意向锁)
                locked_index: GEN_CLUST_INDEX       -- 锁住的是系统生成的聚集索引,锁都是在索引上的
                 locked_type: RECORD            -- 锁的类型,记录锁
              waiting_trx_id: 421992807332576       -- 等待锁的事务ID
         waiting_trx_started: 2018-07-15 12:06:27
             waiting_trx_age: 00:00:09
     waiting_trx_rows_locked: 1
   waiting_trx_rows_modified: 0
                 waiting_pid: 13
               waiting_query: select * from t1 where a=2 lock in share mode -- 等待锁的SQL语句
             waiting_lock_id: 421992807332576:25:3:2   -- 事务ID:space:page_no:heap_no
           waiting_lock_mode: S                        -- 等待的锁的模式
             blocking_trx_id: 3338                     
                blocking_pid: 12
              blocking_query: NULL
            blocking_lock_id: 3338:25:3:2
          blocking_lock_mode: X                -- 阻塞的锁的类型
        blocking_trx_started: 2018-07-15 12:04:41
            blocking_trx_age: 00:01:55
    blocking_trx_rows_locked: 2
  blocking_trx_rows_modified: 0
     sql_kill_blocking_query: KILL QUERY 12
sql_kill_blocking_connection: KILL 12                  -- 给出了建议
1 row in set, 3 warnings (0.00 sec)

下面列举一下通过show engine innodb status\G指令看到的各种锁的样子。

1. 意向锁
TABLE LOCK table `test`.`t3` trx id 3962 lock mode IX

2. Record Locks
RECORD LOCKS space id 39 page no 3 n bits 72 index PRIMARY of table `test`.`t3` trx id 3962 lock_mode X locks rec but not gap
2.1 从index PRIMARY可以看出该锁加在主键上
2.2 记录锁除了lock_mode X locks rec but not gap还有lock_mode S locks rec but not gap

3. Gap Locks
RECORD LOCKS space id 39 page no 5 n bits 72 index c of table `test`.`t3` trx id 3962 lock_mode X locks gap before rec

4. Next-Key Locks
RECORD LOCKS space id 39 page no 5 n bits 72 index c of table `test`.`t3` trx id 3962 lock_mode X

5. Insert Intention Locks
RECORD LOCKS space id 279 page no 3 n bits 72 index PRIMARY of table `test`.`t3` trx id 133587907 lock_mode X insert intention waiting

6. AUTO-INC Locks
TABLE LOCK table xx trx id 7498948 lock mode AUTO-INC waiting

读与MVCC

我们知道如果某行数据加了X锁, 就没法加S锁,即没法读了,不过在实际情况中一般数据库中都允许并发读的,即使要读取的记录上存在X锁。普通的读操作(非for updatelock in share mode)不会加锁。那么在MySQL中如何保障存在DDL的并发操作中读的一致性呢?Innodb使用了MVCC技术来保证读的一致性

MVCC (Multiversion Concurrency Control),即多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能


如果要读取的行上存在X锁,这时读操作不会等行上X锁的释放,而是去读取行的一个快照版本。由于没有事务会对快照数据进行修改,所以对于快照的读取是不用上锁的。

read committedrepeatable read的事务隔离级别中,都是使用MVCC进行读操作。不过这两种隔离级别在选择哪个快照版本进行读取问题上存在区别,区别如下:

  • read committed选择最新的快照版本进行读(事务A),如果其他事务(事务B)对要读取的行进行了更新并提交。则在事务A再进行读时读取到的数据版本是事务B提交修改后形成的最新快照版本。
  • repeatable read读取的是事务(事务A)一开始时记录的快照版本,即使后面事务B对数据进行了修改并提交,只要事务A没有提交,那么在事务A中读取的版本依然是一开始的快照版本。

这也就能说明为啥在read committed中存在不可重复读现象,而repeatable read不存在。

MVCC实现读是不需要加锁的,MySQL当中除了MVCC的方式之外,我们还可以通过显示的加锁来保证并发过程中数据读取的一致性。可以通过下面两种方式显式的加锁:

  1. select ... for update; 加X锁
  2. select ... lock in share mode; 加S锁

这两种形式的查询语句要放在事务当中,一旦事务提交了,锁也就释放了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容