深入解读MySQL InnoDB存储引擎Update语句执行过程
参考b站up 戌米的论文笔记 https://www.bilibili.com/video/BV1Tv4y1o7tA/
书籍《mysql是怎样运行的》
极客时间《mysql实战45讲》
一丶Mysql整体架构

MySQL 可以分为 Server 层和存储引擎层两部分
1.Server 层
Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
- 连接器 - 连接器负责跟客户端建立连接、获取权限、维持和管理连接 
- 查询缓存 - 对于查询语句,mysql server层会将查询语句和对应的结果,使用key - value的缓存结构进行缓存,但是一旦发生更新,那么查询缓存就需要失效。因此查询缓存在高版本的mysql中已经被移除 
- 分析器 - 分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。 - 然后做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。 
- 优化器 - 优化器是在表里面有多个索引的时候,决定使用哪个索引。或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。 
- 执行器 - MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。 
2.存储引擎层
存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。下面我们对比两个常用的存储引擎
| MyISAM | InnoDB | |
|---|---|---|
| 存储结构 | Myisam 创建表后生成的文件有三个,分别为: frm:创建表的语句 MYD:表里面的数据文件(myisam data) MYI:表里面的索引文件(myisam index) | Innodb 创建表后生成的文件有两个,分别为: frm:创建表的语句 idb:表里面的数据+索引文件 | 
| 索引 | 非聚集索引,MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。 | 聚集索引,聚集索引的文件存放在主键索引的叶子节点上 | 
| 事务支持 | 不提供事务支持 | 提供事务支持 | 
| 锁的粒度 | 只支持表级锁 | 支持行级锁。 | 
| 存储表的具体行数 | 保存表的总行数,如果select count() from table;会直接取出出该值。 | 没有保存表的总行数,如果使用select count(*) from table;就会遍历整个表,消耗相当大,但是在加了wehre条件后,myisam和innodb处理的方式都一样。 | 
以下的分析针对 update t set a='1' where 主键 = 1这条语句

二丶开启事务
在mysql中,无论用户是否手动开启一个事务,sql都是在一个事务中进行的。我们可以使用start transaction开启一个事务,commit提交事务,rollback回滚事务。
默认情况下,mysql存在自动提交(autocommit=1),这时候即使我们没有显式开启事务,直接执行update语句,那么mysql会隐式的开启一个事务,并在这条update执行结束后自动提交
如果set autocommit = 0 或者显式开启了一个事务,那么update执行结束后不会自动提交,而是需要手动发起commit 或者rollback
无论式显式事务,还是隐式事务,mysql都会在事务内部第一次执行增删改操作的时候,给事务分配一个事务号
三丶Sql解析,查询计划生成
mysql服务器层会从连接中读取sql语句,然后进行词法解析,语法解析,查询优化(为什么update语句需要查询优化?不查出来怎么知道修改哪些行数据昵)最终生成一个AST树,这便是物理执行计划,执行器会根据执行计划,调用存储引擎的接口,

