MySQL innoDB存储引擎多事务场景下的事务执行情况

news2024/10/7 12:19:44

一、背景

在日常开发中,对不同事务之间的隔离情况等理解如果不够清晰,很容易导致代码的效果和预期不符。因而在这对一些存在疑问的场景进行模拟。

下面的例子全部基于innoDB存储引擎。

二、场景:

2.1、两个事务修改同一行记录

正常来说,两个事务修改相同的记录,肯定会相互阻塞,排队执行的。

一开始号码为13827622366的客户的名称为哈哈哈。A事务先进入事务,但未执行到变更号码为13827622366的客户记录的操作(睡眠实现),B事务开启事务执行变更号码为13827622366的客户记录。

代码

	@ApiOperation(value = "transaction1", notes = "")
	@GetMapping(value = "/transaction1")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction1() throws InterruptedException {
		System.out.println("事务1开始");
		Thread.sleep(8000);//其他业务
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622377");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1:"+list.get(0).getName());
		}
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622377")
				.set(SdSchoolCustomer::getName,"事务1name");
		sdSchoolCustomerService.update(updateWrapper);
		System.out.println("事务1结束");
		return Result.ok();
	}

	@ApiOperation(value = "transaction2", notes = "")
	@GetMapping(value = "/transaction2")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction2() {
		System.out.println("事务2开始");
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622366");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务2:"+list.get(0).getName());
		}
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622366")
				.set(SdSchoolCustomer::getName,"事务2name");
		sdSchoolCustomerService.update(updateWrapper);
		System.out.println("事务2结束");
		return Result.ok();
	}

执行结果

最后该客户的name是“事务1name”。结合下图可以看到,事务1先开启了事务然后睡眠了,接着事务2开启事务,执行查询然后更新记录,接着事务1睡眠完毕,执行查询,查到了事务2提交之后的数据,然后更新记录。也就是说,开启事务之后,在还没有执行到更新操作之前,其他事务还是可以更新该数据并且不会被阻塞。

把睡眠放到update后面,再来验证一下。

代码

@ApiOperation(value = "transaction1", notes = "")
	@GetMapping(value = "/transaction1")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction1() throws InterruptedException {
		System.out.println("事务1开始");
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622366");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1:"+list.get(0).getName());
		}
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622366")
				.set(SdSchoolCustomer::getName,"事务1name");
		sdSchoolCustomerService.update(updateWrapper);
		Thread.sleep(8000);//其他业务
		System.out.println("事务1结束");
		return Result.ok();
	}

	@ApiOperation(value = "transaction2", notes = "")
	@GetMapping(value = "/transaction2")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction2() {
		System.out.println("事务2开始");
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622366");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务2:"+list.get(0).getName());
		}
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622366")
				.set(SdSchoolCustomer::getName,"事务2name");
		sdSchoolCustomerService.update(updateWrapper);
		System.out.println("事务2结束");
		return Result.ok();
	}

执行结果

最后该客户的name是“事务2name”。结合下图,事务1开始执行查询,并执行更新数据的操作,然后进入睡眠。这个时候事务2开始执行,也查询(因为事务1还没提交,所以查到的也还是原来的值),尝试执行更新数据操作,但这次被阻塞了,一直到事务1提交了事务之后才能继续执行update语句后面的代码。

结论

不同事务更新同一条记录,假如A先执行到更新该行记录的事务,A会阻塞其他想要更新该记录的事务;假如B事务在(A事务执行了更新操作但未提交事务之前)也执行到更新该记录,B事务的代码会被阻塞,必须等A事务提交或回滚了之后,B事务的代码才能继续往下执行。

另外,因为在MySQL中,一个SQL也相当于一个事务,所以一个事务一个非事务修改同一行记录的执行结果和上面也是一样的。

2.2、两个事务修改同一个表的不同行记录

事务1开启事务,修改号码为13827622377的记录的名称,然后睡眠;事务2开启事务,修改号码为13827622366的记录,看看事务2是否还会被阻塞。

