MYSQL基础架构、执行过程分析、事务的实现、索引的选择、覆盖索引

本文是mysql45讲的1-5的总结

文章目录

  • 基础架构
    • 连接器
    • 分析器
    • 优化器
    • 执行器
    • SQL查询执行过程
      • 详细执行步骤
    • SQL更新执行过程
    • 重要的日志模块:redo log
    • 重要的日志模块:binlog
      • 阶段性提交
  • 事务
    • 事务隔离的实现
    • 启动
  • 索引
    • 数据库索引模型
    • InnoDB索引组织结构
    • 主键选择
    • B+树维护操作
    • 索引重建问题
  • 覆盖索引
    • 最左前缀原则
    • 索引下推
      • 策略选择和权衡
      • 建议

基础架构

先来看一张图:

basic structure

MySQL分为Server层和存储引擎层两部分。

Server层:连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。

不同存储引擎的表数据存取方式不同,支持的功能也不同

从图中不难看出,不同的存储引擎共用一个Server层,也就是从连接器到执行器的部分。

连接器

顾名思义就是负责跟客户端建立连接、获取权限、维持和管理连接的

我们通常命令行会这么写:

mysql -h$ip -P$port -u$user -p

用户的权限是在登录时就确认好的, 也就是说如果在登录过程中对当前用户的权限进行了修改,当前已登录用户的权限并不会发生改变,直到用户再次登录,权限才会更新

我们可以通过

show processlist

对当前链接用户进行查看

客户端如果太长时间没动静,连接器就会自动将它断开。这个时间是由参数wait_timeout控制的,默认值是8小时。

如果在连接被断开之后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。这时候如果你要继续,就需要重连,然后再执行请求了。

数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。

建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。

分析器

作用:语句词法分析

语法分析器会根据语法规则,判断你输入的这个SQL语句是否满足MySQL语法。

如果你的语句不对,就会收到“You have an error in your SQL syntax”的错误提醒,比如下面这个语句select少打了开头的字母“s”。

mysql> elect * from t where ID=1;

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'elect * from t where ID=1' at line 1

优化器

在真正执行之前还需要一个优化,比如说当表里面有多个索引的时候,有多个join的时候,谁先谁后,这就是优化器要决定的事情,如何使用让效率变得更高

执行器

分析器知道了要怎么做,优化器知道了该怎么做,于是开始执行;

执行优化器选择的执行计划,与存储引擎协同工作,完成数据的实际检索和修改。

执行器会先查看是否有表读取权限,如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

SQL查询执行过程

  • 查询示例select * from T where ID=10;
    • 输入:用户输入的SQL查询语句。
    • 输出:查询结果。

