【MySQL】事务Transaction

1. 事务的概念

事务是什么

在业务逻辑中使用sql,面对一些较复杂的场景,是需要多个sql语句组合起来实现的。如:银行的转账业务,若客户A要转账100元给客户B,就要两条sql:A余额减100,B余额加100;学生注册入学,要向学校数据库中插入该学生的多条信息,也要多sql组合完成。

事务 (Transcation) 的表层理解,就是一组有逻辑关联的DML(data manipulation language)语句的集合。这里的逻辑是上层决定的,通俗理解,事务就是要完成的事情,主要用于处理操作量大,复杂度高的数据。

ACID特性

mysqld是一款网络服务器,这就意味着,同一时间,可能会有多个客户端连接同一个mysqld,那么数据就会存在并发访问的安全问题。因此,事务还满足了四个特性,以保证对数据操作的可靠性。

ACID四大特性:

  1. 原子性 (Atomicity):一个事务,要么做完,要么不做,不存在中间状态。如果事务执行在中间过程出现错误,会回滚(Rollback)到最初状态,在上层看来,就好像这个事务从来没有执行过。
  2. 一致性 (Consistency):事务开始前和结束后的状态必须是可确定的,即事务必须按照预定的规则执行,使得数据库从一个确定的状态变成另一个确定的状态,保持数据库的完整性和准确性。
  3. 隔离性 (Isolation):多个事务并发执行时,一个事务的执行不会影响其它事务,每一个事务都应该感觉自己在独立执行。
  4. 持久性 (Durability):一旦事务提交,其结果就应该永久保存到数据库中,即使系统故障也不会丢失。

为什么有"事务"

MySQL设计之初并没有事务的概念,而是使用了一段时间后,设计者发现用户经常需要考虑并发操作数据库时的安全性和可靠性,大大降低了开发效率。因此设计出了“事务”,让MySQL自己保证可靠性。因此事务本质是为应用层服务的。


2. 事务的版本支持

MySQL中,只有使用了innodb引擎的表或数据库支持事务操作

mysql> show engines\G #查看数据库引擎的信息
*************************** 1. row ***************************
      Engine: InnoDB
     Support: DEFAULT
     Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES #InnoDB支持事务, 其它的都不支持
          XA: YES
  Savepoints: YES
#......
*************************** 4. row ***************************
      Engine: BLACKHOLE
     Support: YES
     Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 5. row ***************************
      Engine: MyISAM
     Support: YES
     Comment: MyISAM storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
#......

3. 事务的基本操作

  1. 事务开始:start transaction or begin
  2. 设置保存点:savepoint point_name
  3. 回滚到某个保存点:rollback to point_name
  4. 回滚到事务初始时:rollback
  5. 事务结束(提交事务):commit

将begin到commit之间的所有sql操作视为一个整体,这就是一个事务。

mysql> select * from test;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张伟   |    4396 |
|  2 | 子乔   | 1234.56 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> start transaction; #开始一个事务, begin也可以
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test (name, balance) values ('一菲', 8888);
Query OK, 1 row affected (0.00 sec)

mysql> savepoint s1; #设置保存点1(后续可以回滚到此处)
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张伟   |    4396 |
|  2 | 子乔   | 1234.56 |
|  5 | 一菲   |    8888 |
+----+--------+---------+
3 rows in set (0.00 sec)

mysql> delete from test where id=2;
Query OK, 1 row affected (0.00 sec)