代码

	@ApiOperation(value = "transaction1", notes = "")
	@GetMapping(value = "/transaction1")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction1() throws InterruptedException {
		System.out.println("事务1开始");
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622366")
				.set(SdSchoolCustomer::getName,"事务1name");
		sdSchoolCustomerService.update(updateWrapper);
		Thread.sleep(8000);//其他业务
		System.out.println("事务1结束");
		return Result.ok();
	}

	@ApiOperation(value = "transaction2", notes = "")
	@GetMapping(value = "/transaction2")
	public Result<?> transaction2() {
		System.out.println("事务2开始");
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622377")
				.set(SdSchoolCustomer::getName,"事务2name");
		sdSchoolCustomerService.update(updateWrapper);
		System.out.println("事务2结束");
		return Result.ok();
	}

执行结果

两个事务都成功提交了,从下图结果来看,事务2并没有因为事务1还未提交而被阻塞,说明开启事务的时候修改不同的行记录不会互相影响。(这样事务执行的效率更高了)

2.3、上面几种场景得出的结论

从上面的几个例子可以看出,事务执行到更新记录操作之后,该行记录暂时不可被该事务之外的操作更改,无论是开启事务来变更记录还是直接变更记录,都会被阻塞。要等待事务1执行完毕提交或回滚事务之后才可以进行记录更新并继续往下执行。(阻塞的位置在更新记录的代码处)

2.4、A事务第一次查询数据,B事务更新数据,A事务再次查询数据

同一条记录,两次查询有什么区别?

innoDB的默认隔离级别是可重复读,这意味着从第一次查询数据开始,这条数据就被记录下来了,只要当前事务没有更改该记录,并且还在当前事务内,无论查询多少次,该条记录的值都是一样的,相当于后续查到的都是记录的一个快照。(这就是事务之间的数据隔离,自己事务更新的数据是可以看到更新之后的值的)

号码为13827622366的记录的name一开始的值是“哈哈哈4”。事务1先开启事务并进行第一次查询,然后睡眠;这时事务2开启事务,并更新该记录的name为“事务2name”;接着事务1睡眠完毕进行第二次查询。

代码

@ApiOperation(value = "transaction1", notes = "")
	@GetMapping(value = "/transaction1")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction1() throws InterruptedException {
		System.out.println("事务1开始");
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622366");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1第一次查询:"+list.get(0).getName());
		}
		Thread.sleep(8000);//其他业务
		list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1第二次查询:"+list.get(0).getName());
		}
		System.out.println("事务1结束");
		return Result.ok();
	}

	@ApiOperation(value = "transaction2", notes = "")
	@GetMapping(value = "/transaction2")
	public Result<?> transaction2() {
		System.out.println("事务2开始");
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622366")
				.set(SdSchoolCustomer::getName,"事务2name");
		sdSchoolCustomerService.update(updateWrapper);
		System.out.println("事务2结束");
		return Result.ok();
	}

执行结果

在事务1还在睡眠的时候,在系统查询该记录,该记录的name已经更新为“事务2name”。但当事务1第二次查询的时候查询出的结果还是“哈哈哈4”,和第一次查询的结果保持一致,符合可重复读。

解析

innoDB的默认隔离级别是可重复读,要求在一个事务内多次读取同一条记录的结果保持一致。MySQL是通过快照读来实现的,在事务内第一次查询数据的时候,记录所有行记录当前最新的已提交的事务版本号,并形成一个视图。该事务内的后续查询都要和视图内的数据进行比对,只能查询出记录的事务版本号及以前版本的数据,从而实现行记录的快照读。(快照是整个表那一刻的快照,下两个例子验证)

2.5、A事务第一次查询数据,B事务插入数据,A事务再次查询数据

两次查询记录的数量有什么不同?记录的数量上也是实现了可重复读。

号码为13827622366的记录一开始只有一条。事务1开启事务,并第一次查询号码为1382762236的记录个数,然后睡眠;接着事务2开启事务,新插入一条号码为13827622366的记录;接着事务1睡眠结束,进行第二次查询号码为1382762236的记录个数。