详细执行步骤

  1. SQL解析

    • 词法分析:将SQL语句分解成有意义的元素(词法单元),如关键字、标识符、常量。
    • 语法分析:根据MySQL的语法规则验证词法单元的结合是否正确。
  2. 查询优化

    • 执行计划生成:优化器评估多种可能的查询路径,生成多个执行计划。
    • 成本估算:为每个执行计划估算执行成本,选择成本最低的计划。
  3. 查询执行

    • 执行器与存储引擎交互:执行器指挥存储引擎按照优化后的计划执行查询。
    • 数据检索:存储引擎从数据文件中检索数据,返回给执行器。

    SQL更新执行过程

    更新里面其实就是查询+修改

    MySQL可以恢复到半个月内任意一秒的状态,那是怎么做到的?

    与查询流程不一样的是,更新流程还涉及两个重要的日志模块,它们正是我们今天要讨论的主角:redo log(重做日志)和 binlog(归档日志)。

    重要的日志模块:redo log

    其实就是MySQL里经常说到的WAL技术,WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。

    具体来说,当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面

    InnoDB的redo log是固定大小的,比如可以配置为一组4个文件,每个文件的大小是1GB,那么这块“粉板”总共就可以记录4GB的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。

    redo_log

    write pos是当前记录的位置,一边写一边后移,写到第3号文件末尾后就回到0号文件开头。checkpoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。

    write pos和checkpoint之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果write pos追上checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把checkpoint推进一下。

    有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe

    要理解crash-safe这个概念,可以想想我们前面赊账记录的例子。只要赊账记录记在了粉板上或写在了账本上,之后即使掌柜忘记了,比如突然停业几天,恢复生意后依然可以通过账本和粉板上的数据明确赊账账目。

    重要的日志模块:binlog

    Server层也有自己的日志,称为binlog(归档日志)

    因为最开始MySQL里并没有InnoDB引擎。MySQL自带的引擎是MyISAM,但是MyISAM没有crash-safe的能力,binlog日志只能用于归档。而InnoDB是另一个公司以插件形式引入MySQL的,既然只依靠binlog是没有crash-safe能力的,所以InnoDB使用另外一套日志系统——也就是redo log来实现crash-safe能力。

    这两种日志有以下三点不同。

    1. redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
    2. redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。
    3. redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

    更新过程

    1. 执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
    2. 执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
    3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。
    4. 执行器生成这个操作的binlog,并把binlog写入磁盘。
    5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。

    更新过程

    阶段性提交

    最后三步看上去有点“绕”,将redo log的写入拆成了两个步骤:prepare和commit,这就是"两阶段提交”

    binlog会记录所有的逻辑操作,并且是采用“追加写”的形式。如果你的DBA承诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有binlog,同时系统会定期做整库备份。这里的“定期”取决于系统的重要性,可以是一天一备,也可以是一周一备。

    当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:

    • 首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;
    • 然后,从备份的时间点开始,将备份的binlog依次取出来,重放到中午误删表之前的那个时刻。

    这样你的临时库就跟误删之前的线上库一样了,然后你可以把表数据从临时库取出来,按需要恢复到线上库去。

    为什么日志需要“两阶段提交”。这里不妨用反证法来进行解释。

    由于redo log和binlog是两个独立的逻辑,如果不用两阶段提交,要么就是先写完redo log再写binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。

    仍然用前面的update语句来做例子。假设当前ID=2的行,字段c的值是0,再假设执行update语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了crash,会出现什么情况呢?

    1. 先写redo log后写binlog。假设在redo log写完,binlog还没有写完的时候,MySQL进程异常重启。由于我们前面说过的,redo log写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行c的值是1。

      但是由于binlog没写完就crash了,这时候binlog里面就没有记录这个语句。因此,之后备份日志的时候,存起来的binlog里面就没有这条语句。

      然后你会发现,如果需要用这个binlog来恢复临时库的话,由于这个语句的binlog丢失,这个临时库就会少了这一次更新,恢复出来的这一行c的值就是0,与原库的值不同。

    2. 先写binlog后写redo log。如果在binlog写完之后crash,由于redo log还没写,崩溃恢复以后这个事务无效,所以这一行c的值是0。但是binlog里面已经记录了“把c从0改成1”这个日志。所以,在之后用binlog来恢复的时候就多了一个事务出来,恢复出来的这一行c的值就是1,与原库的值不同。

    可以看到,如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。

    不只是误操作后需要用这个过程来恢复数据。当你需要扩容的时候,也就是需要再多搭建一些备库来增加系统的读能力的时候,现在常见的做法也是用全量备份加上应用binlog来实现的,这个“不一致”就会导致你的线上出现主从数据库不一致的情况。

    简单说,redo log和binlog都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。

    事务

    当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。

    隔离得越严实,效率就会越低。因此我们在二者之间寻找一个平衡点。

    SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable ):

    • 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
    • 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
    • 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
    • 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

    Oracle数据库的默认隔离级别其实就是“读提交”,因此对于一些从Oracle迁移到MySQL的应用,为保证数据库隔离级别的一致,你一定要记得将MySQL的隔离级别设置为“读提交”。

    事务隔离的实现

    在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

    假设一个值从1被按顺序改成了2、3、4,在回滚日志里面就会有类似下面的记录:

    ,不同时刻启动的事务会有不同的read-view。如图中看到的,在视图A、B、C里面,这一个记录的值分别是1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于read-view A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。

    回滚日志会当系统里没有比这个回滚日志更早的read-view的时候被删除

    为什么不要用长事务

    1. 长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
    2. 长事务还占用锁资源,也可能拖垮整个库

