⛳ MVCC 原理详解

🎍目录

  • ⛳ MVCC 原理详解
    • 🐾 一、事务回顾
      • 📐 1.1、什么是数据库事务,为什么要有事务
      • 🎉 1.2、事务包括哪几个特性?
      • 🎍 1.3、事务并发存在的问题
        • 1.3.1、脏读
        • 1.3.2、不可重复读
        • 1.3.3、幻读
      • 🎒 1.4 四大隔离级别
        • 1.4.1 读未提交
        • 1.4.2 读已提交
        • 1.4 3 可重复读
        • 1.4.4 串行化
      • 🎃 1.5、MySQL数据库是如何保证事务的隔离性的呢?
    • 🏭 二、什么是 MVCC ?
    • 🎨 三、MVCC 实现的关键知识点
      • 🏭 3.1、事务版本号
      • 👣 3.2、隐式字段
      • 🐾 3.3、undo log
      • 📝 3.4、版本链
      • 💭 3.5、快照和当前读
      • 🏀 3.6、Reader View
    • 🎁 四、MVCC的演示过程

⛳ MVCC 原理详解

🐾 一、事务回顾

📐 1.1、什么是数据库事务,为什么要有事务

事务,由一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

假如 A 转账给 B 100 元,先从 A 的账户里扣除 100 元,再在 B 的账户上加上 100 元。如果扣完 A 的 100 元后,还没来得及给 B 加上,银行系统异常了,最后导致 A 的余额减少了,B 的余额却没有增加。所以就需要事务,将 A 的钱回滚回去,就是这么简单。

为什么要有事务呢? 就是为了保证数据的最终一致性。

🎉 1.2、事务包括哪几个特性?

事务四个典型特性,即 ACID,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

  • 原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。
  • 一致性:指在事务开始之前和事务结束以后,数据不会被破坏,假如 A 账户给 B 账户转 10 块钱,不管成功与否,A 和 B 的总金额是不变的。
  • 隔离性:多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。
  • 持久性:表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。

🎍 1.3、事务并发存在的问题

事务并发会引起脏读、不可重复读、幻读问题;

1.3.1、脏读

如果一个事务读取到了另一个未提交事务修改过的数据,我们就称发生了脏读现象。

假设现在有两个事务 A、B :

  1. 假设现在 Jay 的余额是 100 ,事务 A 正在准备查询 Jay 的余额;
  2. 事务 B 先扣减 Jay 的余额,扣了 10 ,但是还没提交;
  3. 最后 A 读到的余额是 90 ,即扣减后的余额;

image-20230829204656632

**脏读:**因为事务 A 读取到事务 B 未提交的数据,这就是脏读。

1.3.2、不可重复读

同一个事务内,前后多次读取,读取到的数据内容不一致;

假设现在有两个事务 A 和 B:

  • 事务 A 先查询 Jay 的余额,查到结果是 100
  • 这时候事务 B 对 Jay 的账户余额进行扣减,扣去 10 后,提交事务
  • 事务 A 再去查询 Jay 的账户余额发现变成了 90

image-20230829205204066

不可重复读:事务 A 被事务 B 干扰到了!在事务 A 范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读

1.3.3、幻读

如果一个事务先根据某些搜索条件查询出一些记录,在该事务未提交时,另一个事务写入了一些符合那些搜索条件的记录(如 insert、delete、update),就意味着发生了幻读

假设现在有两个事务 A、B:

  • 事务 A 先查询 id 大于 2 的账户记录,得到记录 id=2 和 id=3 的两条记录
  • 这时候,事务 B 开启,插入一条 id=4 的记录,并且提交了
  • 事务 A 再去执行相同的查询,却得到了 id=2,3,4 的 3 条记录了。

image-20230829205502421

**幻读:**事务 A 查询一个范围的结果集,另一个并发事务 B 往这个范围中插入新的数据,并提交事务,然后事务 A 再次查询相同的范围,两次读取到的结果集却不一样了,这就是幻读。

🎒 1.4 四大隔离级别

问题:四大隔离级别,都会存在哪些并发问题?

为了解决并发事务存在的脏读、不可重复读、幻读等问题,数据库大叔设计了四种隔离级别。分别是读未提交,读已提交,可重复读,串行化(Serializable)

image-20230829205600936

  • Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED
  • Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。

1.4.1 读未提交

读未提交隔离级别,只限制了两个数据不能同时修改,但是修改数据的时候,即使事务未提交,都是可以被别的事务读取到的,这级别的事务隔离有脏读、不可重复读、幻读的问题;