代码

	@ApiOperation(value = "transaction1", notes = "")
	@GetMapping(value = "/transaction1")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction1() throws InterruptedException {
		System.out.println("事务1开始");
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622366");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1第一次查询数量:"+list.size());
		}
		Thread.sleep(8000);//其他业务
		list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1第二次查询数量:"+list.size());
		}
		System.out.println("事务1结束");
		return Result.ok();
	}

	@ApiOperation(value = "transaction2", notes = "")
	@GetMapping(value = "/transaction2")
	public Result<?> transaction2() {
		System.out.println("事务2开始");
		SdSchoolCustomer customer=new SdSchoolCustomer();
		customer.setCustomerNo(RandomUtil.randomString(10));
		customer.setPhone("13827622366");
		sdSchoolCustomerService.save(customer);
		System.out.println("事务2结束");
		return Result.ok();
	}

执行结果

在事务1还在睡眠的时候,在系统查询号码为1382762236的记录,能查到两条记录,说明事务2所插入的新数据已经生效了。但事务1第二次查到的数量却还是1,说明在事务内,数据在数量上也是存在快照读的。

  2.6、A事务查询甲记录,B事务修改乙记录,A事务接着查询乙记录

上述的甲记录和乙记录属于同一个表,看看A事务第一次查询所记录的快照是针对整个表还是仅针对查到的记录。

一开始号码为13827622377的记录的名称为“哈哈哈5”。事务1先开启事务,查询号码为13827622366的记录,接着睡眠;这时候事务2开启事务,更新号码是13827622377的记录的名称为“事务2name”;然后事务1睡眠结束,查询号码为13827622377的记录,看看查到的记录是事务2更新前还是更新后的数据。

代码

	@ApiOperation(value = "transaction1", notes = "")
	@GetMapping(value = "/transaction1")
	@Transactional(rollbackFor = Exception.class)
	public Result<?> transaction1() throws InterruptedException {
		System.out.println("事务1开始");
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622366");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1第一次查询:"+list.get(0).getName());
		}
		Thread.sleep(8000);//其他业务
		queryWrapper.clear();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622377");
		list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务1第二次查询:"+list.get(0).getName());
		}
		System.out.println("事务1结束");
		return Result.ok();
	}

	@ApiOperation(value = "transaction2", notes = "")
	@GetMapping(value = "/transaction2")
	public Result<?> transaction2() {
		System.out.println("事务2开始");
		LambdaQueryWrapper<SdSchoolCustomer> queryWrapper=new LambdaQueryWrapper<>();
		queryWrapper.eq(SdSchoolCustomer::getPhone,"13827622377");
		List<SdSchoolCustomer> list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务2第一次查询:"+list.get(0).getName());
		}
		LambdaUpdateWrapper<SdSchoolCustomer> updateWrapper=new LambdaUpdateWrapper<>();
		updateWrapper.eq(SdSchoolCustomer::getPhone,"13827622377")
				.set(SdSchoolCustomer::getName,"事务2name");
		sdSchoolCustomerService.update(updateWrapper);
		list = sdSchoolCustomerService.list(queryWrapper);
		if(CollectionUtil.isNotEmpty(list)){
			System.out.println("事务2第二次查询:"+list.get(0).getName());
		}
		System.out.println("事务2结束");
		return Result.ok();
	}

执行结果

事务1还在睡眠的时候,在系统查询号码为13827622377的记录,该记录的name已经更新为“事务2name”。事务1第一次查询号码为13827622366的记录的名称并打印只是用来代表查到了该表的数据;接着事务2开启,更新号码为13827622377的记录的名称;事务1睡眠完毕,查询号码为13827622377的记录的名称,发现查询到的结果是事务2修改之前的结果。和从系统直接查询到的结果不一致,说明事务1在第一次查询的时候保存的快照是针对整个表的快照。