四丶查询需要修改的数据
mysql innodb存储引擎对磁盘的读取是以页为单位的,为了避免每次都从磁盘读取数据,innodb存在buffer pool使用LRU链表维护最近访问到的页,为了更快的从buffer pool中查找到目标页,innodb 还使用表空间号和页号作为key,页作为value,形成Hash表。如果我们目标页已经在buffer pool中那么直接返回目标页,如果不在那么需要进行磁盘io加载目标页到内存,然后缓存到buffer pool中
1.buffer pool是如何维护页在内存中的
buffer poo中存在三个关键的链表结构
- Free List 空闲链表,链表中将空闲缓冲页的控制块(控制块中记录了缓冲页的位置)进行串联,用于管理未被使用的缓冲池空间
- LRU List,最近最少使用链表,利用LRU算法在buffer pool满后淘汰冷门数据页(innodb 为了应对预读,全表扫描,对应LRU链表进行了改进,分成young区,和old区,解决预读:innodb规定当磁盘某个页面在初次加载到buffer pool中某个缓冲页时,该缓冲页对应的控制块会放在old区域的头部,这样预读到的且后续如果不进行后续访问的页面会逐渐从old区移除,而不影响young区使用频率高的缓冲页。,解决全表扫描:nnodb规定对于某个处于old区的缓冲页第一次访问时,就在其控制块中记录下访问时间,如果后续访问的时间和第一次访问的时间,在某个时间访问间隔内(innodb_old_blocks_time可以进行设置)那么页面不会从old区移动到young区,反之移动到young区中。这个时间间隔默认时1000ms,基本上多次访问同一个页面中的多个记录的时间不会超过1s,解决热门数据经常需要移动到LRU链头部的问题: innodb规定只有被访问的缓冲页位于young区的前1/4范围外,才会进行移动,所以前1/4的高热度的数据,不会频繁移动)
- Flush List,脏链,维护在buffer pool中进行了修改,后续需要刷新到磁盘的缓冲页信息,innodb修改后的页不会立即刷盘,而是使用Flush list记录,后台存在线程定时进行刷脏
2.怎么从16k的页中找到目标数据

