不同SQL语句在InnoDB中产生的锁

原文地址

一般来说,加锁读UPDATE,或者DELETE这些SQL语句,会在执行时扫描的每一条记录上加记录锁,无论语句中的WHERE条件是否将某一行排除在外。InnoDB不会记得WHERE条件是什么,只能知道扫描了哪些索引。 这里加的锁通常都是next-key锁,会阻塞向当前记录前面的一段"间隙"里面插入数据的操作。[间隙锁]可以显示的禁用,从而也就不会产生next-key锁。更多信息可以查看InnoDB中的锁。事务隔离级别也会影响加锁的机制,参考事务隔离级别

如果在查询中使用了辅助索引,并且在辅助索引记录上加了排它锁,InnoDB同时也会在相应的聚集索引记录上加上锁。

如果在你的语句中没有合适的索引可以使用,MySQL必须要扫描整个表来处理,表中的每一行都会被锁住,从而阻塞了其他用户往表中插入数据。创建良好的索引非常重要,它可以让你的查询无需扫描很多行记录。

InnoDB设置各种锁的机制如下:

  • SELECT ... FROM是一致性读,它从数据库的一个快照中读取数据,不会加任何锁,除非事务隔离级别被设置为SERIALIZABLE,对于SERIALIZABLE级别,查询过程中会在它遇到的每一条记录加上共享next-key锁。然而,如果使用了唯一索引来查询唯一的记录,则只需要在一条记录上加锁。

  • 对于加锁读(SELECT with FOR UPDATE or LOCK IN SHARD MODE),UPDATEDELETE语句,如何设置锁取决于语句中使用的是唯一索引及唯一查询,还是一个范围查询。

    • 对于使用唯一索引同时查询唯一记录,InnoDB只会在找到的这条记录上加锁,而不会在它前面的间隙上加锁。
    • 对于其他查询条件,以及非唯一索引的查询,InnoDB会在扫描到的记录上加上,使用间隙锁next-key锁,来阻止其他事务向当前记录覆盖的间隙中插入数据。关于间隙锁和next-key锁,参考InnoBD中的锁
  • 对于查询中遇到的索引记录,SELECT ... FOR UPDATE会阻塞其他事务进行https://dev.mysql.com/doc/refman/5.6/en/select.html操作,同时也会阻塞某些隔离级别下的读操作。一致性读忽略当前读取视图中任何记录上的锁。

  • UPDATE ... WHERE ...给查询中遇到的每一条记录加上next-key锁。然而,如果使用了唯一索引来查询唯一的记录,则只需要在一条记录上加锁。

  • UPDATE修改一条聚集索引时,会在相应的辅助索引上产生锁。UPDATE操作在插入新的辅助索引记录之前,会扫描进行重复检查,此时会在辅助索引上加上共享锁。

  • DELETE FROM ... WHERE ...在遇到的每条记录上都加上排它next-key锁。然而,如果使用了唯一索引来查询唯一的记录,则只需要在一条记录上加锁。

  • INSERT在要插入的记录上加排它锁。这是一个记录锁,不是next-key锁(也就是没有间隙锁),因此不会阻止其他事务向这条记录之前的间隙中插入数据。
    在插入数据之前,会加上一种间隙锁,叫做插入意向锁。这个锁显示了一种意图,即当前事务准备向表中插入数据,多个事务可以向同一个间隙中同时插入数据,只要它们不向同一个位置插入数据。假设现在表中有索引记录4和7。不同的事务尝试插入值5和6,在获取插入行的排它锁之前,它们都在4到7之间的间隙上加上了插入意向锁,它们不会阻塞彼此,因为要插入的行是不冲突的。(译注:这段文档字面意思很容易懂,但是整体要表达的意思不好理解,它似乎在强调意向锁不会导致多个插入操作相互阻塞,但是确让人疑惑意向锁到底有什么用)

    在插入数据时,如果出现了重复key的错误,会在重复的记录上加共享锁。共享锁的这种使用方式可能会导致死锁:在多个事务尝试向同一个位置插入数据时,如果某个其他事务已经这条记录的排它锁,并且这个事务删除了这条记录。假设一个表有如下结构:

    CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
    

    假设此时有三个事务按照如下顺序进行操作:
    事务1:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    事务2:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    事务3:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    事务1:

    ROLLBACK;
    

    事务1的第一个操作会获取到这条数据的排它锁。事务2和事务3的操作会得到一个重复key错误,然后它们都会请求在这条数据上加共享锁(译注:不懂为什么)。当事务1会回滚后,它会释放这条数据上的排它锁,然后事务2和事务3请求的共享锁就会成功。在此时,事务2和事务3就死锁了:它们都无法获取到这条数据的排它锁,因为他们都占有了共享锁。

    一个类似的情况是,表中已经有了值为1的这条数据,然后三个事务按如下顺序操作:
    事务1:

    START TRANSACTION;
    DELETE FROM t1 WHERE i = 1;
    

    事务2:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    事务3:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    事务1:

    COMMIT;
    

    事务1首先会获取这条数据的排它锁。事务2和事务3的操作会得到一个重复key错误,然后它们都会请求在这条数据上加共享锁。当事务1提交后,它会释放这条数据上的排它锁,然后事务2和事务3请求的共享锁就会成功。在此时,事务2和事务3就死锁了:它们都无法获取到这条数据的排它锁,因为他们都占有了共享锁。

  • INSERT ... ON DUPLICATE KEY UPDATE和单纯的INSERT不同:当遇到重复key错误时,不会去请求共享锁,而是请求排它锁。对于重复的主键索引,会请求获取一个排它记录锁;对于重复的普通索引,会请求获取next-key锁。

  • 在唯一key没有冲突的情况下,REPLACE的行为和INSERT类似。其他情况下,在准备替换的记录上加排它next-key锁。

  • INSERT INTO T SELECT ... FROM S WHERE ...在插入到表T中的每一行上加排它锁(没有间隙锁)。如果事务隔离级别是READ COMMITTED或者开启了innodb_locks_unsafe_for_binlog并且隔离级别不是SERIALIZABLE,InnoDB使用类似一致性读的方式来查询。其他情况下,InnoDB在表S中的数据加上共享next-key锁。
    CREATE TABLE ... SELECT ...和[INSERT ... SELECT]类似,使用一致性读或者使用共享next-key锁来SELECT
    当使用[SELECT]来构建REPLACE INTO t SELECT ... FROM s WHERE ... or UPDATE t ... WHERE col IN (SELECT ... FROM s ...),InnoDB在表s的数据上加共享next-key锁。

  • 当初始化表中被指定为AUTO_INCREMENT的列时,InnoDB在这一列的索引尾部加上一个排它锁。在访问auto-increment计数器时,InnoDB使用一个专用的AUTO-INC表锁模式,此模式下,锁只会持续到当前SQL语句结束,而不是持续到整个事务结束。当AUTO-INC表锁被占用时,其他事务不能向表中插入数据。参考InnoDB事务模型
    InnoDB在获取AUTO_INCREMENT列的值时,不需要加任何锁。

  • 如果表上有FOREIGN KEY约束,任何需要做约束检查的的插入、更新和删除操作都会在被检查的数据上加共享锁。

  • LOCK TABLES设置表锁,表锁是由InnoDB之上的MySQL层来设置的。如果innodb_table_locks为1(默认情况)并且autocommit为0,并且MySQL层能够知道底层的锁,那么innodb能够察觉到表锁的存在。
    此外,InnoDB的死锁检测机制不能够探测到和表锁关联的死锁。同时,由于高层的MySQL不知道底层的锁,可能出现一种情况: 当前事务加上了表锁,但是其他事务同时也加上了一些底层的锁。然而,这并不会影响事务的完整性,如死锁检测和回滚中所描述的。另外可以参考InnoDB表的限制

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

推荐阅读更多精彩内容

  • 原文地址InnoDB Locking 本章节描述了InnoDB中使用的锁. 共享锁和排它锁 意向锁 记录锁 间隙锁...
    wangjie_yy阅读 1,334评论 0 2
  • 1. 锁类型 锁是数据库区别与文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。InnoDB使用的锁类型...
    butterfly100阅读 1,144评论 0 2
  • 什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点。 原子性:要不全部成功,要不全部撤销 隔...
    jiangmo阅读 1,080评论 0 3
  • 文章导读: 累兮,累兮,要死兮...... 本文解决问题: 1、表级锁定(读锁、写锁) 2、行级锁定(共享锁、排他...
    创造new_world阅读 637评论 0 1
  • 结论:超赞 ,得看! 推荐理由: ①演员的演技从行为举止、语气、微小表情和动作,都能非常到位,能抓住人心 ②故事情...
    雅俗儿的手帐阅读 1,030评论 2 6