三、总结

  1. 事务之间的互相阻塞是在执行到更新操作代码并且更新到相同表的相同行记录情况下才会触发的。(相当于需要顺序执行)
  2. MySQL innoDB存储引擎 可重复读隔离级别下,事务在第一次查询表记录的时候记录的是整个表的快照,后续查询无论是数据上,还是数据的量上都是快照读。
  3. 可重复读隔离级别下,依旧存在幻读问题。可重复读的隔离级别要求事务内多次查询同一个表的数据和数据的量保持一致,这意味着事务内读取到的数据量和实际的数据量可能是不一致的,也就是可能读取到不存在的数据或者读取不到已插入的数据,从而出现幻读问题。

四、实际开发中使用事务的一些见解

  1. 一些业务如果需要同时用到锁和事务,一般锁加在事务外层。
  2. 不同事务方法之间的互相影响一般情况下不需要太过考虑。(真需要可以考虑用乐观锁)

五、底层原理

未完待续~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日一题 — 最大连续 1 的个数III

解法一&#xff1a;暴力枚举 先定义left和right双指针&#xff0c;left先固定在起始位置&#xff0c;遍历right当值等于1的时候&#xff0c;直接跳过&#xff0c;等于0的时候&#xff0c;zero计数器加一当zero等于k的时候&#xff0c;就开始记录此时最大长度是多少然后left加一…

urwid,一个好用的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个好用的 Python 库 - urwid。 Github地址&#xff1a;https://github.com/urwid/urwid Urwid 是一个功能强大的 Python 库&#xff0c;用于创建基于文本的用户界面&#xf…

Linux入门攻坚——18、SELinux、Bash脚本编程续

SELinux——Secure Enhanced Linux&#xff08;安全加强的Linux&#xff09;&#xff0c;工作于Linux内核中。 SELinux 主要作用就是最大限度地减小系统中服务进程可访问的资源&#xff08;最小权限原则&#xff09;。采用委任式存取控制&#xff0c;是在进行程序、文件等细节权…

蓝桥杯2023A-05-互质数(Java)

5.互质数 题目描述 给定 a, b&#xff0c;求 1 ≤ x < a^b 中有多少个 x 与 a^b 互质。由于答案可能很大&#xff0c;你只需要输出答案对 998244353 取模的结果。 输入格式 输入一行包含两个整数分别表示 a, b&#xff0c;用一个空格分隔。 输出格式 输出一行包含一个…

【MPI并行程序】完美解决Attempting to use an MPI routine before initializing MPI

文章目录 错误原因解决方案 最近在写并行程序&#xff0c;犯了一个小错误&#xff0c;记录一下&#xff0c;以防止以后再犯。 Attempting to use an MPI routine before initializing MPI&#xff08;在初始化 MPI 之前尝试使用 MPI 例程&#xff09; 错误原因 这个错误通常是因…

编写Markdown时如何爽爽地渲染树?

在使用VitePress/Dumi等静态网站生成时&#xff0c;一般均支持直接在Markdown中渲染显示Vue/React组件&#xff0c;这给个网站非常丰富极致的表现力&#xff0c;我们在创建静态网站时开心的使用各种Vue/React组件&#xff0c;但是在输出树结构时&#xff0c;实际场景中存在几个…

Git的简单入门使用

文章目录 拷贝项目的步骤创建项目的步骤提交项目或项目文件的步骤恢复项目文件的步骤推送项目文件的步骤 拷贝项目的步骤 找到需要用来存放项目的文件夹&#xff1b;在文件夹页面空白处右键点击&#xff0c;然后再菜单中选择“Open Git Bash here”。在Github上找到需要进行拷…

【Java核心能力】美团优选后端一面:网络 操作系统

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

Vue2 —— 学习(五)

一、生命周期 &#xff08;一&#xff09;引入案例 我们想让一行文字按一定频率逐渐变得透明 1. Vue 实例外写法 函数写在 Vue 实例外面也能实现但是不推荐 <body><div id"root"><h2 :style"{opacity}">欢迎学习Vue</h2><…

服务器开发 Socket 相关基础

Socket 三要素 1.通信的目的地址&#xff1b; 2.使用的端口号&#xff1b; 3.使用的传输层协议&#xff08;如 TCP、UDP&#xff09; Socket 通信模型 服务端实现 #include <iostream> #include <unistd.h> #include <stdio.h> #include <sys/types.h&…