1.4.2 读已提交

读已提交隔离级别,当前事务只能读取到其他事务提交的数据,所以这种事务的隔离级别解决了脏读问题,但还是会存在不可重复读、幻读问题;

1.4 3 可重复读

可重复读隔离级别,限制了读取数据的时候,不可以进行修改,所以解决了不可重复读的问题,但是读取范围数据的时候,是可以插入数据,所以还会存在幻读问题;

1.4.4 串行化

事务最高的隔离级别,在该级别下,所有事务都是进行串行化顺序执行的。可以避免脏读、不可重复读与幻读所有并发问题。但是这种事务隔离级别下,事务执行很耗性能。

🎃 1.5、MySQL数据库是如何保证事务的隔离性的呢?

数据库是通过加锁,来实现事务的隔离性的。这就好像,如果你想一个人静静,不被别人打扰,你就可以在房门上加上一把锁。

加锁确实好使,可以保证隔离性。比如串行化隔离级别就是加锁实现的。但是频繁的加锁,导致读数据时,没办法修改,修改数据时,没办法读取,大大降低了数据库性能

那么,如何解决加锁后的性能问题的?

答案就是,MVCC 多版本并发控制!它实现读取数据不用加锁,可以让读取数据同时修改。修改数据时同时可读取。

🏭 二、什么是 MVCC ?

MVCC 体现了两种思想:

  1. MVCC 是 写时复制(copy on writer)思想的一种体现
  2. MVCC 是 以空间换时间 思想的一种体现;

MVCC (Muti-Version Concurrency Control )多版本并发控制,是用来在数据库中控制并发的⽅法,实现对数据库的并发访问⽤的,就是⼀种写时复制的思想的应⽤。在MySQL中,MVCC只在读取已提交(Read Committed)和可重复读(Repeatable Read)两个事务级别下有效。其是通过Undo⽇志中的版本链和ReadView⼀致性视图来实现的。MVCC就是在多个事务同时存在时,SELECT语句找寻到具体是版本链上的哪个版本,然后在找到的版本上返回其中所记录的数据的过程。

⾸先需要知道的是,在MySQL中,会默认为我们的表后⾯添加三个隐藏字段:

  • DB_ROW_ID:⾏ID,MySQL的B+树索引特性要求每个表必须要有⼀个主键。如果没有设置的话,会⾃动寻找第⼀个不包含NULL的唯⼀索引列作为主键。如果还是找不到,就会在这个DB_ROW_ID上⾃动⽣成⼀个唯⼀值,以此来当作主键(该列和MVCC的关系不⼤);
  • DB_TRX_ID:事务ID,记录的是当前事务在做INSERT或UPDATE语句操作时的事务ID(DELETE语句被当做是UPDATE语句的特殊情况,后⾯会进⾏说明);
  • DB_ROLL_PTR:回滚指针,通过它可以将不同的版本串联起来,形成版本链。相当于链表的next指针。

数据库中同时存在多个版本的数据,并不是整个数据库的多个版本,而是某一条记录的多个版本同时存在,在某个事务对其进行操作的时候,需要查看这一条记录的隐藏列事务版本 id,比对事务 id 并根据事物隔离级别去判断读取哪个版本的数据。

🎨 三、MVCC 实现的关键知识点

🏭 3.1、事务版本号

事务每次开启前,都会从数据库获得一个自增长的事务ID,可以从事务ID判断失误的执行先后顺序。这就是事务版本号。

👣 3.2、隐式字段

对于 InnoDB 存储引擎,每一行记录都有两个隐藏列 trx_idroll_pointer,如果表中没有主键和非 NULL 唯一键时,则还会有第三个隐藏的主键列 row_id

🐾 3.3、undo log

undo log,回滚日志,用于记录数据被修改前的信息。在表记录修改之前,会先把数据拷贝到 undo log 里,如果事务回滚,即可以通过 undo log 来还原数据。

直接上有种后悔药叫undo log

可以这样认为,当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,当 update 一条记录时,它记录一条对应相反的 update 记录。

undo log 有什么用途呢?

  1. 事务回滚时,保证原子性和一致性。
  2. 用于 MVCC 快照读

📝 3.4、版本链

多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(roll_pointer),连成一个链表,这个链表就称为版本链。如下:

image-20230906200129554

