MySQL
MySQL server:连接管理--【查询缓存(维护成本,8.0已删除)--解析--优化】
存储引擎:
InnoDB 具备外键支持功能的事务存储引擎
MyISAM 主要的非事务处理存储引擎
MEMORY 置于内存的表
字符集:有很多种,以utf8mb4为例 一个字符1-4个字节
比较规则:也叫排序规则(比如有些字符集不区分大小写,而有些区分,所以会影响字段排序)
InnoDB
怎么读写数据:
将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小
一般为 16 KB,一般情况下,读写一次至少一页
行格式:
分别是 Compact 、 Redundant 、Dynamic 和 Compressed(除Redundant比较久远外,其他三个差不多)
记录头信息
名称 大小(单位: bit) 描述
预留位1 1 没有使用
预留位2 1 没有使用
delete_mask 1 标记该记录是否被删除
min_rec_mask 1 B+树的每层非叶子节点中的最小记录都会添加该标记
n_owned 4 表示当前记录拥有的记录数(同一槽的数量)
heap_no 13 表示当前记录在记录堆的位置信息
record_type 3 表示当前记录的类型, 0 表示普通记录, 1 表示B+树非叶子节点记录, 2 表示最小记录, 3 表示最大记录
next_record 16 表示下一条记录的相对位置
MySQL 会为每个记录的真实数据默认的添加一些列(也称为 隐藏列 ),row _id、transaction_id(事务ID)和roll_pointer(回滚指针)。
InnoDB数据页结构
名称 中文名 占用空间大小 简单描述
File Header 文件头部 38 字节 页的一些通用信息
Page Header 页面头部 56 字节 数据页专有的一些信息
Infimum + Supremum 最小记录和最大记录 26 字节 两个虚拟的行记录
User Records 用户记录 不确定 实际存储的行记录内容
Free Space 空闲空间 不确定 页中尚未使用的空间
Page Directory 页面目录 不确定 页中的某些记录的相对位置
File Trailer 文件尾部 8 字节 校验页是否完整
不论我们怎么对页中的记录做增删改操作,InnoDB始终会维护一条记录的单链表,链表中的各个 节点是按照主键值由小到大的顺序连接起来的。
Page Directory(页目录)
将每组最后一条记录的地址偏移量【槽(slot)】按顺序存储
1. 通过二分法确定该记录所在的槽,并找到该槽中主键值最小的那条记录。
2. 通过记录的 next_record 属性遍历该槽所在的组中的各个记录。
Page Header(页面头部)
File Header(文件头部)
文件尾部 File Trailer
防止写入磁盘中断(比如断电),数据不一致
前4个字节代表页的校验和 ,这个部分是和 File Header 中的校验和相对应的
后4个字节代表页面被最后修改时对应的日志序列位置(LSN)
InnoDB中的索引方案
聚簇索引
它有两个特点:
1. 使用记录主键值的大小进行记录和页的排序,
这包括三个方面的含义:
页内的记录是按照主键的大小顺序排成一个单向链表。
各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表。
存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成 一个双向链表。
2. B+ 树的叶子节点存储的是完整的用户记录。 所谓完整的用户记录,就是指这个记录中存储了所有列的值(包括隐藏列)。
在 InnoDB 存储引擎中, 聚簇索引 就是数 据的存储方式(所有的用户记录都存储在了 叶子节点 ),也就是所谓的索引即数据,数据即索引
目录项记录 的 record_type 值是1,而普通用户记录的 record_type 值是0
二级索引
这种按照 非主键列 建立的 B+ 树需要一次 回表 操作才可 以定位到完整的用户记录,所以这种 B+ 树也被称为 二级索引 (英文名 secondary index ),或者 辅助索引
联合索引(也是二级索引)多列大小为排序规则建立的B+树
InnoDB的B+树索引的注意事项
一个B+树索引的根节点自诞生之日起,便不会再移动
当根节点中的可用空间用完时继续插入记录,此时会将 根节点 中的所有记录复制到一个新分配的页,比 如 页a 中,然后对这个新页进行 页分裂 的操作,得到另一个新页,比如 页b,根节点 便升级为存储目录项记录的页
内节点中目录项记录的唯一性
对于二级索引的内节点的目录项记录的内容实际上是由三个部分构成的: 索引列的值 主键值 页号
(防止二级索引字段值相同,插入时无法找到对应位置)
以 InnoDB 的 一个数据页至少可以存放两条记录
MyISAM中的索引方案简单介绍
将表中的记录按照记录的插入顺序单独存储在一个文件中,称之为 数据文件,同时每一行数据会有一个行号,
索引数据页记录的是这个行号
意味着 MyISAM 中建立的索引相当于全 部都是 二级索引
B+树索引适用的条件
全值匹配
匹配左边的列
如果我们想使用联合索引中尽可能多的列,搜索条件中的各个列必须是联合索引中 从最左边连续的列。
匹配列前缀
字符模糊匹配
匹配范围值
如果对多个列同时进行范围查找的话,只有对索引最左边的那个 列进行范围查找的时候才能用到 B+ 树索引
用于排序
按照索引排序,直接取数据(联合索引排序字段按照索引字段一致,才能使用,同时字段ASC、DESC顺序要一致,
WHERE子句中出现非排序使用到的索引列也不行
),如果不是按索引排序,数据加载到内存,再通过排序算法,内存不够还需要借助磁盘
访问二级索引使用 顺序I/O ,访问聚簇索引使用 随机I/O 。需要回表的记录越多,使用二级索引的性能就越低,有时候不如直接扫全表
覆盖索引:为了彻底告别 回表 操作带来的性能损耗,我们建议:最好在查询列表里只包含索引列
索引字符串值的前缀,只对字符串的前几 个字符进行索引
第8章 数据的家-MySQL的数据目录
8.1 数据库和文件系统的关系
操作系统用来管理磁盘的那个东东又 被称为 文件系统 ,所以用专业一点的话来表述就是:像 InnoDB 、 MyISAM 这样的存储引擎都是把表存储在文 件系统上的。
8.2 MySQL数据目录
在我的计算机上 MySQL 的数据目录就是 /usr/local/var/mysql/
除了 information_schema 这个系统数据 库外,每个数据库都对应数据目录下的一个子目录,或者说对应一个文件夹
表名.frm 以二进制格式存储的,表结构信息
InnoDB是如何存储表数据的
表空间(文件空间):是一个抽象的概念,它可以对应文件系统上一个或多个真实文件(不同表 空间对应的文件数量可能不同)。每一个 表空间 可以被划分为很多很多很多个 页 ,我们的表数据就存放在某 个 表空间 下的某些页里。
系统表空间(system tablespace):对应文件系统上一个或多个实际的文件,这个文件是所谓的 自扩展文件 ,从MySQL5.5.7到MySQL5.6.6之间的各个版 本中,我们表中的数据都会被默认存储到这个 系统表空间。
独立表空间(file-per-table tablespace):MySQL5.6.6以及之后的版本中,表名.ibd
MyISAM是如何存储表数据的
MyISAM 并没有什么所谓的 表空 间 一说,表数据都存放到对应的数据库子目录下
test.frm
test.MYD 数据
test.MY 索引
第9章 存放页面的大池子-InnoDB的表空间,114
跳过,太理论了
第10章 条条大路通罗马-单表访问方法
10.1 访问方法(access method)的概念
MySQL 执行查询语句的方式称之为 访问方法 或者 访问类型 。
const:
通过主键或者唯一二级索引列来定位一条记录的访问方法定义为: const ,意思是常数级别的,代 价是可以忽略不计的,如果主键或者唯一二级索引是由多个列构成的话,索引中的每一个列都需要与常数进行等值比较,这个 const 访问方法才有效
ref:
种搜索条件为二级索引列与常数等值比较,采用二级索引来执行查询的访 问方法称为: ref 。一般匹配到多条连续的记录
ref_or_null
SELECT * FROM single_demo WHERE key1 = 'abc' OR key1 IS NULL
range
index
SELECT key_part1, key_part2, key_part3 FROM single_table WHERE key_part2 = 'abc'
all
第11章 两个表的亲密接触-连接的原理
对于 内连接 的两个表,驱动表中的记录在被驱动表中找不到匹配的记录,该记录不会加入到最后的结果 集,我们上边提到的连接都是所谓的 内连接 。
对于 外连接 的两个表,驱动表中的记录即使在被驱动表中没有匹配的记录,也仍然需要加入到结果集。 在 MySQL 中,根据选取驱动表的不同,外连接仍然可以细分为2种:
左外连接 选取左侧的表为驱动表。
右外连接 选取右侧的表为驱动表。
WHERE 子句中的过滤条件 WHERE 子句中的过滤条件就是我们平时见的那种,不论是内连接还是外连接,凡是不符合 WHERE 子句中的过 滤条件的记录都不会被加入最后的结果集。 ON 子句中的过滤条件 对于外连接的驱动表的记录来说,如果无法在被驱动表中找到匹配 ON 子句中的过滤条件的记录,那么该记 录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用 NULL 值填充。
内连接中的WHERE子句和ON子句是等价的
对于内连接来说,驱动表和被驱动表是可以互换的,并不会影响最后的查询结果
左外连接和右外连接的驱动表和被驱动表不能轻易互换
在连接查询中对被驱动表使用主键 值或者唯一二级索引列的值进行等值查找的查询执行方式称之为: eq_ref
join buffer 就是执行连接查询前申请的一块固定大小的内存,先把若干条驱动表结果集 中的记录装在这个 join buffer 中,然后开始扫描被驱动表,每一条被驱动表的记录一次性和 join buffer 中的 多条驱动表记录做匹配,
第13章 兵马未动,粮草先行-InnoDB统计数据是如何收 集的
永久性的统计数据 :磁盘上。 非永久性的统计数据:在内存中
InnoDB 默认是以表为单位来收集和存储统计数据的
当我们选择把某个表以及该表索引的统计数据存放到磁盘上时,实际上是把这些统计数据存储到了两个表里:innodb_index_stats , innodb_table_stats
第14章 不好看就要多整容-MySQL基于规则的优化(内 含关于子查询优化二三事儿)
这种在外连接查询中,指定的 WHERE 子句中包含被驱动表中的列不为 NULL 值的条件称之为 空值拒绝 (英文名: reject-NULL )。在被驱动表的WHERE子句符合空值拒绝的条件后,外连接和内连接可以相互转 换。这种转换带来的好处就是查询优化器可以通过评估表的不同连接顺序的成本,选出成本最低的那种连接顺序 来执行查询。
在这种情况下:外连接和内连接也就没有什么区别了!
SELECT * FROM t1 LEFT JOIN t2 ON t1.m1 = t2.m2 WHERE t2.n2 IS NOT NULL;
行子查询
SELECT * FROM t1 WHERE (m1, n1) = (SELECT m2, n2 FROM t2 LIMIT 1);
物化表
不直接将不相关子查询的结果集当作外层查询的参数,而是将该结果集写 入一个临时表里。
半连接 (英文名: semi-join )
SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a')
改成:SELECT s1.* FROM s1 INNER JOIN s2 ON s1.key1 = s2.common_field WHERE s2.key3 = 'a';
类似于:1对多的结果集,s1数据重复
第15章 查询优化的百科全书-Explain详解
列名 描述
id 在一个大的查询语句中每个 SELECT 关键字都对应一个唯一的 id
select_type SELECT 关键字对应的那个查询的类型
table 表名
partitions 匹配的分区信息
type 针对单表的访问方法
possible_keys 可能用到的索引
key 实际上使用的索引
key_len 实际使用到的索引长度
ref 当使用索引列等值查询时,与索引列进行等值匹配的对象信息
rows 预估的需要读取的记录条数
filtered 某个表经过搜索条件过滤后剩余记录条数的百分比
Extra 一些额外的信息
ID
每一个select关键字对应一个ID,
在连接查询的执行计划中,每个表都会对应一条记录,这些记录的id列的值是相同的,出 现在前边的表表示驱动表,出现在后边的表表示被驱动表
查询优化器可能对涉及子查询的查询语句进行重写,从而转换为连接查询
select_type
SIMPLE 查询语句中不包含 UNION 或者子查询的查询都算作是 SIMPLE 类型,连接查询也算是 SIMPLE 类型,单表查询
PRIMARY 对于包含 UNION 、 UNION ALL 或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询 的 select_type 值就是 PRIMARY
UNION 对于包含 UNION 或者 UNION ALL 的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以 外,其余的小查询的 select_type 值就是 UNION
UNION RESULT MySQL 选择使用临时表来完成 UNION 查询的去重工作,针对该临时表的查询的 select_type 就是 UNION RESULT
SUBQUERY 如果包含子查询的查询语句不能够转为对应的 semi-join 的形式,并且该子查询是不相关子查询,并且查询 优化器决定采用将该子查询物化的方案来执行该子查询,由于select_type为SUBQUERY的子查询由于会被物化,所以只需要执行一遍
DEPENDENT SUBQUERY 如果包含子查询的查询语句不能够转为对应的 semi-join 的形式,并且该子查询是相关子查询,则该子查询 的第一个 SELECT 关键字代表的那个查询的 select_type 就是 DEPENDENT SUBQUERY,select_type为DEPENDENT SUBQUERY的查询可能会被执行多次
DEPENDENT UNION 在包含 UNION 或者 UNION ALL 的大查询中,如果各个小查询都依赖于外层查询的话,那除了最左边的那个小 查询之外,其余的小查询的 select_type 的值就是 DEPENDENT UNION 。
DERIVED 对于采用物化的方式执行的包含派生表的查询
MATERIALIZED 当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询
Type 访问方法
possible_keys和key , possible_keys 可能用 到的索引, key 列表示实际用到的索引
key_len 列主要是为了让我们区分某个使用联合索引的查询具体用了几个索引列
Ref 当使用索引列等值匹配的条件去执行查询时,也就是在访问方法是 const 、 eq_ref 、 ref 、 ref_or_null 、 unique_subquery 、 index_subquery 其中之一时, ref 列展示的就是与索引列作等值匹配的东东是个啥
rows 列就代表预计扫描的记录或索引记录行数
filtered
如果使用的是全表扫描的方式执行的单表查询,那么计算驱动表扇出时需要估计出满足搜索条件的记录到底 有多少条。 如果使用的是索引执行的单表扫描,那么计算驱动表扇出的时候需要估计出满足除使用到对应索引的搜索条 件外的其他搜索条件的记录有多少条。
Extra
EXPLAIN FORMAT=JSON :json 格式的执行计划,里边儿包含该计划花费的成本
第18章 调节磁盘和CPU的矛盾-InnoDB的Buffer
在进行完读写访问之后并不着急把该页对应的内存空间释放掉,而是将其 缓存 起来
MySQL 服务器启动的时候就向操作系统申请了一片连续的内存,他 们给这片内存起了个名,叫做 Buffer Pool (中文名是 缓冲池 )
Buffer Pool
每个缓存页对应的控制信息占用的内存大小是相同的,我们就把每个页对应的控制信息占用的一块内存称为一个 控制块 吧,控制块和缓存页是一一对应的,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边,缓存页被存放到 Buffer Pool 后边
free链表
把所有空闲的缓存页对应的控制块作为一个节 点放到一个链表中,这个链表也可以被称作 free链表 (或者说空闲链表)
缓存页的哈希处理
以用 表空间号 + 页号 作为 key , 缓存页 作为 value 创建一个哈希表,在需要访问某个页的数据 时,先从哈希表中根据 表空间号 + 页号 看看有没有对应的缓存页,如果有,直接使用该缓存页就好,如果没 有,那就从 free链表 中选一个空闲的缓存页,然后把磁盘中对应的页加载到该缓存页的位置
flush链表
凡是修改过的缓存页对 应的控制块都会作为一个节点加入到一个链表中,因为这个链表节点对应的缓存页都是需要被刷新到磁盘上的, 所以也叫 flush链表 。
简单的LRU链表 Least Recently Used
只要我们使用到某个缓存页,就把该缓存页调整到 LRU链表 的头部,这样 LRU链表 尾部就是最近最少 使用的缓存页喽~
LRU 链表划分为 young 和 old 区域这两个部分,预读放在old区,全表扫描设置一个时间间隔,在对某个处在 old 区域的缓存页进行第一次访问时就在它对应的控制块中 记录下来这个访问时间,如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会被 从old区域移动到young区域的头部,否则将它移动到young区域的头部
多个Buffer Pool实例
说一个 Buffer Pool 实例 其实是由若干个 chunk 组成的,一个 chunk 就代表一片连续的内存空间
第19章 事务简介
原子性(Atomicity)转账时候,扣钱和加钱要同时成功或同时失败 undo log实现
隔离性(Isolation):两次转账操作互不影响 加锁和mvcc实现
一致性(Consistency):
MySQL仅仅支持CHECK语法,但实际上并没有一点卵用
虽然 CHECK 子句对一致性检查没什么卵用,但是我们还是可以通过定义触发器的方式来自定义一些约束条件 以保证数据库中数据的一致性。
数据库某些操作的原子性和隔离性都是保证一致性的一种 手段,在操作执行完成后保证符合所有既定的约束则是一种结果
只要最后的结果符合所有现实世界中的约束,那么就是符合 一致性 的。
持久性(Durability): redo log实现
事务 大致上划分成了这么几个状态:
活动的(active)
部分提交的(partially committed)
失败的(failed)
中止的(aborted)
提交的(committed)
第20章 说过的话就一定要办到-redo日志
redo日志会把事务在执行过程 中对数据库所做的所有修改都记录下来,在之后系统奔溃重启后可以把事务所做的任何修改都恢复出来
第24章 一条记录的多幅面孔-事务的隔离级别与 MVCC
READ UNCOMMITTED 未提交读 可能发生脏读
READ COMMITTED 已提交读 解决脏读,可能发生不可重复读
REPEATABLE READ 可重复读 解决不可重复读,可能发生幻读
SERIALIZABLE 可串行化
并发事务执行过程中的问题,严重性: 脏写 > 脏读 > 不可重复读 > 幻读
脏写:一个事务修改了另一个未提交事务修改过的数据,结合脏读好理解
A懵逼了,我明明提交了,咋不见了呢…
脏读:一个事务读到了另一个未提交事务修改过的数据
A读到了一个不存在的数据
不可重复读(Non-Repeatable Read)
如果一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交 后,该事务都能查询得到最新值
A每次都查看到最新的值
例子:银行每日对账问题
幻读(Phantom)
如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先 的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来
幻读 强调的是一个事务按照某个相同条件多次读取记录时,后读取时读到了之前没 有读到的记录。 之前读到的,后面删了,就不算
小贴士: 幻读问题的产生是因为某个事务读了一个范围的记录,之后别的事务在该范围内插入了新记录, 该事务再次读取该范围的记录时,可以读到新插入的记录,所以幻读问题准确的说并不是因为读取 和写入一条相同记录而产生的,这一点要注意一下
Oracle 只支持 READ COMMITTED 和 SERIALIZABLE 隔离级别。
MySQL在REPEATABLE READ隔离级别下,是可以禁止幻读问题的发生的(关 于如何禁止我们之后会详细说明的)。
MySQL 的默认隔离级别为 REPEATABLE READ
MVCC 多版本并发控制
指的就 是在使用 READ COMMITTD 、 REPEATABLE READ 这两种隔离级别的事务在执行普通的 SEELCT 操作时访问记录的版 本链的过程,这样子可以使不同事务的
读-写 、 写-读 操作并发执行,从而提升系统性能。
使用 READ COMMITTED 和 REPEATABLE READ 隔离级别的事务来 说,需要判断一下版本链中的哪个版本是当前事务可见的
ReadView 中主要包含4个比较重要的内容:
m_ids :表示在生成 ReadView 时当前系统中活跃的读写事务的 事务id 列表。
min_trx_id :,也就是 m_ids 中的最 小值。
max_trx_id :表示生成 ReadView 时系统中应该分配给下一个事务的 id 值。
creator_trx_id :表示生成该 ReadView 的事务的 事务id 。
执行INSERT、DELETE、UPDATE这些语句,)才会 为事务分配事务id,个只读事务中的事务id值都默认为0。
版本的 trx_id < min_trx_id 值,表明生成该版本的事务在当前事务生 成 ReadView 前已经提交,所以该版本可以被当前事务访问。
trx_id > min_trx_id,以版本不可以被当前事务访问
min_trx_id < trx_id < max_trx_id, 在 m_ids 列表中,不能访问;如果不在,可以被访问
READ COMMITTED 和 REPEATABLE READ 隔离级别的的一个非常大的区别就是它们生成ReadView的 时机不同。
使 用READ COMMITTED隔离级别的事务在每次查询开始时都会生成一个独立的ReadView。
然后从版本链中挑选可见的记录,从图中可以看出,最新版本的记录行开始执行查看
使用 REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView ,之后的查 询就不会重复生成了
delete mark操作,主要就是为MVCC服 务的
第25章 锁
不加锁 意思就是不需要在内存中生成对应的 锁结构 ,可以直接执行操作。
获取锁成功,或者加锁成功 意思就是在内存中生成了对应的 锁结构 ,而且锁结构的 is_waiting 属性为 false ,也就是事务可以 继续执行操作。
获取锁失败,或者加锁失败,或者没有获取到锁 意思就是在内存中生成了对应的 锁结构 ,不过锁结构的 is_waiting 属性为 true ,也就是事务需要等 待,不可以
继续执行操作。
MySQL 在 REPEATABLE READ 隔离级别实际上就已经解决了 幻读 问题。
怎么解决 脏读 、 不可重复读 、 幻读 这些问题呢?
方案一:读操作利用多版本并发控制( MVCC ),写操作进行加锁
方案二:读、写操作都采用 加锁 的方式
比如银行存款业务
一致性读(Consistent Reads)
事务利用 MVCC 进行的读取操作称之为 一致性读 ,或者 一致性无锁读 ,快照读 。所有普通 的 SELECT 语句( plain SELECT )在 READ COMMITTED 、 REPEATABLE
READ 隔离级别下都算是 一致性读 ,
一致性读 并不会对表中的任何记录做 加锁 操作,其他事务可以自由的对表中的记录做改动
锁定读(Locking Reads)
共享锁 ,英文名: Shared Locks ,简称 S锁 。在事务要读取一条记录时,需要先获取该记录的 S锁 。
独占锁 ,也常称 排他锁 ,英文名: Exclusive Locks ,简称 X锁 。在事务要改动一条记录时,需要先获 取该记录的 X锁 。
以上是行级锁 或者 行锁
表级锁 或 者 表锁 也分S锁和X锁,了解一下,尽量别用
意向共享锁,英文名: Intention Shared Lock ,简称 IS锁 。当事务准备在某条记录上加 S锁 时,需要先 在表级别加一个 IS锁 。
意向独占锁,英文名: Intention Exclusive Lock ,简称 IX锁 。当事务准备在某条记录上加 X锁 时,需 要先在表级别加一个 IX锁 。
总结一下:IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否 被上锁,以避免用遍历的方式来查看表中有没有上
锁的记录,也就是说其实IS锁和IX锁是兼容的,IX锁和IX锁是 兼容的。
By the way :对于 MyISAM 、 MEMORY 、 MERGE 这些存储引擎来说,它们只支持表级锁,而且这些引擎并不支持事务,所以使 用这些存储引擎的锁一般都是针对
当前会话来说的
在对某个表执行一些诸如 ALTER TABLE 、 DROP TABLE 这类的 DDL 语句时,其他事务对这个表并发执 行诸如 SELECT 、 INSERT 、 DELETE 、 UPDATE 的语句会发生阻塞,同理,某个事务中对某个表执行 SELECT 、 INSERT 、 DELETE 、 UPDATE 语句时,在其他会话中对这个表执行 DDL 语句也会发生阻塞。这个 过程其实是通过在
server层 使用一种称之为 元数据锁 (英文名: Metadata Locks ,简称 MDL )
表级别的 AUTO-INC锁
AUTO_INCREMENT 修饰的列递增赋值
行锁
也称为 记录锁 ,顾名思义就是在记录上加的锁。
Record Locks :LOCK_REC_NOT_GAP ,正经记录锁,有 S锁 和 X锁 之分的
Gap Locks : 在记录前边的间隙加的锁,这个 gap锁 的提出仅仅是为了防止插入幻影记录而提出的,虽然有 共享gap锁 和 独占gap锁 这样的说法, 但是它们起到的作用都是相同的。如果是最后一条记录,会在Supremum 记录(页面中最大的记录)前加入
Next-Key Locks :LOCK_ORDINARY,既想锁住某条记录,又想阻止其他事务在该记录前边的 间隙 插入新记录
Insert Intention Locks : : LOCK_INSERT_INTENTION ,我们也可以称为 插入意向锁,插入操作需要等待时,需要在内存中生成一个 锁结构
事实上插入意向锁并不会阻止别的事务继 续获取该记录上任何类型的锁( 插入意向锁 就是这么鸡肋)
隐式锁 一个事务对新插入的记录可以不显式的加锁(生成一个锁结构),但是由于 事务id 这个牛逼的东东的存在,相当于加了一个 隐式锁 。别的事务在对这条
记录加 S锁 或者 X锁 时,由于 隐式锁 的存在,会先帮助当前事务生成一个锁结构,然后自己再生成一个锁结构后进入等待 状态。
InnoDB锁的内存结构
如果符合下边这些条件:
在同一个事务中进行加锁操作
被加锁的记录在同一个页面中
加锁的类型是一样的
等待状态是一样的
那么这些记录的锁就可以被放到一个 锁结构 中。
浙公网安备 33010602011771号