【QT】pro文件里添加又删除LIBS不影响运行的原因

我发现个问题啊&#xff0c;如果运行项目&#xff0c;发现报错&#xff0c;缺少某dll&#xff0c;接着你在pro文件里加上win32:LIBS -lOpengl32&#xff08;举个例子&#xff09;&#xff0c;接着可以运行了&#xff0c;接着把这行删掉&#xff0c;再运行&#xff0c;仍然可以…

hive 数据库表常用操作及相关函数讲解

创建数据库并指定hdfs存储位置 create database myhive2 location ‘/myhive2’; 使用location关键字&#xff0c;可以指定数据库在HDFS的存储路径。 Hive的库在HDFS上就是一个以.db结尾的目录 默认存储在&#xff1a; /user/hive/warehouse内 当你为Hive表指定一个LOCATION时…

数据库(mysql)-连接嵌套查询-2

子查询 MySQL中的子查询&#xff08;Subquery&#xff09;是嵌套在其他SQL查询中的查询。子查询可以出现在SELECT、FROM或WHERE子句中&#xff0c;并用于返回将被用于外部查询的数据。子查询的结果可以是一个单一的值、一行、一列或多行多列的数据集。 单行单列查询 实例 #查…

如何提高旋转花键运行稳定性?

现代化精密仪器设备中&#xff0c;精密仪器的稳定工作性能对于生产效率和产品质量至关重要&#xff0c;运行效率和精度是常见问题。旋转花键作为机械传动系统中的重要组成部分&#xff0c;其稳定性也是直接影响到机械装配的质量和使用寿命&#xff0c;那么我们应该如何提升旋转…

【汇编语言实战】已知10个整数求最大值

C语言描述该程序流程&#xff1a; #include <stdio.h> int main() {int a[]{11,33,23,54,12,51,2,4,34,45};int maxa[0];for(int i1;i<9;i){if(a[i]>max){maxa[i];}}printf("%d",max); }汇编语言&#xff1a; include irvine32.inc .data arr dword 11…

STM32G030F6P6 HSE时钟不能使用无源晶振,只能使用有源晶振!

STM32G030F6P6 HSE时钟不能使用无源晶振&#xff0c;只能使用有源晶振。 参见STM32CubeMX配置 使能RCC中 BYPASS CLOCK SOURCE后只有一个 PC14引脚。 查手册中 5.2.1 HSE clock章节 部分引脚少的封装&#xff0c;HSE时钟只有 OSC-IN&#xff0c;因此只能使用有源晶振 查Data…

经典机器学习模型(八)梯度提升树GBDT详解

经典机器学习模型(八)梯度提升树GBDT详解 Boosting、Bagging和Stacking是集成学习(Ensemble Learning)的三种主要方法。 Boosting是一族可将弱学习器提升为强学习器的算法&#xff0c;不同于Bagging、Stacking方法&#xff0c;Boosting训练过程为串联方式&#xff0c;弱学习器…

2024中国航空航天暨无人机展览会8月在重庆举办

2024中国航空航天暨无人机展览会8月在重庆举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 展会背景&#xff1a; 为更好的培养航空航天产业人才&#xff0c;汇聚航空教育产业创新科技&#xff0c;…

IO流的基础详解

文件【1】File类&#xff1a; 封装文件/目录的各种信息&#xff0c;对目录/文件进行操作&#xff0c;但是我们不可以获取到文件/目录中的内容。 【2】引入&#xff1a;IO流&#xff1a; I/O &#xff1a; Input/Output的缩写&#xff0c;用于处理设备之间的数据的传输。 【3】…

Terraform 扩展

Terraform 扩展 Terraform Meta-Arguments 元参数 count 创建相似的资源for_each 创建相似的资源depends_on 定义资源或者模块的依赖provider 定义provider选项lifecycle 资源的生命周期行为 参数使用范围备注countresource module适用于创建多个相似的资源&#xff0c;使用…