其实,通过版本链,我们就可以看出事务版本号、表格隐藏的列和 undo log 它们之间的关系。我们再来小分析一下。

  1. 假设现在有一张 core_user 表,表里面有一条数据,id 为 1,名字为孙权:

    image-20230906200223989

  2. 现在开启一个事务 A:对 core_user 表执行update core_user set name ="曹操" where id=1,会进行如下流程操作

    • 首先获得一个事务 ID=100
    • 把 core_user 表修改前的数据,拷贝到 undo log
    • 修改 core_user 表中,id=1 的数据,名字改为曹操
    • 把修改后的数据事务 Id=101 改成当前事务版本号,并把 roll_pointer 指向 undo log 数据地址。

    image-20230906200420149

💭 3.5、快照和当前读

快照读: 读取的是记录数据的可见版本(有旧的版本)。不加锁,普通的 select 语句都是快照读,如:

select * from core_user where id > 2;

当前读:读取的是记录数据的最新版本,显式加锁的都是当前读

select * from core_user where id > 2 for update;
select * from account where id>2 lock in share mode;

🏀 3.6、Reader View

Read View 是什么呢? 它就是事务执行 SQL 语句时,产生的读视图。实际上在 innodb 中,每个 SQL 语句执行前都会得到一个 Read View。

Read View 有什么用呢? 它主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据~

Read View 是如何保证可见性判断的呢?我们先看看 Read view 的几个重要属性

  • m_ids:当前系统中那些活跃(未提交)的读写事务 ID, 它数据结构为一个 List。
  • min_limit_id:表示在生成 Read View 时,当前系统中活跃的读写事务中最小的事务 id,即 m_ids 中的最小值。
  • max_limit_id:表示生成 Read View 时,系统中应该分配给下一个事务的 id 值。
  • creator_trx_id: 创建当前 Read View 的事务 ID

image-20230906200957129

(其中min_id指向ReadView中未提交事务数组中的最⼩事务ID,⽽max_id指向ReadView中的已经创建的最⼤事务ID)

  • 如果落在绿⾊区间(DB_TRX_ID < min_id):这个版本⽐min_id还⼩(事务ID是从⼩往⼤顺序⽣成的),说明这个版本在SELECT之前就已经提交了,所以这个数据是可⻅的。或者(这⾥是短路或,前⾯条件不满⾜才会判断后⾯这个条件)这个版本的事务本身就是当前SELECT语句所在事务的话,也是⼀样可⻅的;
  • 如果落在红⾊区间(DB_TRX_ID > max_id):表示这个版本是由将来启动的事务来⽣成的,当前还未开始,那么是不可⻅的;
  • 如果落在⻩⾊区间(min_id <= DB_TRX_ID <= max_id):这个时候就需要再判断两种情况:
    • 如果这个版本的事务ID在ReadView的未提交事务数组中,表示这个版本是由还未提交的事务⽣成的,那么就是不可⻅的;
    • 如果这个版本的事务ID不在ReadView的未提交事务数组中,表示这个版本是已经提交了的事务⽣成的,那么是可⻅的。

如果在上述的判断中发现当前版本是不可⻅的,那么就继续从版本链中通过回滚指针拿取下⼀个版本来进⾏上述的判断。

🎁 四、MVCC的演示过程

下⾯通过⼀个示例来具体演示MVCC的执⾏过程(假设是在可重复读事务级别下),当前account表中已经有了⼀条初始数据(id=1,name=monkey):

image-20230906201549902

image-20230906201619674

从左往右分别是五个事务,从上到下是时刻点。其中在第2和3时刻点中事务100和事务200(这⾥两个事务之间相差100只是为了更加⽅便去看,正常来说下个事务的ID是以+1的⽅式来创建的)分别执⾏了⼀条UPDATE语句,这两条语句并⽆实际作⽤,只是为了⽣成事务ID的,所以在下⾯的MVCC执⾏过程中就不分析这两条语句所带来的影响了,我们只研究account表。⽽其中最后两个事务,我是注明没有事务ID的。因为事务ID是执⾏⼀条更新操作(增删改)的语句后才会⽣成(这也是事务100和事务200要先执⾏5⼀条更新语句的意义),并不是开启事务的时候就会⽣成。最后两个事务中可以看到就是执⾏了⼀些SELECT语句⽽已,所以它们并没有事务ID。

⾸先来看⼀下初始状态时的版本链和ReadView(ReadView此时还未⽣成):

image-20230906201730417

其中事务1在account表中创建了⼀条初始数据。
之后在第1时刻点,五个事务分别开启了事务(如上所说,这个时候还没有⽣成事务ID)。