结合B+树索引结构,执行引擎根据页号找到根节点,然后根据根节点中的索引数据进行比较,找到子节点,重复此过程直至找到叶子节点所在的页。
到了叶子节点所在的页后,根据叶子节点页中的Page Dictionary中的槽找到目标记录所在的组,然后遍历这一组中的记录,找到目标记录。如果是范围查询,还需要根据B+树叶子节点间的双向指针继续查找,直到找到不符合要求的记录位置。(为了避免我们在遍历B+树的时候,其他线程修改了B+树的结构,此过程还需要对B+树进行加闩锁)(详细可看 Mysql索引(究极无敌细节版中的InnoDB索引方案一节)
五丶检验锁和加锁
1.Mysql中的锁
- 元数据锁MDL,mysql服务器层的MDL主要是避免操作数据的同时存在另外线程修改表结构,实现二者的互斥 
- innodb表锁 - 表级S锁,X锁 - 使用 - Lock Tables t Read,innodb存储引擎会对表t加共享锁- 使用 - Lock tables t write,innodb存储引擎会对表t加独占锁
- 表级意向锁: - innodb存储引擎中,当对表中某些记录加S锁之前,会在表上加上一个IS锁,同样加X锁之前会加表级IX锁,这里的I表示意向锁, - S or X表示共享还是互斥,表级意向锁存在的目的是后续对表加S锁,X锁的时候,可以快速判断表中是否存在加锁的记录,避免遍历每一个记录查看是否被加锁。
 
- innodb 行锁 - Record Lock
 - 官方名称 - Lock_REC_NOT_GAP - 记录锁有S锁和X锁,S型记录锁之间可以共享,X型记录锁和S型记录锁,X型记录锁互斥 - GAP Lock
 - innodb的可重复读级别,使用词锁解决幻读问题,前面我们说过,其难点在于,加锁的时候幻影记录还未出现。官方使用 - Lock_GAP实现如下操作 - 此处的gap锁可以反之其他事务在number为8记录前面的间隙插入新的记录,在区间(3,8)内无法进行插入操作,当另外一个事务要插入number为4的记录时,首先需要定位到该条记录的下一条记录,也就是number为8的记录,此时number为8的记录具备gap锁,所以将阻塞插入操作,直到gap锁被释放,其他事务才能进行插入。 
 - gap锁出现的目的,就是为了防止插入幻影记录,如果对记录上gap锁,并不会限制其他事务对记录加记录锁。- innodb有两个虚拟的记录 - Infimum(虚拟最小),Supermun(虚拟最大)当我们想在(xx,正无穷)范围锁住幻影记录时就可以对Supermun加gap锁。- Next-Key Lock
 - Next-Key Lock = 记录锁 + gap锁,既锁住记录,也锁住记录之前的间隙  - Insert Intention Lock
 - 插入意向锁,表示事务想在某个间隙插入新的记录,但是当前处于等待状态。 - 比如事务A持有(4,8)范围内的gap锁,事务B和C,想插入(4,8)范围内的记录,就会在内存中生成事务B,C对应的插入意向锁,当前事务A释放gap锁的时候,将唤醒事务B和C,事务B和C可以同时获取插入意向锁,然后进行插入。插入意向锁并不会阻止对记录继续上锁。 - 隐式锁
 - 为事务生成内存中的锁结构并不是一个0成本的事情,为了节省这个成本,提出 - 隐式锁的概念。- 当一个事务插入语一条记录A,其他事务 - select xxx Lock in share mode读取记录A(获取记录A的S锁),或者使用- select xxx for update(获取记录A的X锁)
- 立即修改记录A(获取x锁)
 - 对于聚簇索引来说,有一个隐藏列 - trx_id此列存储着最后更改记录的事务id,在当前事务A插入记录后,便是存储着当前事务A的id,其他事务B企图获取x锁,s锁的时候,就需要下先看一下,trx_id隐藏列对应的事务是否存活,如果不是那么正常获取,反之需要为当前事务A创建一个x锁内存结构,并标记is_waiting为false,然后事务B将为自己创建一个锁结构,is_waiting 为true然后事务B进入等待状态- 对于二级索引来说,其不具备隐藏列 - trx_id但是在二级索引页面的page header中的- page_maxt_trx_id属性,记录了改动页面最大的事务id,如果其属性值小于当前最小的活跃事务id,那么说明对页面的改动事务已经提交,否则需要定位到二级索引记录,然后回表对聚簇索引进行上述聚簇索引的操作。- 一个事务对新插入的记录不需要显示的加锁,由于事务id的存在相当于加了一个隐式锁,别的事务需要加S锁或者X锁的时候,先帮之前的事务生成锁结构,然后为自己生成锁结构,再进入阻塞状态。隐式锁起到了延迟加锁的作用,也许别的事务不会获取于隐式锁冲突的锁,这时候可以减少内存中生成锁结构。 
2.一条Update语句涉及的锁

2.1加共享元数据锁
为了避免当前事务操作的时候,存在另外的用户对当前表进行DDL操作,mysql首先会为当前操作的表加共享元数据锁。这个过程可能存在阻塞的可能,如果当前事务企图加共享元数据锁的时候,存在另外一个事务正在对表进行DDL操作,这时候另外一个事务上了互斥元数据锁,这时候会出现当前事务阻塞的情况
2.2 加表级意向互斥锁
此阶段也可能存在阻塞,但是由于innodb支持行锁,基本上很少有人给表上锁。如果执行当前事务之前存在另外一个事务给表上了表记共享锁,表记互斥锁,那么当前操作也会被阻塞。
加表记意向锁的好处在于,若没有意向锁,那么其他事务对表加锁的时候,需要遍历表中所有记录确保当前行中的记录没有被上锁
2.3 行锁

innodb中的行锁,其实是在内存中,为当前行生成一个锁结构,记录事务id,索引信息,锁信息,锁类型等.如果当前事务加锁的时候,记录并没有加锁,那么会生成一个锁结构存储于内存中。如果锁已经被占用那么会挂起当前事务,直到锁被释放后唤醒当前事务。
六丶修改数据和生成日志
在成功上锁之后,就可以放心的更新数据了,innodb将写三部分内容
1.写缓冲页
- 如果修改前后这行数据的大小完全没有发生改变,每一个字段所占用的大小和之前一样,那么进行就地更新
- 但凡存在任何一个字段的大小发生了改变,那么删除旧记录,将旧纪录放入页的垃圾链表中,插入新的记录
不进行需要修改sql中指定的字段,还需要更新trx_id=当前事务的id,roll_pointer = 指向undo log

buffer pool中脏页的刷盘依赖于后台定时任务线程定时进行刷新,如果修改到此为止将存在数据丢失的问题,为此innodb存储引擎还需要写入以下两种日志
2.写undo log
undo log是为了记录行数据修改前的结果,用于回滚和mvcc。undo log 可以分为两种——记录insert undo log,和 update/delete undo log,生成的undo log会写入到undo log buffer。
- insert undo log 如何帮助回滚刚insert的一行数据 - insert undo log实际上记录了插入行数据的主键,回滚是只需要根据主键进行删除即可  
- update/delete undo log怎么回滚update/delete的一行数据 - update/delete操作的回滚需要记录操作前数据的完整信息  - update/delete undo log中的- trx_id,- roll_pointer是为了支持mvcc,并且还需要记录修改删除前后的列信息,便于回滚恢复记录
2.1.mvcc

如图多个版本的数据,在undo log中进行了记录,并且使用roll_pointer,进行串联,形成版本链。快照读查询语句执行前,或者使用start transaction with consistent snapshot(立即生成read view)会生成一个read view(一致性视图,如下)

read view包含如下几个字段
- m_ids:在生成read view时,当前系统中活跃的读写事务id列表
- min_trx_id:生成read view时,当前系统中活跃的读写事务中最小事务id,也就是m_ids中的最小值
- max_trx_id:生成read view时,系统应该分配给下一个事务的事务id值
- creator_trx_id:生成该read view的事务的事务id
2.2如何利用一致性视图判断数据是否可见
- 如果被访问版本的trx_id和creator_trx_id相同,意味着当前事务在访问自己修改的记录,自然可见
- 如果访问版本的trx_id属性值小于read view中的min_trx_id表明此版本是生成read view之前已经提交的事务,那么自然可见
- 如果访问版本的trx_id,大于等于read view中的max_trx_id说明,当前版本数据是生成read view后开启事务产生的,那么自然不可见
- 如果访问版本的trx_id介于min_trx_id和max_trx_id之间,需要判断trx_id是否位于m_ids列表中,如果在说明创建read view时生成该版本的事务还是活跃的,那么该版本,不可被访问,如果不在说明创建read view 时生成该版本的事务已经提交,可以被访问到
2.3Read Committed和 Repeatable Read的不同
- Read Committed——每次读取数据前都生成一个Read View - 这样可以保证生成Read view 中的m_ids是实时活跃事务id集合,也许第一次读取的时候事务A没提交,其id位于m_ids中,但是第二次读取的时候事务A提交了,事务A将不位于m_ids中,这样在第二次读取的时候,通过m_ids判断事务A是否提交的时候,可以得到事务A已经提交了,然后让事务A版本产生的数据可见(见2.2.4中的内容)。 
- Repeatable Read——如果使用begin开启事务那么在第一次查询的时候生成Read view,如果使用 - start transaction with consistent snapshot那么执行的时候就会生成read view- 这样可以保证当前事务从头到尾都是read view中记录的内容是一致的,第一次读取的时候事务A没有提交,那么不可见,但是第二次读取的时候事务A提交了,但是read view的 - m_ids和- max_trx_id可以判断事务A不可见,比如事务A事务id小于max_trx_id意味着生成read view是事务A启动但是没提交,即使第二次读事务A提交了,但是- m_ids中还是包含事务A,那么不可见。如果事务A事务id大于max_trx_id,那么自然第二次还是大于max_trx_id,也是不可见的,从而实现了可重复读。
3.写redo log
redo log 记录事务修改了哪个表空间(space id属性),哪个页(page number 属性),修改后的值(data属性)
即使是非常简单的一条变更sql,往往涉及到多出的改动,比如需改sql数据的字符数发生了变更,需要先删除,后插入。并且需要对上一条行记录的next_record 属性进行修改,页中行数据的修改,往往同样需要修改page header,page dictionary等内容,并且可能伴随着B+树节点分裂和合并。为了解决存在多种不同修改的问题,innodb存在多种类型的redo log。
3.1 mini-transaction
innodb 把一次变更分为多个mini-transaction(MTR)一个MTR包含一组redo log,这一组redo log以一个特殊类型的redo log作为类型,恢复的时候,这一组redo log具备原子性,只有检测到特殊类型的redo log才任何一组redo log是完整的才会进行恢复(B+树叶子节点的分裂,不能说分裂一半)
3.2 log buffer
生成redo log,会写入到log buffer,log buffer是一块连续的内存空间,由一个个大小为512B的log block组成,默认16mb大小。生成的redo log会找最小的一个redo log block 顺序写入

- buf_next_to_write 标记redo log已经落盘的位置 
- buf_free 是标记buffer pool 剩下的空闲空间 
3.3 redo log 刷盘的时机:
- 事务提交
- log buffer 空间低于50%
- 后台线程周期性刷盘
- mysql服务正常关闭
- 做checkpoint
3.4 redo log 进行崩溃恢复
从checkpoint_lsn位置开始读取redo log,来恢复脏页和undo log,然后通过undo log把所有未提交的事务的脏页进行回滚
七丶本地提交
提交阶段 innodb存储引擎需要落盘redo log,mysql服务器层需要落盘binlog
1.binlog
二进制逻辑日志,在逻辑备份和主备复制中发挥重要作用,具备三种格式
- statement - 每一条会修改数据的 SQL 都会记录在 binlog 中。 - Statement 模式只记录执行的 SQL,不需要记录每一行数据的变化,因此极大的减少了 binlog 的日志量,避免了大量的 IO 操作,提升了系统的性能。 - 但是,正是由于 Statement 模式只记录 SQL,而如果一些 SQL 中包含了函数,那么可能会出现执行结果不一致的情况。比如说 - uuid()函数,每次执行的时候都会生成一个随机字符串,在 master 中记录了 uuid,当同步到 slave 之后,再次执行,就获取到另外一个结果了。- 所以使用 Statement 格式会出现一些数据一致性问题。 
- row - Row 格式不记录 SQL 语句上下文相关信息,仅仅只需要记录某一条记录被修改成什么样子了。 - Row 格式的日志内容会非常清楚的记录下每一行数据修改的细节,这样就不会出现 Statement 中存在的那种数据无法被正常复制的情况。 - 不过 Row 格式也有一个很大的问题,那就是日志量太大了,特别是批量 update、整表 delete、alter 表等操作,由于要记录每一行数据的变化,此时会产生大量的日志,大量的日志也会带来 IO 性能问题 
- mixed - Row 格式不记录 SQL 语句上下文相关信息,仅仅只需要记录某一条记录被修改成什么样子了。 - Row 格式的日志内容会非常清楚的记录下每一行数据修改的细节,这样就不会出现 Statement 中存在的那种数据无法被正常复制的情况。 - 不过 Row 格式也有一个很大的问题,那就是日志量太大了,特别是批量 update、整表 delete、alter 表等操作,由于要记录每一行数据的变化,此时会产生大量的日志,大量的日志也会带来 IO 性能问题 
2.怎么保证binlog 和redo log状态一致
mysql采用了内部XA事务的机制保证binlog,和redo log的状态顺序一致,通过两阶段提交的方式实现,两阶段提交存在一个协调者和多个参与者,在mysql中binlog是协调者,redo log是参与者
2.1mysql的两阶段提交
- prepare阶段
- innodb刷redo log到磁盘,redo log刷盘完成后,修改事务状态为TRX_PREPARED
- prepare如果失败,那么事务会回滚,而prepare成功那么进入commit阶段
 
- commit阶段
- mysql服务器层写入binlog,写入完成后,修改事务状态为TRX_NOT_STARTED,表示事务已经成功提交
 
2.2宕机的处理
- 事务转换若为TRX_ACTIVE那么回滚事务
- 事务状态为TRX_NOT_STARTED 那么说明redo log 和binlog都成功落盘,这时候任务事务已经提交
- 恢复的时候如果发现事务状态为TRX_PREPARED,根据binlog的状态判断是提交还是回滚。
- 若binlog 写入失败,那么回滚
- 若binlog写入成功那么提交并修改事务为TRX_NOT_STARTED
 
3.redo log 和binlog 物理落盘策略
3.1 innodb_flush_log_at_trx_commit空置redo log的落盘
- 0表示每秒进行一次刷新
- 1表示每次事务提交时落盘
- 2表示每次事务提交都只写redolog缓冲写道操作系统缓存中,由操作系统决定何时刷盘
3.2 sync_binlog控制binlog的落盘
- 0 表示当事务提交之后,MySQL不做fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让Filesystem自行决定什么时候来做同步,或者cache满了之后才同步到磁盘。
- n表示当每进行n次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。
- 1表示每次事务提交都刷盘
八丶主备复制
主库写入binlog之后,备库的io线程会读取主库的binlog,并转存为本地的中继日志relay log,备库上的sql线程读取relay log并在本地执行
1.主备复制的策略
- 异步复制:主库写完binlog后即可返回提交成功,无需等待备库响应
- 半同步复制:主库接受指定数量的备机转储relay log成功的ACK后可返回提交成功(还支持超时时间,超时没有返回那么主库返回成功)
- 同步复制:主库等备库回放relay log执行完,事务之后才能返回提交成功
不同的策略,其性能和一致性要求不同,也影响到主库能否返回
九丶返回提交成功
至此mysql会给客户端返回成功
十丶脏页刷盘
innodb后台有专门的线程负责将buffer pool中的脏页刷新到磁盘
- 从LRU链表中的冷数据刷新一部分页面到磁盘 - 后台线程定时从LRU链表尾部扫描一些页面,扫描的页面数量可以通过 - innodb_lru_scan_depth指定,如果在LRU中发现脏页,那么刷新到磁盘
- 从flush链表刷新一部分页面到磁盘 - 后台线程也会定时从flush链表中刷新一部分页面到磁盘,刷新速率取决于系统是否繁忙 
如果后台线程刷新的很慢,且有新的页面需要进行缓存,这时候会从LRU链表尾部看看是否有可以直接释放的非脏页,如果不存在那么需要刷盘然后缓存新的页。
深入解读MySQL InnoDB存储引擎Update语句执行过程的更多相关文章
- MySQL InnoDB存储引擎体系架构 —— 索引高级
		转载地址:https://mp.weixin.qq.com/s/HNnzAgUtBoDhhJpsA0fjKQ 世界上只两件东西能震撼人们的心灵:一件是我们心中崇高的道德标准:另一件是我们头顶上灿烂的星 ... 
- 浅析Mysql InnoDB存储引擎事务原理
		浅析Mysql InnoDB存储引擎事务原理 大神:http://blog.csdn.net/tangkund3218/article/details/47904021 
- mysql innodb存储引擎介绍
		innodb存储引擎1.存储:数据目录.有配置参数为“ innodb_data_home_dir ” .“ innodb_data_file_path ” 和 “innodb_log_group_ho ... 
- MySQL InnoDB存储引擎中的锁机制
		1.隔离级别 Read Uncommited(RU):这种隔离级别下,事务间完全不隔离,会产生脏读,可以读取未提交的记录,实际情况下不会使用. Read Committed (RC):仅能读取到已提交 ... 
- MySQL InnoDB存储引擎undo redo解析
		本文介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo Log Undo Log 为了实现事务原子,在MySQL数据库InnoDB存储引擎,还使用Undo Log(简称:MVCC ... 
- MySQL Innodb 存储引擎学习篇
		master thread的县城优先级别最高.其内部由几个循环(loop)组成:主循环(loop).后台循环(background loop).刷新循环(flush loop).暂停循环(suspen ... 
- MySQL InnoDB 存储引擎原理浅析
		注:本文主要基于MySQL 5.6以后版本编写,多数知识来着书籍<MySQL技术内幕++InnoDB存储引擎>,本文章仅记录个人认为比较重要的部分,有兴趣的可以花点时间读原书. 一.MyS ... 
- MySQL InnoDB存储引擎事务的ACID特性
		1.前言 相信工作了一段时间的同学肯定都用过事务,也都听说过事务的4大特性ACID.ACID表示原子性.一致性.隔离性和持久性.一个很好的事务处理系统,必须具备这些标准特性: 原子性(Atomicit ... 
- MySQL InnoDB存储引擎
		200 ? "200px" : this.width)!important;} --> 介绍 本篇文章是对Innodb存储引擎的概念进行一个整体的概括,innodb存储引擎的 ... 
- MySQL InnoDB 存储引擎探秘
		在MySQL中InnoDB属于存储引擎层,并以插件的形式集成在数据库中.从MySQL5.5.8开始,InnoDB成为其默认的存储引擎.InnoDB存储引擎支持事务.其设计目标主要是面向OLTP的应用, ... 
随机推荐
- AlertManager 之微信告警模板,UTC时间错8个小时的解决办法
			注意事项: alertmanager中的web页面显示的报警时间是UTC时间,错8个小时,企业微信报警模板中已经修改过来了 下面配置可以作为参考: 1.prometheus操作 1.1 配置告警规则, ... 
- .NET6 JWT(生成Token令牌)
			一.Net 6环境下的.net core项目里如何使用JWT. 第一步,在Nuget引入JWT.Microsoft.AspNetCore.Authentication.JwtBearer这两个NuGe ... 
- 线性回归大结局(岭(Ridge)、 Lasso回归原理、公式推导),你想要的这里都有
			本文已参与「新人创作礼」活动,一起开启掘金创作之路. 线性模型简介 所谓线性模型就是通过数据的线性组合来拟合一个数据,比如对于一个数据 \(X\) \[X = (x_1, x_2, x_3, ..., ... 
- 关于HM NISEDIT在新版系统下编译并运行提示权限不足问题的解决方案
			如果你使用过NSIS为你的项目制作过安装包,那HM nisedit一定是你绕不过去的槛,作为NSIS号称的最佳免费IDE/编辑器,功能齐全,与NSIS配合性能强悍,实至名归.只是开发作者最后版本更新在 ... 
- C# 传不定参数
			1 public class MyClass 2 { 3 public static void UseParams(params int[] list) 4 { 5 for (int i = 0; i ... 
- linux操作系统运行一个java程序并外网访问
			(一)安装jdk 1.新建文档java : mkdir java 2.进入java并且下载jdk 下载jdk : wget --no-check-certificate --no-cooki ... 
- 发送HTTP请求方法- 留着自用
			/** * 发送HTTP请求方法,目前只支持CURL发送请求 * @param string $url 请求URL * @param array $data POST的数据,GET请求时该参数无效 * ... 
- dp优化 | 各种dp优化方式例题精选
			前言 本文选题都较为基础,仅用于展示优化方式,如果是要找题单而不是看基础概念,请忽略本文. 本文包含一些常见的dp优化("√"表示下文会进行展示,没"√"表示暂 ... 
- 6.pygame-搭建主程序
			职责明确 新建plane_main.py 封装主游戏类 创建游戏对象 启动游戏 新建plane_sprites.py 封装游戏中所有需要使用的精灵子类 提供游戏的相关工具 #plane_sprit ... 
- k8s机器群扩容问题
			今天遇到了一个很奇葩的问题.公司新人吧k8s集权扩容完后,发现服务器上的磁盘没有做分区2T磁盘没有做任何动作 把两台slave节点扩容上去的节点弄到了/目录.由于是生产环境情况比较特殊并没通过kube ... 