启动

MySQL的事务启动方式有以下几种:

  1. 显式启动事务语句, begin 或 start transaction。配套的提交语句是commit,回滚语句是rollback。
  2. set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个select语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行commit 或 rollback 语句,或者断开连接。

有些客户端连接框架会默认连接成功后先执行一个set autocommit=0的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。

因此,我会建议你总是使用set autocommit=1, 通过显式语句的方式来启动事务。

但是有的开发同学会纠结“多一次交互”的问题。对于一个需要频繁使用事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句的交互次数。如果你也有这个顾虑,我建议你使用commit work and chain语法。

在autocommit为1的情况下,用begin显式启动的事务,如果执行commit则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行begin语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。

你可以在information_schema库的innodb_trx这个表中查询长事务,比如下面这个语句,用于查找持续时间超过60s的事务。

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

索引

数据库索引模型

  1. 哈希表
    • 适用于等值查询,比如Memcached及其他一些NoSQL引擎
    • 查询效率高,但不支持范围查询
    • 插入删除效率低,可能会出现哈希冲突
  2. 有序数组
    • 在等值查询和范围查询场景中的性能就都非常优秀
    • 查询效率高
    • 插入删除效率低,需要移动数据
    • 有序数组索引只适用于静态存储引擎
  3. B+树
    • InnoDB采用B+树作为索引模型
    • 支持等值查询和范围查询
    • 查询效率高,插入删除效率也较高

InnoDB索引组织结构

example

从图中不难看出,根据叶子节点的内容,索引类型分为主键索引和非主键索引。

主键索引的叶子节点存的是整行数据。在InnoDB里,主键索引也被称为聚簇索引(clustered index)。

非主键索引的叶子节点内容是主键的值。在InnoDB里,非主键索引也被称为二级索引(secondary index)。

  1. 主键索引对应聚集索引
    • 数据按主键物理顺序存储;主键查询只需要搜索B+树就行
  2. 辅助索引的叶子节点为主键值
    • 通过主键索引查找数据,普通索引需要先搜索引树得到ID的值,在到ID索引树搜索,称为回表操作;

也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询

主键选择

  1. 自增主键
    • 插入顺序,不会触发分裂
    • 空间占用小
  2. 业务主键
    • 更符合实际需求
    • 也可以直接作为主键索引

B+树维护操作

  1. 插入如果导致页满,需要分裂页
  2. 删除如果导致页利用率低,需要合并页

索引重建问题

  1. 直接使用ALTER TABLE效率可能不高
  2. 应考虑使用线上DDL或备份还原方式

显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。

所以,从性能和存储空间方面考量,自增主键往往是更合理的选择。

有没有什么场景适合用业务字段直接做主键的呢?还是有的。比如,有些业务的场景需求是这样的:

  1. 只有一个索引;
  2. 该索引必须是唯一索引。

覆盖索引

  • 定义:如果一个索引包含了查询所需的所有字段,则称为覆盖索引。
  • 优点:能够减少数据库访问成本,因为可以直接在索引中获取数据,无需回表到主数据文件。
  • 应用:例如,在查询 SELECT ID FROM T WHERE k BETWEEN 3 AND 5 中,如果索引 k 已经包括 ID 字段,则此索引覆盖了查询需求。