mysql> savepoint s2; #设置保存点2
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张伟   |    4396 |
|  5 | 一菲   |    8888 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> insert into test (name, balance) values ('小贤', 6767);
Query OK, 1 row affected (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张伟   |    4396 |
|  5 | 一菲   |    8888 |
|  6 | 小贤   |    6767 |
+----+--------+---------+
3 rows in set (0.00 sec)

mysql> rollback to s2; #回滚到保存点2
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张伟   |    4396 |
|  5 | 一菲   |    8888 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> rollback to s1; #回滚到保存点1
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张伟   |    4396 |
|  2 | 子乔   | 1234.56 |
|  5 | 一菲   |    8888 |
+----+--------+---------+
3 rows in set (0.00 sec)

mysql> rollback; #回滚到事务初始处
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张伟   |    4396 |
|  2 | 子乔   | 1234.56 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> commit; #提交事务,即该事务结束执行
Query OK, 0 rows affected (0.00 sec)

注意:一条对InnoDB数据库进行操纵的sql语句,会被打包成一个事务,默认提交方式是自动提交,用户可以修改为手动提交。一旦autocommit=0,从你执行一个sql语句,到手动commit,就是一个事务。

mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            1 |
+--------------+
1 row in set (0.00 sec)

#autocommit自动提交默认为真,设为0则不会自动提交,需要用户手动提交事务

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            0 |
+--------------+
1 row in set (0.00 sec)

4. 事务的隔离性

事务的隔离性,本质是为了防止事务并发运行中互相干扰,让每一个事务都感觉自己在独立运行。 不同场景对隔离性的严格程度要求不同,因此也衍生出了不同的隔离级别,选择合适的隔离级别,在事务并发控制中尤其重要。下面是MySQL中四种隔离级别:

(1) 四个隔离级别

  1. Read Uncommitted (读未提交): 最宽松的隔离级别,两个事务并发运行时,一个事务对数据库进行的所有写操作,另一个事务都立即可见。读未提交的效率高,但问题也比较多。引发问题:脏读 (dirty read,一个事务在执行过程中,读到另一个事务更新且未提交的数据)

    在这里插入图片描述

  2. Read Committed (读提交): 多个事务并发运行时,若有进行更新数据(update/insert/delete)的事务,在其commit之前,其它事务不会读到该事务更新的数据,只有其提交事务(事务结束)后,其它事务才能读到。引发问题,不可重复读(non reapeatable read,一个事务在执行时,多次读取同一份数据,结果不能保证相同)

在这里插入图片描述

  1. Repeatable Read (可重复读): InnoDB默认采用的隔离级别。一个事务从begin到commit的全过程中,不会读取到其它事务更新的数据,每次读取同一份数据,都是相同的结果。只有在更新数据的事务commit之后begin的事务,才能看到更新的数据。引发问题:幻读

在这里插入图片描述

  1. Serializable (串行化): 最严格的隔离级别,同时代价最高,对表的操作都需要加锁完成,性能很低,一般很少使用,在该级别下,事务互斥地、按顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。(如下图:左边事务执行中,右边事务的insert语句阻塞)

    在这里插入图片描述

(2) 三个并发问题

脏读

脏读,可以理解为事务读取到无效的、不合理的数据(事务A在提交之前对数据的更新,对于事务B来说是无效的,因为此时事务A还没执行结束,要把其视为一个整体,这是事务的原子性决定的),通常在业务逻辑层面会有影响。

假设现在有两个事务,事务A和事务B,他们对同一张表进行操作。事务B向表中插入一条记录,若在read uncommitted隔离级别下,事务A立马能看到B更新的数据。此时,事务B因为某种原因出现故障中止,执行回滚操作,那么B先前更新的数据就是无效的。因此A读到了无效的数据,甚至A浑然不知这是无效的数据,并应用该数据,会造成意想不到的后果。这就是脏读的危害。

脏读会在Read Uncommitted隔离级别下发生。

不可重复读

在Read Committed隔离级别下,事务A(执行中)会在事务B提交后,读取到事务B更新的数据,导致事务A可能前后多次读取同一份数据的结果不同,这就是不可重复读。不可重复读会对业务逻辑造成影响,例如,begin一个事务A,对员工的工资区间进行划分,中途如果有事务B对某个员工工资进行修改,并在事务A结束前提交,可能会导致事务A的执行出错。

在这里插入图片描述

幻读

如果使用锁机制来实现RR和RC这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加行锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁(锁住整张表),读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。

MySQL中,使用MVCC来避免幻读问题。

不可重复读和幻读有点类似,都是前后多次读取结果不同。但不可重复读的重点是updatedelete,是对某一条现有数据的修改。而幻读的重点是insert,是插入一条新的数据。

📝一些承上启下的理解

  1. 在实际应用中,“可重复读”(Repeatable Read)隔离级别是相对较常见的选择。这是因为它在防止脏读(Dirty Read)和不可重复读(Non-Repeatable Read)的同时,性能开销相对较小,适用于大多数应用场景。

  2. 事务每次读取的数据,都必须是最新的吗?不一定,想想事务本质是什么,无非就是从事务begin到commit这一过程中,对数据库中的数据做增删查改,至于具体的,就要由业务逻辑决定的。这一过程会被设为一个整体(事务的原子性),而事务对数据的读取,也不一定是最新的,也肯定不是最老的,而应该是最合适的。

    这就好比一个21级大学生从入学到毕业,他在学校想要办各种事情,如在学业方面:上课、选课、转专业,应该查看21级的培养方案,不是最新的22级,更不是20级、19级。甚至在生活方面,住宿都只关心学校划分的21级的生活区域,对于与自己无关的不关心。

    对于事务而言,最“合适”的数据,就是事务begin时能看到的数据,在Repeatable Read隔离级别下,这份数据会被视为事务将要操作的数据,从begin到commit整个过程中,都不该受到其它事务对该数据更新的影响(即使其它事务已commit),事务会感觉只有自己在执行着,任何增删改工作都是自己执行的,所以读取数据也必然是合法的数据,也能保证可重复读,即不会有意料之外的数据变化,所有的数据变化都是当前事务执行的。


5. MVCC多版本并发控制

数据库事务并发运行的场景有三种:

  1. 读-读:只读不会有问题
  2. 读-写:会有线程安全问题
  3. 写-写:会有线程安全问题,要通过加锁解决

多版本并发访问(MVCC, Multi-Version Concurrency Control)用于解决事务读写并发冲突的无锁解决方法。MVCC的核心思想是在数据库中为每个事务保留多个版本的数据,以允许不同事务同时读取和修改相同的数据而不会相互干扰。

事务ID与多版本记录

数据库表中的一行数据,称为一条记录

  1. 在数据库中,活跃中的事务会被管理起来。先将事务描述为一个个的结构体对象,再通过某种方式组织起来。其次,因为MVCC的需要,事务还会分配到一个ID,这个ID是随时间戳而递增的。活跃事务的ID并不一定是连续的,可能有些中途退出,有些后续加入。事务的ID可能区分事务启动的先后顺序。

    在这里插入图片描述

  2. 数据库中的每一条数据记录,都会有三个隐藏列字段:

    • DB_TRX_ID:最近修改该记录的事务ID,标记创建(insert)或最近一次修改(update)该数据记录的事务ID。大小6byte。
    • DB_ROLL_PTR:指向当前记录上一个历史版本的指针,称为回滚指针
    • DB_ROW_ID:隐藏的自增主键,若表没有设置主键,InnoDB会用该隐藏主键创建索引。
    • 还有一个删除标记位(Delete Mask)。因为被删除的记录可能还会被恢复,因此事务中delete数据是将删除标记位置为true,恢复数据再置为false。

    例如下面有一个简单的员工表,他的一行数据记录实际记录的信息如下:

    idnamesalaryDB_TRX_IDDB_ROLL_PTRDB_ROW_ID
    1SMITH2000最近修改的事务ID回滚指针隐藏主键
  3. MVCC为事务的每一次数据修改都保留一份历史版本,历史版本数据保存在undo log缓冲区中,是内存级的缓存数据,为多版本并发控制而服务。

    例如:事务10(ID为10的事务)insert一条员工信息。以ID为主键,那么此时隐藏主键就不需要了,为空。并且由于是新插入的数据,没有历史版本,所以回滚指针也为空。

    idnamesalaryDB_TRX_IDDB_ROLL_PTRDB_ROW_ID
    1SMITH200010NULLNULL

    此时,另一个事务(ID为11)将员工SMITH的工资改为1000,数据被修改,历史版本缓存到内存中。分为以下几个步骤(这个过程是原子的)

    1. 写操作,必须先对本记录加行锁,防止与其它并发事务产生冲突
    2. 修改前,先将历史版本拷贝到undo日志缓冲区中,可以理解为是一种写时拷贝,因为这个历史版本的数据可能别的事务要使用。
    3. 备份历史数据后,即可修改原生数据:回滚指针指向历史副本数据(上一个版本),salary=1000,事务ID改为11。
    4. 一切修改完毕,释放行锁。
      在这里插入图片描述

    此时再有一个事务(ID为12)将员工SMITH的id改为2,如下:

    在这里插入图片描述

    MVCC将一条数据记录的多个历史版本,穿成一条链表,缓存在mysqld开辟的一段undo日志缓冲区中。

    前面主要针对的是对数据的update操作,而delete操作其实也可以看作是一次特殊的update,因为只需修改Delete Mask标志位。

Read View

正确认识事务对于数据的读写。在MVCC中,数据可以有多个版本的数据,以允许不同事务同时读取和修改相同的数据而不会相互干扰。不同事务,读取同一份数据,产生不同的结果,底层必然是读取不同的数据,这个不同的数据就是历史版本链中的不同节点!

对于事务的读操作,每个事务都有一个特定的“数据可见范围”,这个范围决定了事务在读取某一行记录时,应该读取哪一个版本,不应该读取哪一个版本。

对于事务的写操作,每次修改的都是原生数据(写入数据库的数据),而不是内存中的历史版本,历史版本只供读取使用,解决事务的读写并发冲突。一个事务在修改数据时,另一个事务要想读取,不用阻塞等待,而是读取合适的历史版本。

  • 快照读:在事务中,如果其它事务正在修改数据,对于普通的查询语句select,不用加锁保护,而是读取历史版本,这种读取方式称为快照读,每一个历史版本数据都称为一个快照。

  • 当前读:可以指定读当前最新版本的数据,比如:select lock in share mode(共享锁), select for update ,这种情况需要加锁保护。

事务的启动有先有后,不同事务看到不同的内容,这恰恰就是事务隔离性决定的。在这个基础下,如何决策事务应该看到多少内容,哪些内容,事务对于数据的可见性,则是隔离级别决定的。

What is Read View

Read View本质就是用来判断事务的“数据可见范围”。Read View(读视图)决定了在MVCC中,事务对于历史版本链中,哪些可见,哪些不可见。在MySQL中,Read View是一个类对象,每个事务关联一个Read View,用以判断该事务的可见性。

在这里插入图片描述

Read View结构体的源代码中,简化后的几个主要字段如下:

class ReadView
{
    //省略...
private:
	ids_t m_ids; 				//创建视图时活跃的事务ID
	trx_id_t m_up_limit_id;		//低水位,小于该ID的事务均可见
	trx_id_t m_low_limit_id; 	//高水位,大于等于该ID的事务均不可见
	trx_id_t m_creator_trx_id 	//创建该ReadView的事务ID
}

字段详解:

  • m_ids:一个集合,记录创建ReadView时,活跃的事务ID;

  • m_up_limit_id:记录m_ids集合中最小的事务ID;

  • m_low_limit_id: 记录创建ReadView时,系统尚未分配的下一个事务ID,也就是目前已经出现过的最大ID+1;

  • m_creator_trx_id:创建该ReadView的事务ID

ReadView何时生成?这里我们先统一认为是首次select时创建,后面详解。

ReadView in MVCC

现在我们已经有了Read View读视图和版本链了,接下来就可以真正认识到MVCC中是如何实现快照读的。快照读的本质,就是当事务读取一条记录时,遍历该记录的历史版本链,根据自己的Read View,找到符合读取条件的版本。

  1. 为事务创建一个ReadView,可以理解为一次“快照”,就像在那一瞬间,为系统中活跃的事务拍下一张照片,记录下它们的ID。

  2. 事务ID的大小,可以区分事务的先后顺序,因为事务ID是随时间增长的。

  3. 事务如何通过ReadView判断数据可见性呢?见下图:

    在这里插入图片描述

快照形成Read View(后面简称快照),得到的m_ids列表是图中这些不一定连续的“小红点”,因为在快照前可能有一些事务已经提交了,不属于活跃事务,导致m_ids的不连续。事务select数据时,就会将数据的DB_TRX_ID(最近修改事务ID)和ReadView中的字段作对比,查询条件:

  1. DR_TRX_ID < up_limit_id,表示最近修改这条记录的事务(后面简称last事务)在快照前已经提交了,数据可见(这个范围的ID在上图的最左区间);

  2. DR_TRX_ID == creator_id, 表示last事务就是本事务,那必然可见;

  3. DR_TRX_ID ∈[up_limit_id, low_limit_id) ,分两种情况

    • DR_TRX_ID不在m_ids中,表示last事务已经提交,数据可见
    • DR_TRX_ID在m_ids中,表示last事务依然活跃,随时可能再次修改数据,数据不可见。
  4. DR_TRX_ID >= low_limit_id, 表示last事务是快照之后新创建的事务,数据不可见。

RR和RC的本质区别

RR (Repeated Read) 和 RC (Read Committed)两种隔离级别的本质区别,实际上是ReadView读视图的创建方式不同!

  1. 对于RR可重复读,只有在首次select时快照创建一次ReadView,直到事务提交,都使用这个ReadView。ReadView始终不变,事务对于数据的可见性也不变。
  2. 对于RC读提交,每次select都会更新一次ReadView,即每次查询数据前都会更新数据可见性。

这样一来便很好理解两种隔离级别的现象。对于RR,因为数据可见性始终不变,所以即使快照时活跃的事务已经提交,本事务也浑然不知,始终将其视为活跃的事务,其最新修改的数据对本事务不可见,那么本事务每次select读取的结果都是一致的,可重复读由此而来。而对于RC,因为数据可见性每次查询前都会更新,所以一旦历史快照过的事务已经提交,再次select时就会将其ID从本事务ReadView的m_ids中去除,其最新修改的数据本事务就可见了,这就是为什么能够读提交。


总结:MySQL数据库中,为了实现事务并发控制,updata、delete、insert这些写操作是需要加锁的,锁的类型有很多,如:行锁、读锁写锁(锁整张表)、Next-Key锁。而select读操作与写操作并不冲突,因为它是MVCC多版本并发控制实现的。这就是通过读写锁+MVCC完成事务的隔离性。

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

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

相关文章

react-router-dom5升级到6

前言 升级前版本为5.1.2 下载与运行 下载 npm install react-router-dom6运行 运行发现报错: 将node_modules删除&#xff0c;重新执行npm i即可 运行发现如下报错 这是因为之前有引用react-router-dom.min&#xff0c;v6中取消了该文件&#xff0c;所以未找到文件导致报错。…

浅谈数字孪生的应用与发展

1、数字孪生概念 ”数字孪生是充分利用物理模型、传感器更新、运行历史等数据,集成多学科、多物理量、多尺度、多概率的仿真过程,在虚拟空间中完成映射,从而反映相对应的实体装备的全生命周期过程。数字孪生是一种超越现实的概念,可以被视为一个或多个重要的、彼此依赖的装…

Kubernetes集群部署Rook Ceph实现文件存储,对象存储,块存储

Kubernetes集群部署Rook Ceph部署Ceph集群 1. Rook Ceph介绍 Rook Ceph是Rook项目中的一个存储方案&#xff0c;专门针对Ceph存储系统进行了优化和封装。Ceph是一个高度可扩展的分布式存储系统&#xff0c;提供了对象存储、块存储和文件系统的功能&#xff0c;广泛应用于提供…

Spring Data Redis对象缓存序列化问题

相信在项目中&#xff0c;你一定是经常使用 Redis &#xff0c;那么&#xff0c;你是怎么使用的呢&#xff1f;在使用时&#xff0c;有没有遇到同我一样&#xff0c;对象缓存序列化问题的呢&#xff1f;那么&#xff0c;你又是如何解决的呢&#xff1f; Redis 使用示例 添加依…

Stable Diffusion WebUI制作光影文字效果

在huggingface上下载control_v1p_sd15_brightness模型。 将模型放在stable-diffusion-webui\extensions\sd-webui-controlnet\models目录下。 SD参数配置 正向提示词&#xff1a; city,Building,tall building,Neon Light, gentle light shines through, anime style, paint…

AI模型训练【偏差/方差】与【欠拟合/过拟合】

在我们拿到一个数据集&#xff0c;高高兴兴准备训练一个模型时&#xff0c;会遇到欠拟合或过拟合的问题&#xff0c;业内也喜欢用偏差和方差这两指标去定义它们&#xff0c;那这些词什么意思呢&#xff1f;有什么方法能避免/解决 欠拟合和过拟合呢&#xff1f; 这其实是非常非常…

【测试基础】构造测试数据之 MySQL 篇

构造测试数据之 MySQL 篇 作为一名测试工程师&#xff0c;我们经常会构造测试数据进行一些功能验证。为了暴露更多的问题&#xff0c;在测试数据的构造上&#xff0c;我们应该尽可能的构造不同类型的字段数据&#xff0c;且一张表的字段最好不低于 10 10 10 个。 对于 MySQL …

UDP信号多个电脑的信息传输测试、配置指南

最近要做一个东西&#xff0c;关于一个软件上得到的信号&#xff0c;如何通过连接的局域网&#xff0c;将数据传输出去。我没做过相关的东西&#xff0c;但是我想应该和软件连接数据库的过程大致是差不多的&#xff0c;就一个ip和一个端口号啥的。 一.问题思路 多个设备同时连…

自动化测试系列 之 Python单元测试框架unittest

一、概述 什么是单元测试 单元测试是一种软件测试方法&#xff0c;是测试最小的可测试单元&#xff0c;通常是一个函数或一个方法。 在软件开发过程中&#xff0c;单元测试作为一项重要的测试方法被广泛应用。 为什么需要单元测试 单元测试是软件开发中重要的一环&#xf…

微服务系列之分布式事务理论

概述 事务是由一组操作构成的可靠的独立的工作单元&#xff0c;事务具备ACID的特性&#xff0c;即原子性、一致性、隔离性和持久性。 分类 大多数情况下&#xff0c;分类是没有意义的一件事。但是分类可以一定程度上&#xff0c;加深理解。 实现 从实现角度来看&#xff0…

c语言函数篇——递归函数

递归函数的工作原理 递归函数的工作原理基于两个主要部分&#xff1a;基本情况和递归情况。基本情况是函数不再调用自身的条件&#xff0c;当达到基本情况时&#xff0c;递归停止并返回结果。递归情况是函数调用自身的部分&#xff0c;它将问题分解为更小的、相似的子问题。 …

【Matlab】基于遗传算法优化BP神经网络 (GA-BP)的数据时序预测

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/88682033 一&#xff0c;概述 基于遗传算法优化BP神经网络 (GA-BP) 的数据时序预测是一种常用的机器学习方法&#xff0c;用于预测时间序列数据的趋势和未来值。 在使用这种方法之前&#xff0c;需要将时间序…

微信小程序开发系列-07组件

微信小程序开发系列目录 《微信小程序开发系列-01创建一个最小的小程序项目》《微信小程序开发系列-02注册小程序》《微信小程序开发系列-03全局配置中的“window”和“tabBar”》《微信小程序开发系列-04获取用户图像和昵称》《微信小程序开发系列-05登录小程序》《微信小程序…

【FileZilla】的基本使用

一、FileZilla的使用 1.1 FileZilla简介 1.2 软件下载 到官方网站下载 FileZilla 的服务端和客户端程序 FileZilla - The free FTP solution 自行下载即可 1.3 软件安装 &#xff08;1&#xff09;先安装服务端【傻瓜式安装】&#xff0c;一直下一步下一步安装即可 &#xf…

uniapp中组件库的丰富NumberBox 步进器的用法

目录 基本使用 #步长设置 #限制输入范围 #限制只能输入整数 #禁用 #固定小数位数 #异步变更 #自定义颜色和大小 #自定义 slot API #Props #Events #Slots 基本使用 通过v-model绑定value初始值&#xff0c;此值是双向绑定的&#xff0c;无需在回调中将返回的数值重…

【linux】head的用法 输出文件开头的内容

在linux可以用find查找一个文件&#xff0c;可以用grep查找符合要求的文件内容&#xff0c;但是有的时候希望查看文件的前几行或者后几行&#xff08;其实这种场景经常可以遇到&#xff0c;比如接触到日志分析的时候&#xff09;&#xff0c;那就应该使用head和tail这两个工具了…

SpringMVC源码解析——DispatcherServlet初始化

在Spring中&#xff0c;ContextLoaderListener只是辅助功能&#xff0c;用于创建WebApplicationContext类型的实例&#xff0c;而真正的逻辑实现其实是在DispatcherServlet中进行的&#xff0c;DispatcherServlet是实现Servlet接口的实现类。Servlet是一个JAVA编写的程序&#…

STM32——F407定时器概述

1 定时器分类 定时器类型数量位号位宽时钟捕获/比较输出DMA请求计数互补输出基本2TIM6,TIM716bitAPB1-有递增-通用2TIM2,TIM532bitAPB14通道有递增、递减、中心对齐-通用2TIM3,TIM432bitAPB14通道有递增、递减、中心对齐-通用1TIM916bitAPB14通道有递增-通用2TIM10、TIM1116bi…

图像分割实战-系列教程3:unet医学细胞分割实战1(医学数据集、图像分割、语义分割、unet网络、代码逐行解读)

&#x1f341;&#x1f341;&#x1f341;图像分割实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 上篇内容&#xff1a; Unet系列算法 下篇内容&#xff1a; unet医学细胞分割实战2 1、医学细胞数据…

STM32——通用计时器

通用计时器框图 1.时钟源 1&#xff09;内部时钟(CK_INT) 2&#xff09;外部时钟模式 1&#xff1a;外部输入引脚(TIx)&#xff0c;x1&#xff0c;2&#xff08;即只能来自于通道 1 或者通道 2&#xff09; 3&#xff09;外部时钟模式 2&#xff1a;外部触发输入(ETR) 4&#…