在第2时刻点,第⼀个事务执⾏了⼀条UPDATE语句,⽣成了事务ID为100。

在第3时刻点,第⼆个事务执⾏了⼀条UPDATE语句,⽣成了事务ID为200。

在第4时刻点,第三个事务执⾏了⼀条UPDATE语句,将account表中id为1的name改为了monkey301。同时⽣成了事务ID为300。

在第5时刻点,事务300也就是上⾯的事务执⾏了commit操作。

在第6时刻点,第四个事务执⾏了⼀条SELECT语句,想要查询⼀下当前id为1的数据(如上所说,该事务没有⽣成事务ID)。此时的版本链和ReadView如下:

image-20230906201812253

因为在第5时刻点,事务300已经commit了,所以ReadView的未提交事务数组中不包含它。此时根据上⾯所说的⽐对规则,拿版本链中的第⼀个版本的事务ID为300进⾏⽐对,⾸先当前这条SELECT语句没有在事务300中进⾏查询,然后发现是落在⻩⾊区间,⽽且事务300也没有在ReadView的未提交事务数组中,所以是可⻅的。即此时在第6时刻点,第四个事务所查找到的结果是monkey301。

在第7时刻点,事务100执⾏了⼀条UPDATE语句,将account表中id为1的name改为了monkey101。

在第8时刻点,事务100⼜执⾏了⼀条UPDATE语句,将account表中id为1的name改为了monkey102。

在第9时刻点,第四个事务执⾏了⼀条SELECT语句,想要查询⼀下当前id为1的数据。此时的版本链和ReadView如下:

image-20230906201857493

注意,因为当前是在可重复读的事务级别下,所以此时的ReadView沿⽤了在第6时刻点⽣成的ReadView(如果是在读取已提交的事务级别下,此时就会重新⽣成⼀份ReadView了)。然后根据上⾯所说的⽐对规则,拿版本链中的第⼀个版本的事务ID为100进⾏⽐对,⾸先当前这条SELECT语句没有在事务100中进⾏查询,然后发现是落在⻩⾊区间,⽽且事务100是在ReadView的未提交事务数组中,所以是不可⻅的。此时通过回滚指针拿取下⼀个版本,发现事务ID仍然为100,经过分析后还是不可⻅的。此时⼜拿取下⼀个版本:事务ID为300进⾏⽐对,⾸先当前这条SELECT语句没有在事务300中进⾏查询,然后发现是落在⻩⾊区间,但是事务300没有在ReadView的未提交事务数组中,所以是可⻅的。即此时在第9时刻点,第四个事务所查找到的结果仍然是monkey301(这也就是可重复读的含义)。

在第10时刻点,事务100commit提交事务了。同时事务200执⾏了⼀条UPDATE语句,将account表中id为1的name改为了monkey201。

在第11时刻点,事务200⼜执⾏了⼀条UPDATE语句,将account表中id为1的name改为了monkey202。

在第12时刻点,第四个事务执⾏了⼀条SELECT语句,想要查询⼀下当前id为1的数据。此时的版本链和ReadView如下:

image-20230906201951550

跟第9时刻点⼀样,在可重复读的事务级别下,ReadView沿⽤了在第6时刻点⽣成的ReadView。然后根据上⾯所说的⽐对规则,拿版本链中的第⼀个版本的事务ID为200进⾏⽐对,⾸先当前这条SELECT语句没有在事务200中进⾏查询,然后发现是落在⻩⾊区间,⽽且事务200是在ReadView的未提交事务数组中,所以是不可⻅的。此时通过回滚指针拿取下⼀个版本,发现事务ID仍然为200,经过分析后还是不可⻅的。此时⼜拿取下⼀个版本:事务ID为100进⾏⽐对,⾸先当前这条SELECT语句没有在事务100中进⾏查询,然后发现是落在⻩⾊区间内,同时在ReadView的未提交数组中,所以依然是不可⻅的。此时⼜拿取下⼀个版本,发现事务ID仍然为100,经过分析后还是不可⻅的。此时再拿取下⼀个版本:事务ID为300进⾏⽐对,⾸先当前这条SELECT语句没有在事务300中进⾏查询,然后发现是落在⻩⾊区间,但是事务300没有在ReadView的未提交事务数组中,所以是可⻅的。即此时在第12时刻点,第四个事务所查找到的结果仍然是monkey301。