最左前缀原则

  • 定义:在使用复合索引时,查询条件可以使用索引的最左边的一个或多个字段来优化查询。
  • 优点:使得数据库能够利用索引快速定位数据,减少扫描范围。
  • 实例:在联合索引 (name, age) 中,查询可以利用 namenameage 的组合来加速,但不能只用 age

索引下推

 无索引下推执行流程

索引下推执行流程

  • 定义:MySQL 5.6及以上版本的优化技术,允许在索引层面过滤数据,而不是在找到完整记录后才进行过滤。
  • 优点:减少了不必要的数据加载和回表操作,提高查询效率。
  • 示例:在 name, age 索引中,查询 SELECT * FROM tuser WHERE name LIKE '张%' AND age = 10 可以直接在索引层过滤掉不符合 age 条件的记录。

策略选择和权衡

  • 联合索引的选择:在创建联合索引时,应考虑查询模式以及字段在查询中的频率和顺序。正确的字段顺序可以减少冗余索引,优化存储和维护成本。
  • 索引的成本:尽管索引可以提高查询性能,但它们也会增加写操作的负担,并占用额外的存储空间。因此,在添加索引前需要权衡其对性能的潜在影响。

建议

  • 覆盖索引使用建议:对于高频查询特别是在只需要表中少数几个字段的情况下,考虑使用覆盖索引。
  • 索引审查:定期审查现有索引的效能和必要性,移除不再有效或重复的索引,优化索引配置

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Unity常见小问题]打包ios后无法修改模型透明度

问题 在Editor下可以使用如下代码去修改模型的材质的透明度,但是打包ios后无法对透明度进行修改且没有任何warning和error using System.Collections; using System.Collections.Generic; using UnityEngine;public class NewBehaviourScript : MonoBehaviour {[R…

订票系统|基于Springboot+vue的火车票订票系统(源码+数据库+文档)

订票系统目录 基于Springbootvue的火车票订票系统 一、前言 二、系统设计 三、系统功能设计 1会员信息管理 2 车次信息管理 3订票订单管理 4留言板管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍…

【RAG 论文】SKR:Self-Knowledge 指导下的 RAG

论文:Self-Knowledge Guided Retrieval Augmentation for Large Language Models ⭐⭐⭐⭐ Tsinghua, arXiv:2310.05002 文章目录 一、论文速读二、实现细节2.1 数据的收集2.2 引出 LLM 的 Self-Knowledge 的方法1)Direct Prompting2)In-Cont…

正则将段落分割成句子

