Ⅰ、总览

  • S行级共享锁

    lock in share mode
  • X行级排它锁

    增删改
  • IS意向共享锁
  • IX意向排他锁
  • AI自增锁

Ⅱ、锁之间的兼容性

X IX S IS
X × × × ×
IX × ×
S × ×
IS ×

2.1 意向锁

意向锁揭示了下一层级请求的锁类型,意向锁全兼容

  • IS:事务想要获得一张表中某几行的共享锁
  • IX:事务想要获得一张表中某几行的排它锁

InnoDB存储引擎中意向锁都是表锁,是不是读下来很懵逼?

如果没有意向锁,当你去锁一张表的时候,你就需要对表下的所有记录都进行加锁操作,且对其他事务刚刚插入的记录(游标已经扫过的范围)就没法在上面加锁了,此时就没有实现锁表的功能

对一棵树加锁的概念:

从上往下的,先加意向锁再加记录锁,内存操作,很快,释放操作则是从记录锁开始从下往上进行释放

假设数据库四个层级,库,表,页,记录

假如此时有事务tx1需要在记录A上进行加X锁:
1. 在该记录所在的数据库上加一把意向锁IX
2. 在该记录所在的表上加一把意向锁IX
3. 在该记录所在的页上加一把意向锁IX
4. 最后在该记录A上加上一把X锁
假如此时有事务tx2需要对记录B(假设和记录A在同一个页中)加S锁:
1. 在该记录所在的数据库上加一把意向锁IS
2. 在该记录所在的表上加一把意向锁IS
3. 在该记录所在的页上加一把意向锁IS
4. 最后在该记录B上加一把S锁
假如此时有事务tx3需要在记录A上进行加S锁:
1. 在该记录所在的数据库上加一把意向锁IS
2. 在该记录所在的表上加一把意向锁IS
3. 在该记录所在的页上加一把意向锁IS
4. 发现该记录被锁定(tx1的X锁),那么tx3需要等待,直到tx1进行commit

tips:

  • 共享锁和排它锁不是说只能加在记录级别上,是可以加在各个级别上的

    innodb表锁的获取:

    lock table l read;
    lock table l write;
    unlock tables;
    这是server层的锁(mdl锁)

    从原理上讲innodb也是可以对表加X锁的,但是没有一个具体的命令来触发,也可以把lock table l read; 理解为加X锁

    通常来说不需要加表级别的锁,mysqldump都不加,ddl不支持online的时候就是先对一张表先加一个S锁,现在不一样了

  • 为什么意向锁都是互相兼容的?因为在当前级别上并没有加锁啊

但是在MySQL中没有数据库级别的锁和页级别的锁,这就意味着一共就两层,所有的意向锁都是表锁,意向锁是innodb层级的

tips:

MySQL8.0中所有的锁都在innodb层,现在的锁一部分在innodb层一部分在server层,server层的不好理解

Ⅱ、自增锁

  • 一个表一个自增列,自增锁做自增并发处理
  • auto_increment pk 代表这个列的自增有一把锁
  • 在事务提交前释放

    其他锁在事务提交时才释放
  • Think about

    insert ... select ...

tips:

MySQL的自增存在一个回溯的问题,5.7版本之前都是非持久化的,都是服务启动时候执行下面这个sql获取自增值,从下个位置开始继续自增,如果数据库重启了,之前的自增值可能被重复使用,8.0已解决,这个值会被写到元数据表(innodb引擎)中。

select max(auto_inc_col) from t for update;

2.1 自增列的约束

(root@localhost) [test]> create table t (a int auto_increment, b int) engine = innodb;
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
(root@localhost) [test]> create table t (a int auto_increment, b int, key(b,a)) engine = innodb;
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
(root@localhost) [test]> create table t (a int auto_increment, b int, key(a,b)) engine = innodb;
Query OK, 0 rows affected (0.04 sec)

InnoDB自增列必须被定义为一个key,且必须是这个key的开始部分

WHY?

select max(auto_inc_col) from t for update;

避免重启执行上面这句的时候扫全表 ,myisam是非聚集索引的,不是用这个方式来采集自增值的,8.0虽然持久化了,但还是有这个限制

经测试,myisam自增列也需要被定义为一个key,但是不需要是key的开始部分

2.2 自增的参数

(root@localhost) [test]> show variables like 'auto_increment%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 1 | -- 步长
| auto_increment_offset | 1 | --初始值
+--------------------------+-------+
2 rows in set (0.01 sec)

多节点全局唯一

N台服务器:A:[offset = 1, increment=N] , B:[offset = 2, increment=N] , C:[offset = 3, increment=N]...N:[offset = N, increment=N]

注意,这不能用来做多主,如果有额外的唯一索引就保证不了全局唯一了

2.3 自增锁分析

session1:

(root@localhost) [test]> create table t_ai_l(a int auto_increment, b int, primary key(a));
Query OK, 0 rows affected (0.02 sec) (root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> insert into t_ai_l values(NULL, 10);
Query OK, 1 row affected (0.00 sec) 事务不提交

session2:

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> insert into t_ai_l values(NULL, 20);
Query OK, 1 row affected (0.00 sec)

咦?没等待耶,amazing!

AI锁在事务提交前就释放了,类似latch,使用完就释放了

session1&2:

(root@localhost) [test]> rollback;
Query OK, 0 rows affected (0.02 sec)

session1:

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> insert into t_ai_l values(NULL, 30);
Query OK, 1 row affected (0.00 sec) (root@localhost) [test]> commit;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> select * from t_ai_l;
+---+------+
| a | b |
+---+------+
| 3 | 30 |
+---+------+
1 row in set (0.00 sec)

可以看到虽然rollback,但AI锁是提交过了的,自增值不会跟着回滚,这样自增值就不连续,但连续也没什么用

也就是说,仅仅是这条sql执行的这段时间里,其他session是不可以对这个表操作的,插入过程太长,对insert也会阻塞

执行这条sql的时候,自增是被锁住的,所以插进去之后都是连续的值

2.4 利用sleep()分析自增锁

session1:

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> insert into t_ai_l (a,b) select NULL, sleep(1) from tmp limit 10000;
~~~

session2:

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421958478908128, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 31217775, ACTIVE 10 sec
mysql tables in use 2, locked 2
4 lock struct(s), heap size 1136, 11 row lock(s), undo log entries 10
MySQL thread id 2255, OS thread handle 140482757068544, query id 3006342 localhost root User sleep
insert into t_ai_l (a,b) select NULL, sleep(1) from tmp limit 10000
TABLE LOCK table `test`.`tmp` trx id 31217775 lock mode IS
RECORD LOCKS space id 1408 page no 4 n bits 624 index PRIMARY of table `test`.`tmp` trx id 31217775 lock mode S
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000001cd15db; asc ;;
2: len 7; hex d4000001760110; asc v ;;
3: len 4; hex 80000001; asc ;; Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 000001cd15dc; asc ;;
2: len 7; hex d5000001300110; asc 0 ;;
3: len 4; hex 80000002; asc ;; ... TABLE LOCK table `test`.`t_ai_l` trx id 31217775 lock mode AUTO-INC
TABLE LOCK table `test`.`t_ai_l` trx id 31217775 lock mode IX
...

插入数据过程分析:

  • tmp表被加了IS锁,表中记录被加S锁,注意不会一次性所有记录加锁,是被查到的记录就被锁住,最终事务结束后释放所有锁
  • t_ai_l表上有两个锁AUTO-INC和IX

session2:

(root@localhost) [test]> insert into t_ai_l (a,b) select NULL, sleep(1) from tmp limit 10000;
~~~

session3:

(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421958478909040, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 31218060, ACTIVE 15 sec setting auto-inc lock
mysql tables in use 2, locked 2
LOCK WAIT 3 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 2255, OS thread handle 140482757068544, query id 3006385 localhost root Sending data
insert into t_ai_l (a,b) select NULL, b from tmp limit 10000
------- TRX HAS BEEN WAITING 15 SEC FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table `test`.`t_ai_l` trx id 31218060 lock mode AUTO-INC waiting
------------------
TABLE LOCK table `test`.`tmp` trx id 31218060 lock mode IS
RECORD LOCKS space id 1408 page no 4 n bits 624 index PRIMARY of table `test`.`tmp` trx id 31218060 lock mode S
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000001cd15db; asc ;;
2: len 7; hex d4000001760110; asc v ;;
3: len 4; hex 80000001; asc ;; TABLE LOCK table `test`.`t_ai_l` trx id 31218060 lock mode AUTO-INC waiting
---TRANSACTION 31218051, ACTIVE 40 sec
mysql tables in use 2, locked 2
4 lock struct(s), heap size 1136, 40 row lock(s), undo log entries 39
MySQL thread id 2254, OS thread handle 140482756536064, query id 3006383 localhost root User sleep
insert into t_ai_l (a,b) select NULL, sleep(1) from tmp limit 10000
TABLE LOCK table `test`.`tmp` trx id 31218051 lock mode IS
RECORD LOCKS space id 1408 page no 4 n bits 624 index PRIMARY of table `test`.`tmp` trx id 31218051 lock mode S
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000001cd15db; asc ;;
2: len 7; hex d4000001760110; asc v ;;
3: len 4; hex 80000001; asc ;; Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 000001cd15dc; asc ;;
2: len 7; hex d5000001300110; asc 0 ;;
3: len 4; hex 80000002; asc ;;
...

insert into t_ai_l (a,b) select NULL, b from tmp limit 10000 在等待三个锁

  • t_ai_l表上的AUTO-INC锁
  • tmp表上的IS锁
  • tmp表中第一条记录上的S锁

这样设计的初衷是希望批量插入的自增值是连续的,但实际上是牺牲了并发度的

2.5 自增锁的分类

- 说明
insert-like 所有插入语句都属于此类
simple inserts 插入之前能确定插入多少行(insert into table_1 values(NULL, 1), (NULL, 2)

InnoDB中锁的模式的更多相关文章

  1. InnoDB中锁的模式,锁的查看,算法

    InnoDB中锁的模式   Ⅰ.总览 S行级共享锁lock in share mode X行级排它锁增删改 IS意向共享锁 IX意向排他锁 AI自增锁 Ⅱ.锁之间的兼容性 兼 X IX S IS X ...

  2. InnoDB中锁的查看

    Ⅰ. show engine innodb status\G 1.1 实力分析一波 锁介绍的那篇中已经提到了这个命令,现在我们开一个参数,更细致的分析一下这个命令 (root@localhost) [ ...

  3. InnoDB中锁的算法(1)

    Ⅰ.InnoDB锁算法的介绍 首先明确一点,锁锁住的是什么?锁锁住的是索引 Record Lock 单个行记录上的锁 Gap Lock 锁定一个范围,但不包含记录本身 Next-key Lock Ga ...

  4. InnoDB中锁的算法(2)

    Ⅰ.上节回顾 session1: (root@localhost) [test]> select * from l; +---+------+------+------+ | a | b | c ...

  5. InnoDB中锁的算法(3)

    Ⅰ.隐式锁vs显示锁 session1: (root@localhost) [test]> show variables like 'tx_isolation'; +-------------- ...

  6. Innodb中怎么查看锁信息

    一.前言 上一篇说了下innodb中锁的大概意思, 这篇说说怎么查看加的哪些锁.不然后续出现死锁或者锁等待都不知道为什么. 二.底层基础表信息 在学会如何查看有哪些锁信息时, 需要了解一些基础表信息, ...

  7. InnoDB之锁机制

    前两天听了姜老大关于InnoDB中锁的相关培训,刚好也在看这方面的知识,就顺便利用时间把这部分知识做个整理,方便自己理解.主要分为下面几个部分 1. InnoDB同步机制 InnoDB存储引擎有两种同 ...

  8. Innodb中的事务隔离级别和锁的关系

    前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力. ...

  9. Innodb中的事务隔离级别和锁的关系(转)

    原文:http://tech.meituan.com/innodb-lock.html 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库 ...

随机推荐

  1. 内核中的锁机制--RCU

    一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁(rwlock),它们使用起来非常简单,而且是一种很有效的同步机制,在UNIX系统和Linux系统中得到了 ...

  2. lsb_release command not found

    Linux里的lsb_release命令用来查看当前系统的发行版信 息(prints certain LSB (Linux Standard Base) and Distribution inform ...

  3. pandas DataFrame apply()函数(2)

    上一篇pandas DataFrame apply()函数(1)说了如何通过apply函数对DataFrame进行转换,得到一个新的DataFrame. 这篇介绍DataFrame apply()函数 ...

  4. 训练的时候 nvidia:自动刷新

    间隔一秒自动刷新: watch -n 1 -d nvidia-smi

  5. VS2015 怎么安装RDLC报表模板?

    这几天刚好用到微软自带的RDLC报表,但是在VS2015张找了一圈也没找,难道是我VS版本 不支持,在网上查了下,有的人说VS2015社区版,企业版不支持,只有专业版支持,各说不一,想想不科学呀,微软 ...

  6. Vue自用axios封装

    [本文出自天外归云的博客园] 这是我的Vue项目中的request.js文件,请求报错了看console就会有具体请求信息,方便调试.分享一下. 其中用到了axios和element-ui的组件,ax ...

  7. 转载记录一个有效的jetbrains激活码

    来自:https://blog.csdn.net/ahun535915415/article/details/80687762 K03CHKJCFT-eyJsaWNlbnNlSWQiOiJLMDNDS ...

  8. 12 Best Live Chat Software for Small Business Compared (2019) 最佳的wordpress在线聊天工具推荐插件 来帮你和潜在客户互动

    12 Best Live Chat Software for Small Business Compared (2019)     Did you know that more than 67% of ...

  9. Linux 开机启动顺序_005

    ***了解Linux开机启动顺序之前先了解一下Linux运行级别,通过inittab配置文件查看运行级别的定义: [root@oldboy ~]# cat /etc/inittab # Default ...

  10. JavaScript学习历程03

    一闪一闪亮晶晶 <script type="text/javascript"> var nn = Number(prompt('请输入一个1-9的正整数!')); va ...