同时在第12时刻点,第五个事务执⾏了⼀条SELECT语句,想要查询⼀下当前id为1的数据。此时的版本链和ReadView如下:

image-20230906202053337

注意,此时第五个事务因为是该事务内的第⼀条SELECT语句,所以会重新⽣成在当前情况下的ReadView,即上图中所示的内容。可以看到,和第四个事务⽣成的ReadView并不⼀样,因为在之前的第10时刻点,事务100已经提交事务了。然后根据上⾯所说的⽐对规则,拿版本链中的第⼀个版本的事务ID为200进⾏⽐对,⾸先当前这条SELECT语句没有在事务200中进⾏查询,然后发现是落在⻩⾊区间,⽽且事务200是在ReadView的未提交事务数组中,所以是不可⻅的。此时通过回滚指针拿取下⼀个版本,发现事务ID仍然为200,经过分析后还是不可⻅的。此时⼜拿取下⼀个版本:事务ID为100进⾏⽐对,发现是在绿⾊区间,所以是可⻅的。即此时在第12时刻点,第五个事务所查找到的结果是monkey102(可以看到,即使是同⼀条SELECT语句,在不同的事务中,查询出来的结果也可能是不同的,究其原因就是因为ReadView的不同)。

在第13时刻点,事务200执⾏了commit操作,整段分析过程结束。

以上演示的就是MVCC的具体执⾏过程,在多个事务下,版本链和ReadView是如何配合进⾏查找的。上⾯还遗漏了⼀种情况没有进⾏说明,就是如果是DELETE语句的话,也会在版本链上将最新的数据插⼊⼀份,然后将事务ID赋值为当前进⾏删除操作的事务ID。但是同时会在该条记录的信息头(recordheader)⾥⾯的deleted_flag标记位置为true,以此来表示当前记录已经被删除。所以如果经过版本⽐对
后发现找到的版本上的deleted_flag标记位为true的话,那么也不会返回,⽽是继续寻找下⼀个。

另外,如果当前事务执⾏rollback回滚的话,会把版本链中属于该事务的所有版本都删除掉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/128834.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Jetpack 中Hilt的使用

Hilt 是 Android 的依赖项注入库&#xff0c;可减少在项目中执行手动依赖项注入的样板代码。执行 手动依赖项注入 要求您手动构造每个类及其依赖项&#xff0c;并借助容器重复使用和管理依赖项。 Hilt 通过为项目中的每个 Android 类提供容器并自动管理其生命周期&#xff0c;…

依赖项的处理与层的创建与注册

依赖项的处理与层的创建与注册 依赖项的处理与层的创建与注册 新问题什么是 layer?layer 的创建与注册 与函数同时创建和绑定单独上传 layer 再绑定函数(推荐) 真正的运行时依赖 注册包的约定与平台强关联的运行时 1. 云端安装依赖2. 本地构建 Amazon Linux 2 容器环境3. 利用…

数字图像滤波的本质

一、说明 在数字时代&#xff0c;图像是我们交流和表达不可或缺的一部分。从社交媒体到医学成像&#xff0c;图像的质量和内容非常重要。这就是图像过滤和卷积领域介入的地方&#xff0c;为我们提供了一个转换和完善这些视觉叙事的工具包。 图像过滤不仅仅是让照片看起来更好;这…

Fiddler 系列教程(二) Composer创建和发送HTTP Request跟手机抓包

Fiddler Composer介绍 Composer的官方帮助文档&#xff1a;http://www.fiddler2.com/fiddler/help/composer.asp Fiddler的作者把HTTP Request发射器取名叫Composer(中文意思是&#xff1a;乐曲的创造者), 很有诗意 Fiddler Composer的功能就是用来创建HTTP Request 然后发送…

Chrome 基于 Wappalyzer 查看网站所用的前端技术栈

1. 找到谷歌商店 https://chrome.google.com/webstore/search/wappalyzer?utm_sourceext_app_menu 2. 搜索 Wappalyzer 3. 添加至Chrome 4. 使用 插件 比如打开 https://www.bilibili.com/ 就可以看到其所以用的前端技术栈了

【系统设计系列】 负载均衡和反向代理

系统设计系列初衷 System Design Primer&#xff1a; 英文文档 GitHub - donnemartin/system-design-primer: Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards. 中文版&#xff1a; https://github.com/donnemart…

一百七十二、Flume——Flume采集Kafka数据写入HDFS中(亲测有效、附截图)