这里分割段落不区分中英文标点,你可以根据需求改 分割后标点跟随句子后面 def split_sentences_keep_delimiter(text):pattern r[^。!!??::;;,,][。!!??::;&#xff…

记录DemoApplication.java不变蓝问题

问题 解决方案 一、点击右下角加载 二、右键项目 勾选maven

计算机毕设

随着社会和国家的重视,大学对于大学生毕业设计越来越重视。 做软件设计设计方面,前后端分离是必不可少的,代码管理工具,前后端接口测试是项目中必须要用到的工具。做大数据设计方面,主要是要用到爬虫进行数据爬取&…

多选择性更容易理解!基于可选择性遗传算法的微电网经济-低碳协调优化程序代码!

前言 随着能源危机和环境污染日益严重,传的能源已不再满足人们日益增长的能源需求,丰富清洁的可再生能源是未来的发展方向,分布式可再生能源发电技术获得了飞速进步。然而,在引入分布式可再生电源后,微电网的复杂性以…

音频智能切换器JR-AR42-A

憬锐JR-AR42-A音频自动智能切换器(四切一),具备四路模拟卡侬立体声音频输入,两路模拟卡侬立体声音频输出,其中输入第1路和输出第1路为断电直通通道。具有输入音频信号幅度判别,可设置门限电平和切换延时时间,可以根据需…

通信系列:通信中如何度量消息中所包含的信息量?如何评估通信系统的性能?

微信公众号上线,搜索公众号小灰灰的FPGA,关注可获取相关源码,定期更新有关FPGA的项目以及开源项目源码,包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、通信中如何度量消息…

代码随想录训练营31day-动态规划4

一、完全背包(参考博客) 和01背包区别在于物品可以无限次放入背包。完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。 因此在需要在遍历顺序上进行区别,参考代码随想录: 二、518.零钱兑换II 题目求的是组…

ASP.NET网上图书预约系统的设计

摘 要 《网上图书预约系统的设计》是以为读者提供便利为前提而开发的一个信息管理系统,它不仅要求建立数据的一致性和完整性,而且还需要应用程序功能的完备、易用等特点。系统主要采用VB.NET作为前端的应用开发工具,利用SQL Server2000数据…

初识指针(2)<C语言>

前言 前文介绍完了一些指针基本概念,下面介绍一下,const关键字、指针的运算、野指针的成因以及避免,assert函数等。 目录 const(常属性) 变量的常属性 指针的常属性 指针的运算 ①指针 -整数 ②指针-指针 ③指针与…

设计网页用什么软件

在设计网页时,可以使用多种软件来完成不同的任务。以下是一些常用的网页设计软件,以及它们的特点和用途。 1. Adobe Photoshop: Adobe Photoshop 是一款功能强大的图像编辑软件。在网页设计中,它常用于创建和编辑网页所需的图像、…

Linux—-vim基础使用

1、基本概念 Vim的工作模式有四种,普通模式,输入模式,命令模式,可视模式。 在终端中打开vim,只需要输入vim 文件,在普通模式下按i就会进入到输入模式,按下:进入命令模式,输入:q就可…

通义灵码:智能编码的革命性助手

通义灵码是由阿里云推出的一款基于通义大模型的智能编码辅助工具,它通过先进的人工智能技术,为开发者提供了一系列的智能编码功能,极大地提升了编码效率和质量。以下是通义灵码的一些核心功能和应用案例。 核心功能 代码智能生成 通义灵码…

视频降噪算法 Meshflow 介绍

介绍 Meshflow 视频降噪算法来自于 2017 年电子科技大学一篇高质量论文。 该论文提出了一个新的运动模型MeshFlow,它是一个空间平滑的稀疏运动场 (spatially smooth sparse motion field),其运动矢量 (motion vectors) 仅在网格顶点 (mesh vertexes) 处…

Spring+SpringMVC+Jsp实现校园二手交易系统

前言介绍 在社会快速发展的影响下,使校园二手交易系统的管理和运营比过去十年更加理性化。依照这一现实为基础,设计一个快捷而又方便的网上校园二手交易系统是一项十分重要并且有价值的事情。对于传统的管理控制模型来说,网上校园二手交易系…

【AI】深度学习框架的期望与现实 机器学习编译尚未兑现其早期的一些承诺……

深度学习框架的期望与现实 机器学习编译尚未兑现其早期的一些承诺…… 来自:Axelera AI 资深软件工程师 Matthew Barrett 原帖是linkedin帖子: https://linkedin.com/posts/matthew-barrett-a49929177_i-think-its-fair-to-say-that-ml-compilation-ac…

【LeetCode刷题】739. 每日温度(单调栈)

1. 题目链接2. 题目描述3. 解题方法4. 代码 1. 题目链接 739. 每日温度 2. 题目描述 3. 解题方法 用一个栈st保存每个数的下标,同时创建一个数组res保存结果,初始值都为0。循环遍历题目中的数组temperature。如果temperature[i] > st.top()&#x…

【arduino】库的安装方法

arduino 库的安装方法 假设你已经安装好 Arduino IDE 以 OneButton 为例来介绍几种安装方法 文章目录 arduino 库的安装方法方法一:直接安装法方法二:导入 .ZIP库方法三:将库文件夹直接复制到贡献库路径下方法四:将库文件夹直接…