文章目录
一.安装与认识
- 部分命令为链接文件
- 不经外部网络的unix域套接字 TCP https://blog.csdn.net/qq_38950316/article/details/81087809
- 连接管理:缓存线程、SSL安全套接字
- 解析优化:查询缓存(任何不同都会导致无法命中;查询里有任何系统函数、表、用户自定义变量,将不会缓存;表结构或数据被修改,任何缓存立即失效)语法解析 查询优化(修改我们的sql语句,外联转内联)
- 存储引擎:数据的存储与提取 是否支持事务、分布式事务、部分事务回滚、聚类索引、B-Tree索引 不同表可以选择不同引擎
二.启动选项与系统变量
- -u --user --skip-networking --skip_networking
- ./configure make make install (https://blog.csdn.net/u012501054/article/details/77880139)
- 多个配置文件,以最后一个为准 多个配置组,以最后一个为准 命令行与配置文件,以命令行为准
- 多个单词组成的系统变量必须用下划线连接;系统变量的范围分为GLOBAL和SESSION;状态变量用于显示运行状况的,也分GLOBAL和SESSION,不能修改
三.字符集与比较规则
- ASCII 1字节(8位)编码 GB2312 变长编码 if in ASCII: 1字节编码 else: 2字节编码
utf-8 1-4字节变长编码 (是unicode字符集的一种编码方案) - 使用4字节编码一个字符的情况,比如存储emoji表情,使用utf8mb4
- 比较规则命名:字符集+语言+具体规则
- character_set_client character_set_connection character_set_results
四.innoDB记录结构
- 真实数据在不同存储引擎中存放的格式一般是不同的
- innoDB将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB
- 有四种格式 其中compact格式 一条记录由变长字段长度列表、NULL值列表、记录头信息、各列值组成
- 行溢出数据 记录到其他页 当前页记录地址
五.数据页结构
- user records部分:
字段delete_mask表明记录是否被删除,即使被删除也会暂时留在页中,移除它们之后把其他的记录在磁盘上重新排列需要性能消耗,所有被删除掉的记录都会组成一个所谓的垃圾链表,在这个链表中的记录占用的空间称之为所谓的可重用空间
next_record 从当前记录的真实数据到下一条记录的真实数据的地址偏移量,InnoDB始终会维护一条记录的单链表,链表中的各个节点是按照主键值由小到大的顺序连接起来的。 - 页目录: 所有记录分组,每组最后一条记录头信息里存储该组拥有的记录数,将每组最后一条记录的偏移量(槽)单独提取放置于尾部,被称作页目录
- 在一个数据页中查找指定主键值的记录的过程分为两步:通过二分法确定该记录所在的槽,并找到该槽中主键值最小的那条记录。通过记录的next_record属性遍历该槽所在的组中的各个记录。
- 所有的数据页其实是一个双链表
- File Trailer:用于检验页是否完整的部分
六.B+树索引
- 通过记录移动保证下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值,称作页分裂
- 复用之前存储用户记录的数据页来存储目录项,称为目录项记录,所有目录项记录都存储在内节点
- 实际用户记录其实都存放在B+树的最底层的节点上
- 一般情况下,我们用到的B+树都不会超过4层
- 具有这两种特性的B+树称为聚簇索引:使用记录主键值的大小进行记录和页的排序,B+树的叶子节点存储的是完整的用户记录
- 因为这种按照非主键列建立的B+树需要一次回表操作才可以定位到完整的用户记录,所以这种B+树也被称为二级索引,或者辅助索引
- 以c2和c3列的大小为排序规则建立的B+树称为联合索引,它的意思与分别为c2和c3列分别建立索引的表述是不同的
- 一个B+树索引的根节点自诞生之日起,便不会再移动
- 一个页面最少存储2条记录
- MyISAM存储引擎,表中的记录按照记录的插入顺序单独存储在一个文件中,称之为数据文件,索引信息另外存储到一个称为索引文件的另一个文件中,是主键值 + 行号的组合。MyISAM中每次查询需要进行一次回表操作,意味着MyISAM中建立的索引相当于全部都是二级索引
七.B+树索引的使用
- 如果我们想使用联合索引中尽可能多的列,搜索条件中的各个列必须是联合索引中从最左边连续的列
- 匹配列前缀可以用到索引,因为已自动排好序了,而匹配列中缀或后缀则需要全表扫描
- 联合索引,精确匹配某一列并范围匹配另外一列时都可以用到索引
- 文件排序:暂时借助磁盘的空间来存放中间结果,排序操作完成后再把排好序的结果集返回到客户端。如果order by用到了索引列,可能会省去文件排序的过程
- 联合索引时无法使用索引的情况:ASC、DESC混用、WHERE子句中出现了非排序使用到的索引列、排序列包含非同一个索引的列、排序列使用了复杂的表达式
- 分组可以用B+树索引进行
- 把连着的记录从磁盘中读出来,这种读取方式我们也可以称为顺序I/O,不连续的读取方式称作随机I/O
- 需要回表的记录越多,使用二级索引的性能就越低
- 只需要用到索引的查询方式称为索引覆盖,排序操作也优先使用覆盖索引的方式进行查询。
- 只为出现在WHERE子句中的列、连接子句中的连接列,或者出现在ORDER BY或GROUP BY子句中的列创建索引
- 在记录行数一定的情况下,列的基数越大,该列中的值越分散,列的基数越小,该列中的值越集中。最好为那些列的基数大的列建立索引,为基数太小列的建立索引效果可能不好。
- 在表示的整数范围允许的情况下,尽量让索引列使用较小的类型,意味着节省更多的存储空间,更块的比较操作和更高效的I/O。
- 在字符串类型能存储的字符比较多的时候,鼓励只对字符串的前几个字符进行索引
- 如果索引列在比较表达式中不是以单独列的形式出现,而是以某个表达式,或者函数调用形式出现的话,是用不到索引的。
- 让主键具有AUTO_INCREMENT,让存储引擎自己为表生成主键,而不是我们手动插入 。避免页分裂和记录移位造成性能损耗
- 唯一索引是二级索引
八. Mysql的数据目录
- 存储加载文件和运行产生数据的目录称为数据目录
- 每个数据库都对应数据目录下的一个子目录,或者说对应一个文件夹,数据库相关属性为db.opt的文件
- 表结构存储为表名.frm的文件,二进制格式。
- 系统表空间:InnoDB会在数据目录下创建一个名为ibdata1、大小为12M的文件,这个文件就是对应的系统表空间在文件系统上的表示,为自扩展文件
- 独立表空间:在该表所属数据库对应的子目录下创建一个表示该独立表空间的文件表名.ibd
- MySQL中的视图其实是虚拟的表,只需要存储它的结构即视图名.frm
- mysql文件系统里还包括:服务器进程文件(记录自己的进程ID)、服务器日志文件、默认生成的SSL\RSA证书和密钥
- 文件系统的制约:数据库名称和表名称不得超过文件系统所允许的最大长度、特殊字符(数据库名和表名中所有除数字和拉丁字母以外的所有字符在文件名里都映射成 @+编码值的形式作为文件名)、文件长度受文件系统最大长度限制
九. InnoDB的表空间
- 连续的64个页就是一个区(extent),也就是说一个区默认占用1MB空间大小,每256个区被划分成一组,每个组的最开始的几个页面类型是固定的
- 一次性分配多个连续的区,可能造成一点点空间的浪费(数据不足填充满整个区),但是从性能角度看,可以消除很多的随机I/O
- 某些零散的页面以及一些完整的区的集合就算是一个段(segment),一个索引会生成2个段,一个叶子节点段,一个非叶子节点段。
- 碎片(fragment)区:在一个碎片区中,并不是所有的页都是为了存储同一个段的数据而存在的,碎片区中的页可以用于不同的目的
- 每一个区都对应着一个XDES Entry结构,不同状态的区对应的XDES Entry各自串成一个链表,共有FREE、FREE_FRAG和FULL_FRAG这3个链表,直属于表空间,用于快速查找不同状态的区
- 向表中插入数据本质上就是向表中各个索引的叶子节点段、非叶子节点段插入数据
- 当段中数据较少的时候,首先会查看表空间中是否有状态为FREE_FRAG的区,也就是找还有空闲空间的碎片区,如果找到了,那么从该区中取一些零碎的页把数据插进去;否则到表空间下申请一个状态为FREE的区,也就是空闲的区,把该区的状态变为FREE_FRAG,然后从该新申请的区中取一些零碎的页把数据插进去。之后不同的段使用零碎页的时候都会从该区中取,直到该区中没有空闲空间,然后该区的状态就变成了FULL_FRAG。
- 每个段中的区对应的XDES Entry结构建立了三个链表,用于判断哪些区属于哪个段,分成FREE、NOT_FULL和FULL这3个链表。每个链表都对应一个List Base Node的结构
- 每个段都定义了一个INODE Entry结构来记录一下段中的属性,在第一个分组的第三个页面的类型是INODE中存储
- XDES Entry就是在表空间的第一个页面中保存的,直属于表空间的FREE链表的基节点、FREE_FRAG链表的基节点、FULL_FRAG链表的基节点在表空间的第一个页面(也就是FSP_HDR类型的页面)的File Space Header部分
- 系统表也被称为数据字典,它们都是以B+树的形式保存在系统表空间的某些页面中
十. 单表访问方法
- MySQL执行查询语句的方式称之为访问方法或者访问类型
- 通过主键或者唯一二级索引列来定位一条记录的访问方法定义为:const(唯一二级索引查询NULL值除外,会访问到多条记录)
- 搜索条件为二级索引列与常数等值比较,采用二级索引来执行查询的访问方法称为:ref
- 找出某个二级索引列的值等于某个常数的记录,还想把该列的值为NULL的记录:ref_or_null
- 利用索引进行范围匹配的访问方法称之为:range
- 遍历二级索引记录的执行方式称之为:index
- 使用全表扫描执行查询的方式称之为:all
- 优化器一般会根据single_table表的统计数据来判断到底使用哪个条件到对应的二级索引中查询扫描的行数会更少,选择那个扫描行数较少的条件到对应的二级索引中查询。然后将从该二级索引中查询到的结果经过回表得到完整的用户记录后再根据其余的WHERE条件过滤记录。
- 使用到多个索引来完成一次查询的执行方法称之为:index merge,分为intersection merge、union merge、
- intersectionm merge将各自条件经过二级索引得到的结果取交集再回表,即and。二级索引列是等值匹配的情况、主键列可以是范围匹配的情况下可以采用
- union merge为取并集,即or
- sort-union merge,在取并集之前先对各条件查找结果按主键进行排序
十一. 连接的原理
- 连接实际上是笛卡儿积
- 第一个需要查询的表,这个表称之为驱动表,在两表连接查询中,驱动表只需要访问一次,被驱动表可能被访问多次,访问次数取决于对驱动表执行单表查询后的结果集中的记录条数的连接执行方式称之为嵌套循环连接
- 内联:驱动表中的记录在被驱动表中找不到匹配的记录,该记录不会加入到最后的结果集
- 不论是内连接还是外连接,凡是不符合WHERE子句中的过滤条件的记录都不会被加入最后的结果集。对于外连接的驱动表的记录来说,如果无法在被驱动表中找到匹配ON子句中的过滤条件的记录,那么该记录仍然会被加入到结果集中
- join buffer就是执行连接查询前申请的一块固定大小的内存,每一条被驱动表的记录一次性和join buffer中的多条驱动表记录做匹配,可以显著减少被驱动表的I/O代价,称为基于块的嵌套连接算法
十二. 基于成本的优化
- 一条查询语句的执行成本由CPU成本和IO成本组成
- 读取一个页面花费的成本默认是1.0,读取以及检测一条记录是否符合搜索条件的成本默认是0.2。数字称为成本常数
- 查询优化器找出成本最低的执行计划的过程:根据搜索条件,找出所有可能使用的索引;计算全表扫描的代价,需要知道聚簇索引占有的页面数、该表中的记录数;计算使用不同索引执行查询的代价,需要范围区间数量、需要回表的记录数;对比各执行方案的代价,找出成本最低的那个
- 连接查询的成本由单次查询驱动表的成本、多次查询被驱动表的成本组成。
- 驱动表进行查询后得到的记录条数称之为驱动表的扇出
- 减少计算连接查询成本的方法:提前结束某种顺序的成本评估、系统变量optimizer_search_depth、根据某些规则不考虑某些连接顺序
- MySQL目前的实现中,并不能准确预测某个查询需要访问的块中有哪些块已经加载到内存中,有哪些块还停留在磁盘上
十三. InnoDB统计数据的收集
- InnoDB默认是以表为单位来收集和存储统计数据的,可以把某些表的统计数据(以及该表的索引统计数据)存储在磁盘上,把另一些表的统计数据存储在内存中。
- 把某个表以及该表索引的统计数据存放到磁盘上时,实际上是把这些统计数据存储到了两个表里
- 自动重新计算统计数据的过程是异步发生的,ANALYZE TABLE语句会立即重新计算统计数据,也就是这个过程是同步的
- 索引列不重复的值的数量这个统计数据对于MySQL查询优化器十分重要,因为通过它可以计算出在索引列中平均一个值重复多少行,它的应用场景主要是单表查询中的单点区间、连接查询对等值匹配连接的有索引列的被驱动表的列
- 最好不在索引列中存放NULL值才是正解。有的人认为NULL值代表一个未确定的值;有的人认为其实NULL值在业务上就是代表没有,所有的NULL值代表的意义是一样的;有的人认为NULL完全没有意义
十四. 基于规则的优化(查询重写)
- 条件化简:移除不必要的括号、常量传递、等值传递、移除没用的条件、表达式计算(如果某列不是以单独的形式作为表达式的操作数时,优化器是不会尝试对这些表达式进行化简的,因此最好让索引列以单独的形式出现在表达式中)
- Having子句与Where子句的合并:常量表检测(查询的表中只有一条记录或者没有记录、使用主键等值匹配或者唯一二级索引列等值匹配,通过这两种方式查询的表称作常量表)、外连接消除(指定的WHERE子句中包含被驱动表中的列不为NULL值的条件称之为空值拒绝,在被驱动表的WHERE子句符合空值拒绝的条件后,外连接和内连接可以相互转化)
- 由子查询结果集组成的表称为派生表
- 子查询分为只返回一个单一值的标量子查询、返回一条记录的行子查询、查询一个列数据的列子查询、包含很多行很多列的表子查询
- 对于包含不相关的标量子查询或者行子查询的查询语句来说,mysql会分别独立的执行外层查询和子查询,先执行子查询,再执行外层查询
- 相关的标量、行子查询的执行方式为,先从外层查询获取一条记录,从该记录中找出子查询涉及的值并执行子查询,根据子查询的结果判断外层查询where语句的条件是否成立,成立则加入结果集,否则就丢弃
- IN子查询优化,不直接将不相关子查询的结果集当作外层查询的参数,而是将该结果集写入一个临时表里,称之为物化表,过程称为物化,再建立两表的内联查询
- 子查询转化为semi-join,将表s1与s2半连接的意思是指,对于s1的某条记录来说,我们只关心在s2表中是否存在与之匹配的记录,不关心有多少条记录与之匹配,最终的结果集中只保留s1表的记录
- 半连接实现方法。Table pullout,当子查询的查询列表处只有主键或者唯一索引时,可以直接把子查询中的表上拉到外层查询的FROM子句中,并把子查询的搜索条件合并到外层查询的搜索条件中;DuplicateWeedout execution strategy,通过加入临时表时消除重复;LooseScan execution strategy,扫描索引但只取值相同的家具路第一条去做匹配操作的方式称作松散索引扫描;semi-join materialization execution strategy;FirstMatch execution strategy;
- 相关子查询并不是一个独立的查询,所以不能转换为物化表来执行查询
- 不能转为semi-join查询的子查询,不管子查询是相关的还是不相关的,都可以把IN子查询尝试转为EXISTS子查询
- ANY/ALL子查询优化,转化为MAX/MIN
- 如果[NOT] EXISTS子查询是不相关子查询,可以先执行子查询,得出该[NOT] EXISTS子查询的结果是TRUE还是FALSE,并重写原先的查询语句
- MySQL在执行带有派生表的时候,优先尝试把派生表和外层查询合并掉,如果不行的话,再把派生表物化掉执行查询。
十五. 查询优化之Explain
- explain语句可以查看某个查询语句的具体执行计划
- EXPLAIN语句输出的每条记录都对应着某个单表的访问方法,该条记录的table列代表着该表的表名
- 在连接查询的执行计划中,每个表都会对应一条记录,但是这些记录的id值都是相同的,出现在前边的表表示驱动表,出现在后边的表表示被驱动表
- 由于select_type为SUBQUERY的子查询由于会被物化,所以只需要执行一遍。select_type为DEPENDENT SUBQUERY的查询可能会被执行多次。
- possible_keys列中的值并不是越多越好,可能使用的索引越多,查询优化器计算查询成本时就得花费更长时间,所以如果可以的话,尽量删除那些用不到的索引
十六. optimizer trace优化器生成计划过程
- information_schema数据库下的OPTIMIZER_TRACE表中查看完整的优化过程
十七. InnnDB的Buffer Pool
- 建立buffer pool在内存中缓存之前访问的硬盘页信息
- 每个页对应的控制信息占用的一块内存称为一个控制块,和缓存页是一一对应的,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边
- 所有空闲的缓存页对应的控制块作为一个节点放到一个链表中,这个链表也可以被称作free链表,链表定义了一个基节点,里边儿包含着链表的头节点地址,尾节点地址,以及当前链表中节点的数量。基节点占用的并非buffer pool,而是单独申请的一块内存空间
- 用表空间号 + 页号作为key,缓存页作为value创建一个哈希表作缓存命中
- 被修改的缓存页叫做脏页,其控制块会作为一个节点加入到一个链表中,称之为flush链表
- 为了按照最近最少使用的原则去淘汰缓存页的链表可以被称为LRU链表(LRU的英文全称:Least Recently Used),只要我们使用到某个缓存页,就把该缓存页调整到LRU链表的头部,这样LRU链表尾部就是最近最少使用的缓存页
- 线性预读、随机预读和全表扫描会降低缓存命中,因此LRU链表被按比例分成两截,一部分存储使用频率高的,叫做热数据,一部分存储冷数据。当磁盘上的某个页面在初次加载到Buffer Pool中的某个缓存页时,该缓存页对应的控制块会被放到old区域的头部,这样针对预读到Buffer Pool却不进行后续访问的页面就会被逐渐从old区域逐出。针对全表扫描,规定在对某个处在old区域的缓存页进行第一次访问时就在它对应的控制块中记录下来这个访问时间,如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会被从old区域移动到young区域的头部,否则将它移动到young区域的头部。
- 脏页刷新到磁盘主要有两种方式,从LRU链表的冷数据中刷新一部分页面到磁盘,从flush链表中刷新一部分页面到磁盘。
- 在Buffer Pool特别大的时候,可以把它们拆分成若干个小的Buffer Pool,每个Buffer Pool都称为一个实例。当innodb_buffer_pool_size的值小于1G的时候设置多个实例是无效的
- 为了支持在服务器运行过程中调整buffer pool的大小,将一个Buffer Pool实例改为由若干个chunk组成,以chunk位单位项操作系统申请空间
十八. 事务
- 要么全做,要么全不做的规则称之为原子性
- 要保证其它的状态转换不会影响到本次状态转换,这个规则被称之为隔离性.
- 数据库某些操作的原子性和隔离性都是保证一致性的一种手段,在操作执行完成后保证符合所有既定的约束则是一种结果。
- 转换的结果将永久的保留,称为持久性
- 事务即具有ACID等规则,分为active、partially committed、failed、aborted、committed等状态,只有当事务处于提交的或者中止的状态时,一个事务的生命周期才算是结束了。
- 关闭事务的自动提交之后,在某些语句下依然会隐式提交
- 可以rollback到指定的保存点
十九. redo日志
- redo日志本质上只是记录了一下事务对数据库做了哪些修改,占用空间小,顺序写入磁盘
- 物理日志:只需记录在某个页面的某个偏移量处修改了几个字节,以及具体的修改内容,此外还有逻辑日志
- 只有一条redo日志称作乐观插入、对多个页面进行修改,也就意味着会产生多条redo日志,称作悲观插入
- 一个事务由许多mini-transaction组成,每个mtr包含若干条redo日志,为一组
- 为了更好的进行系统奔溃恢复,通过mtr生成的redo日志都放在了大小为512字节的block中,redo log buffer缓冲redo日志,以一组redo日志为单位写入
- 将log buffer中的redo日志刷新到磁盘的本质就是把block的镜像写入日志文件中
- 每一组由mtr生成的redo日志都有一个唯一的LSN值与其对应,LSN值越小,说明redo日志产生的越早
- flush链表中的脏页按照修改发生的时间顺序进行排序,也就是按照oldest_modification代表的LSN值进行排序,被多次更新的页面不会重复插入到flush链表中,但是会更新newest_modification属性的值
- 判断某些redo日志占用的磁盘空间是否可以覆盖的依据就是它对应的脏页是否已经刷新到磁盘里,依据为checkpoint
- 崩溃恢复:通过checkpoint确定恢复的起点,终点为最后一个未填满的block
二十. undo日志
- 为了回滚而记录的撤销日志,就是undo日志
- 如果某个事务执行过程中对某个表执行了增、删、改操作,那么InnoDB存储引擎就会给它分配一个独一无二的事务id,事务id可能不连续,由一个内存全局变量和一个磁盘字段共同维护
- 删除的事务只是将该行数据的隐藏删除字段值修改,该删除语句所在的事务提交之后,会有专门的线程后来真正的把记录删除掉。所谓真正的删除就是把该记录从正常记录链表中移除,并且加入到垃圾链表中
- update分为更新主键与不更新主键。不更新主键时,如果更新前后所占存储空间不变,可以就地更新,否则先删除(直接更改链表结构)再插入;更新主键时,先做软删除,再插入
- undo日志有特有的存储页格式
- 一个事务中最多有4个以Undo页面为节点组成的链表(插入、更新、临时表、普通表)
- 一个Undo页面链表被重用的条件:该链表中只包含一个Undo页面、该Undo页面已经使用的空间小于整个页面空间的3/4
- 回滚段存放了各个Undo页面链表的frist undo page的页号,不同的回滚段可能分布在不同的表空间中
- 对普通表和临时表划分不同种类的回滚段的原因:在修改针对普通表的回滚段中的Undo页面时,需要记录对应的redo日志,而修改针对临时表的回滚段中的Undo页面时,不需要记录对应的redo日志。
二十一. 事务隔离级别和MVCC
- 脏写:一个事务修改了另一个未提交事务修改过的数据
- 脏读:一个事务读到了另一个未提交事务修改过的数据
- 不可重复读:一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值
- 幻读:一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来
- mysql允许一定的差错换取性能,但任何情况下,脏写都不被允许
- ReadView判断版本链中的哪个版本是当前事务可见的。隔离级别的的一个非常大的区别就是它们生成ReadView的时机不同
- MVCC(Multi-Version Concurrency Control ,多版本并发控制)指的就是在使用READ COMMITTD、REPEATABLE READ这两种隔离级别的事务在执行普通的SEELCT操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能
二十二. 锁 (悲观锁)
- 当一个事务想对某条记录做改动时,首先会看看内存中有没有与这条记录关联的锁结构,当没有的时候就会在内存中生成一个锁结构与之关联
- 没有获取到锁时,会新建锁结构,将相关字段设为等待,当前一事务释放该锁时,修改此字段
- 采用MVCC方式的话,读-写操作彼此并不冲突,性能更高,采用加锁方式的话,读-写操作彼此需要排队执行,影响性能
- 锁根据功能分为共享锁S锁与独占锁X锁,根据维度分为行锁与表锁
- IS、IX锁是意向性表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录,也就是说其实IS锁和IX锁是兼容的,IX锁和IX锁是兼容的
- 其他引擎不支持事务,所以锁是针对会话而言的
- auto_increment 采用AUTO-INC锁,也就是在执行插入语句时就在表级别加一个AUTO-INC锁,然后为每条待插入记录的AUTO_INCREMENT修饰的列分配递增的值,在该语句执行结束后,再把AUTO-INC锁释放掉
- gap锁可以锁住间隙
- 一个事务对新插入的记录可以不显式的加锁(生成一个锁结构),但是由于事务id的存在,相当于加了一个隐式锁。别的事务在对这条记录加S锁或者X锁时,由于隐式锁的存在,会先帮助当前事务生成一个锁结构,然后自己再生成一个锁结构后进入等待状态