一、目的 作为日志采集工具Flume&#xff0c;它在项目中最常见的就是采集Kafka中的数据然后写入HDFS或者HBase中&#xff0c;这里就是用flume采集Kafka的数据导入HDFS中 二、各工具版本 &#xff08;一&#xff09;Kafka kafka_2.13-3.0.0.tgz &#xff08;二&#xff09;…

ES 集群常用排查命令

说明&#xff1a;集群使用非默认端口9200&#xff0c;使用的是7116端口举例 一、常用命令 #1.集群健康状态 [wlsadminelastic-01~]$ curl -XGET "http://10.219.27.00:7116/_cluster/health?pretty" { cluster name":"cluster" "status"…

flutter报错-cmdline-tools component is missing

安装完androidsdk和android studio后&#xff0c;打开控制台&#xff0c;出现错误 解决办法 找到自己安装android sdk的位置&#xff0c;然后安装上&#xff0c;并将下面的勾选上 再次运行 flutter doctor 不报错&#xff0c;出现以下画面 Doctor summary (to see all det…

在Git中将本地分支推送到远程仓库

这里很明显 我git云端只有一个master分支 然后 我在本地创建了一个develop分支 然后 现在我想将他放在云端 首先 我们要执行 git checkout -b develop将本地切换到 develop 分支上 因为我这里已经选择的就是了 就不需要了 然后我们执行 git push origin develop这样 刷新云…

队列(Queue)的顶级理解

目录 1.队列(Queue) 的概念 2.单链表模拟实现队列 2.1创建队列 2.2入队列 2.3判断是否为空 2.4出队列 2.5获取队头元素 2.6完整代码&#xff1a; 2.7双向链表模拟实现队列代码 3.数组模拟实现队列代码 3.1创建队列 3.2判断是否为满 3.3检查是否为空 3.4插入元素 3…

静态路由 网络实验

静态路由 网络实验 拓扑图初步配置R1 ip 配置R2 ip 配置R3 ip 配置查看当前的路由表信息查看路由表信息配置静态路由测试 拓扑图 需求&#xff1a;实现 ip 192.168.1.1 到 192.168.2.1 的通信。 初步配置 R1 ip 配置 system-view sysname R1 undo info-center enable # 忽略…

公开游戏、基于有向图的游戏

目录 〇&#xff0c;背景 一&#xff0c;公开游戏、策梅洛定理 1&#xff0c;公开游戏 2&#xff0c;策梅洛定理 二&#xff0c;有向图游戏 1&#xff0c;狭义有向图游戏 2&#xff0c;广义有向图游戏 3&#xff0c;狭义有向图游戏的SG数 4&#xff0c;Bash Game 力扣…

在群晖上安装Nextcloud-AIO详解

本文是应网友 刘源 的要求折腾的&#xff1b; 什么是 Nextcloud AIO &#xff1f; Nextcloud AIO 就是 Nextcloud All-in-One &#xff0c;顾名思义就是一个 Nextcloud 的 All-in-One 版本&#xff0c;这是一个基于 Docker 的项目&#xff0c;它允许仅安装一个容器&#xff0c;…

有哪些适合初学者的编程语言?

C语言 那为什么我还要教你C语言呢&#xff1f;因为我想要让你成为一个更好、更强大的程序员。如果你要变得更好&#xff0c;C语言是一个极佳的选择&#xff0c;其原因有二。首先&#xff0c;C语言缺乏任何现代的安全功能&#xff0c;这意味着你必须更为警惕&#xff0c;时刻了…

【C++进阶】多态

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

如何用Jmeter提取和引用Token

1.执行获取token接口 在结果树这里&#xff0c;使用$符号提取token值。 $根节点&#xff0c;$.data.token表示提取根节点下的data节点下的token节点的值。 2.使用json提取器&#xff0c;提取token 变量路径就是把在结果树提取的路径写上。 3.使用BeanShell取样器或者BeanShell后…

webpack打包常用配置项

webpack打包配置项 参考链接 文件结构&#xff1a;最基础版 先安装 npm i webpack webpack-cli --dev 运行命令&#xff1a;npx webpack 进行打包 1. 配置webpack.config.js文件&#xff1a; const path require(path); module.exports {mode: development, // 开发环境 …

MyBatis: 向oracle表中插入null字段的处理

一、可以在SQL中指定类型&#xff1a; Insert("insert into student values(#{name,jdbcTypeNULL},#{age})")int addStudent(Param("name")String name, Param("age") int age);二、可以进行全局配置&#xff08;单独使用MyBatis时可如下配置&am…