MySQL 中的 MVCC 版本控制机制原理

news2025/4/7 20:18:06

1. MVCC(多版本并发控制)概述

MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种数据库事务并发控制机制,主要用于提高数据库的读写性能。它通过维护数据的多个版本,使得读操作无需加锁,同时保证一致性,减少了事务之间的阻塞。

在 MySQL 的 InnoDB 存储引擎中,MVCC 主要用于**可重复读(REPEATABLE READ)读已提交(READ COMMITTED)**这两种事务隔离级别。

2. MVCC 的实现原理

MVCC 通过隐藏删除和修改的行,加上一些额外的信息来实现多版本控制。主要依赖于 UNDO日志事务 ID(Transaction ID)

(1) 数据隐藏 & 版本控制

InnoDB 的数据行结构中包含两个隐藏的字段:

  • trx_id(事务 ID):表示最近对该行进行修改的事务 ID。
  • roll_pointer(回滚指针):指向该行的 旧版本(即 undo log 记录),从而支持回滚和版本链。

当数据被修改时:

  • 更新(UPDATE):不会直接修改数据,而是将旧版本保存到 undo log,然后生成新的数据版本,并更新 trx_id
  • 删除(DELETE):不会立即删除,而是生成一个新版本,标记该行已删除,并记录 undo log
  • 插入(INSERT):只插入最新版本的数据,不会产生历史版本(因此插入数据不受 MVCC 影响)。

(2) Undo Log 及回滚指针

Undo Log 主要用于:

  • 回滚事务:当事务失败或回滚时,可以通过 Undo Log 恢复数据。
  • MVCC 读取旧版本数据:Undo Log 形成一个版本链,事务可以基于 trx_id 获取合适的旧版本数据,而不会影响其他事务。

(3) MVCC 的可见性规则

当一个事务读取数据时,它需要判断哪些数据版本对自己可见。InnoDB 通过 Read View(读取视图)来管理可见性。

可见性规则如下:

  1. 当前事务 ID (trx_id) < Read View 的最小活跃事务 ID (min_trx_id)
    • 该数据版本已经提交,对当前事务可见。
  2. 当前事务 ID (trx_id) > Read View 的最大活跃事务 ID (max_trx_id)
    • 该数据版本是在当前事务之后创建的,不可见。
  3. 当前事务 ID (trx_id) 介于 min_trx_idmax_trx_id 之间
    • trx_id 属于活跃事务列表,则表示该事务还未提交,不可见。
    • trx_id 不在活跃事务列表中,则可见。

3. MVCC 在不同事务隔离级别下的表现

隔离级别MVCC 读取的版本
读已提交(Read Committed)每次 SELECT 都创建新的 Read View,读取最近提交的数据版本
可重复读(Repeatable Read)事务开始时创建 Read View,整个事务期间保持一致
串行化(Serializable)不使用 MVCC,需要加锁

未提交读(Read Uncommitted) 不使用 MVCC,而是直接读取最新的数据版本,因此可能会读取到未提交的数据(脏读)。

4. MVCC 的优缺点

优点

  1. 非阻塞读:读取数据不需要加锁,提高并发性能。
  2. 减少事务冲突:多个事务可以同时操作不同版本的数据,避免不必要的锁竞争。
  3. 提高可重复读性能:避免了 SELECT 过程中加锁的开销。

缺点

  1. 需要额外存储:Undo Log 需要存储多个版本的数据,可能会导致存储空间增长。
  2. 版本回收问题:老版本数据需要定期清理,否则会影响性能。
  3. 不适用于高并发写入:因为写入仍然需要锁定行,多个事务同时写入相同数据时,仍然需要等待。

5. 总结

  • MVCC 主要依赖 trx_idundo log 来维护数据的多个版本,允许事务在不同时间点读取合适的数据版本。
  • 不同事务隔离级别下 MVCC 的行为不同Read Committed 每次查询创建新 Read View,而 Repeatable Read 事务开始时创建 Read View 并保持不变。
  • MVCC 使得大部分读操作无需加锁,提高了数据库的并发能力,但也带来存储开销和版本管理的挑战。

在实际应用中,MVCC 适用于读多写少的场景,对于高并发写入,可能需要结合锁机制或优化索引来提升性能。

理解 Read View 和 事务可见性规则

当事务在 READ COMMITTEDREPEATABLE READ 隔离级别下执行 SELECT 语句时,InnoDB 不会直接读取最新的数据版本,而是通过 Read View 来决定 哪个数据版本对当前事务可见

Read View 的关键字段

在 MVCC 机制下,每个事务在读取数据时都会维护一个 Read View,它主要包含:

  • trx_id:每个事务都有一个唯一递增的事务 ID,越新的事务 ID 值越大。
  • m_ids(活跃事务列表):当 Read View 生成时,当前正在执行但未提交的事务 ID 列表。
  • min_trx_id(最小活跃事务 ID):m_ids 中最小的事务 ID。
  • max_trx_id(下一个将要分配的事务 ID):比当前所有活跃事务 ID 都大的值,代表未来新事务的起始 ID。

数据可见性规则

当事务 T 读取一行数据时,该数据的 trx_id(创建它的事务 ID)将与 T 的 Read View 进行比较,以决定该版本是否可见。

数据版本的 trx_id 与 Read View 比较可见性解释
trx_id < min_trx_id✅ 可见该数据版本是比 Read View 生成时更早的事务创建的,并且该事务已经提交。
trx_id > max_trx_id❌ 不可见该数据版本是比 Read View 生成时更晚的事务创建的,因此不可见。
min_trx_id ≤ trx_id < max_trx_idtrx_id ∈ m_ids❌ 不可见该数据版本是由某个活跃未提交的事务创建的,不可见。
min_trx_id ≤ trx_id < max_trx_idtrx_id ∉ m_ids✅ 可见该数据版本的事务已提交,但其事务 ID 仍然在 Read View 生成时的范围内,因此可见。

直观示例

假设有如下事务操作:

  1. T1 开启事务,写入数据 A,但未提交 (trx_id = 10)。
  2. T2 开启事务,创建 Read View (min_trx_id = 10, max_trx_id = 15)
  3. T3 开启事务,并更新数据 A (trx_id = 12),但也未提交
  4. T4 开启事务 (trx_id = 15),并提交更新数据 A 的事务

此时:

  • T2 读取数据 A 时,trx_id = 10m_ids 内,未提交,不可见。
  • T2 也看不到 trx_id = 12(未提交)。
  • T2 只能看到 trx_id < 10 的数据,即 T1 之前的版本。
  • T4 提交后,T2 依然看不到 trx_id = 15 创建的数据,因为 Read View 在创建时已经固定了事务状态。

如何理解 Read View 的作用

  1. 保证一致性
    • REPEATABLE READ 级别下,同一事务内多次 SELECT 看到的数据是一致的,因为 Read View 在事务开始时创建,不会变化。
    • READ COMMITTED 级别下,每次 SELECT 都会生成新的 Read View,所以可以看到最新已提交的数据。
  2. 避免幻读
    • REPEATABLE READ 级别的 Read View 确保了事务期间看到的行数据不会随其他事务的提交而变化,但对 INSERT 仍然可能出现幻读(需借助 Next-Key Lock 解决)。
  3. 提升并发性能
    • 通过 Read View 让事务读取旧版本数据,而不需要加锁,避免阻塞其他事务。

创建 Read View

T1 开启事务时是否创建 Read View 取决于 事务的隔离级别执行的 SQL 语句

什么时候创建 Read View?

操作Read View 何时创建?
READ COMMITTED每次执行 SELECT 语句时都会创建一个新的 Read View。
REPEATABLE READ第一次执行 SELECT 语句时 创建 Read View,并在整个事务生命周期内保持不变。
SERIALIZABLE由于需要加锁,不依赖 MVCC Read View,而是直接使用锁进行事务隔离。

T1 开启事务时是否创建 Read View?

  • 如果 T1 只是执行 START TRANSACTION;,此时并不会创建 Read View,只是开启一个事务,还没有执行任何查询。
  • 只有当 T1 执行 SELECT 语句时,才会创建 Read View(前提是隔离级别需要 Read View,如 READ COMMITTEDREPEATABLE READ)。
  • READ COMMITTED 级别下,每次查询都会创建一个新的 Read View。
  • REPEATABLE READ 级别下,T1 第一次查询时 会创建 Read View,后续查询都会使用这个 Read View,即使其他事务提交了新数据,T1 也不会看到(除非显式提交)。

除非显示提交的含义

这里的 “除非显式提交” 是指 T1 事务提交 (COMMIT) 后,再次开启新的事务并执行查询,此时会生成一个新的 Read View,从而看到其他事务提交的数据。

1. 提交事务的方式

在 MySQL 中,提交事务的方式主要有两种:

① 显式提交(Explicit Commit)

手动执行 COMMIT 语句 来提交事务:

BEGIN;
SELECT * FROM users;  -- 事务 T1 创建 Read View
-- 此时 Read View 固定,无法看到其他事务提交的数据

COMMIT;  -- 显式提交事务

COMMIT 之后,事务结束,Read View 被销毁
如果 T1 之后再执行 SELECT,需要开启新事务,此时会创建新的 Read View,可以看到最新数据。

② 隐式提交(Implicit Commit)

MySQL 在某些情况下会自动提交事务,比如执行以下 SQL 语句时:

  • DDL 语句CREATE, ALTER, DROP, TRUNCATE
  • SET AUTOCOMMIT = 1(默认开启自动提交)
  • LOCK TABLES(会自动提交当前事务)

示例:

BEGIN;
SELECT * FROM users;  -- 创建 Read View
ALTER TABLE users ADD COLUMN age INT;  -- DDL 语句触发隐式提交
SELECT * FROM users;  -- 这时 Read View 重新创建

执行 ALTER TABLE 之后,事务被 MySQL 隐式提交,Read View 也重新生成。

2. “除非显式提交” 的含义

REPEATABLE READ 级别,T1 第一次查询时创建的 Read View 不会变化,即使其他事务 (T2) 提交了新数据,T1 也看不到。
只有当 T1 执行 COMMIT 之后,再次查询时才会创建新的 Read View,并看到最新数据。

(1) 事务 T1 只读取,不提交
-- 事务 T1 开启
BEGIN;
SELECT * FROM users;  -- 创建 Read View (RV1)

此时 Read View (RV1) 记录了活跃事务列表。

-- 事务 T2 执行更新
BEGIN;
INSERT INTO users VALUES (2, 'Bob');
COMMIT;

事务 T2 提交了新数据。

-- T1 继续查询
SELECT * FROM users;  -- 仍然使用 Read View (RV1),看不到 Bob

由于 T1 没有提交,它的 Read View 没变,所以看不到 T2 提交的数据。

(2) T1 提交后,再次查询
COMMIT;  -- 显式提交事务
BEGIN;
SELECT * FROM users;  -- 重新创建 Read View (RV2)

T1 提交事务后,Read View (RV1) 销毁。
新事务创建新的 Read View (RV2),此时能看到 Bob!

3. 事务提交的不同方式总结

提交方式触发时机是否创建新 Read View
显式提交 (COMMIT)事务手动提交✅ 是,提交后开启新事务会创建新的 Read View
隐式提交(DDL、LOCK TABLES 等)特定 SQL 语句执行时自动提交✅ 是,事务自动提交,Read View 重新创建
未提交 (ROLLBACK 或未执行 COMMIT)事务未结束❌ 否,事务继续使用旧的 Read View

4. 结论

🔹 “除非显式提交” 的意思是:

  • REPEATABLE READ 级别下,T1 不会看到其他事务的新提交数据,除非 T1 自己先 COMMIT 事务。
  • COMMIT 之后,Read View 被销毁,新的查询会创建新的 Read View,这时就能看到最新数据了!

示例

假设数据库初始状态:

CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));
INSERT INTO users VALUES (1, 'Alice');

场景 1:REPEATABLE READ

-- T1 事务
BEGIN;
SELECT * FROM users;  -- (此时创建 Read View)

这时 Read View 记录了当前活跃事务列表和 min_trx_id

-- T2 事务
BEGIN;
INSERT INTO users VALUES (2, 'Bob');
COMMIT;
-- T1 继续查询
SELECT * FROM users;  -- 仍然只能看到 Alice,因为 Read View 不变

T1 的 Read View 在事务开始时确定,不会看到 Bob

场景 2:READ COMMITTED

-- T1 事务
BEGIN;
SELECT * FROM users;  -- (此时创建 Read View)

Read View 记录了当前事务状态。

-- T2 事务
BEGIN;
INSERT INTO users VALUES (2, 'Bob');
COMMIT;
-- T1 再次查询
SELECT * FROM users;  -- 这次能看到 Bob,因为 Read View 在每次查询时重新生成

READ COMMITTED 级别每次查询都会创建新的 Read View,因此 T1 在第二次查询时能看到 T2 提交的数据。

Read View 绑定性

1. 事务只能看到自己创建的 Read View

  • Read View 是事务内部的快照,用于决定当前事务能看到哪些数据版本。
  • 不同事务的 Read View 互不影响,事务 A 无法访问事务 B 的 Read View。
  • 每个事务只能使用自己创建的 Read View 来查询数据。

2. 示例:不同事务的 Read View 是独立的

-- 事务 A 开始
BEGIN;
SELECT * FROM users;  -- 创建 Read View A

此时 Read View A 记录了当前活跃事务列表。

-- 事务 B 开始
BEGIN;
SELECT * FROM users;  -- 创建 Read View B

此时 Read View B 也创建了,可能与 Read View A 不同。

-- 事务 A 继续查询
SELECT * FROM users;  -- 仍然使用 Read View A

事务 A 只能使用自己的 Read View A,而不会使用事务 B 的 Read View。

-- 事务 B 继续查询
SELECT * FROM users;  -- 仍然使用 Read View B

事务 B 只能使用自己的 Read View B,而不会使用事务 A 的 Read View。

所以,每个事务的 Read View 就像是自己专属的“数据时间快照”,它决定了事务能看到的数据版本 🎯。

默认事务

MySQL 默认是自动提交 (AUTOCOMMIT = 1),即每条 SQL 语句都会作为一个独立的事务执行,并在执行后立即提交。

  • 在默认情况下,每条 SQL 语句(如 INSERTUPDATEDELETE)都会立即提交,不会等待 COMMIT
  • 只有显式关闭 AUTOCOMMIT 或使用 BEGIN; / START TRANSACTION; 才能开启手动提交模式。

示例 1:默认情况下,每条语句都会自动提交

SELECT @@AUTOCOMMIT;  -- 查询当前自动提交模式
-- 结果:1 (表示自动提交开启)

INSERT INTO users VALUES (1, 'Alice');  
-- 这条 INSERT 语句在执行后立即提交,不需要手动 COMMIT。

如果想要手动管理事务(即不让 SQL 语句自动提交),可以通过以下两种方式:

方法 1:使用 SET AUTOCOMMIT = 0

SET AUTOCOMMIT = 0;  -- 关闭自动提交
BEGIN;               -- 开启事务
UPDATE users SET name = 'Bob' WHERE id = 1;
COMMIT;              -- 手动提交事务

注意:

  • SET AUTOCOMMIT = 0 的作用是对当前会话生效,也就是说,当前连接的所有操作都会使用手动提交,直到手动 COMMITROLLBACK
  • 但是 如果连接断开,AUTOCOMMIT 也会恢复为默认值 1

方法 2:使用 START TRANSACTION; / BEGIN;

START TRANSACTION;  -- 开启事务(不会立即提交)
UPDATE users SET name = 'Charlie' WHERE id = 1;
COMMIT;             -- 手动提交事务

或者:

BEGIN;  -- 也是开启事务
UPDATE users SET name = 'David' WHERE id = 1;
ROLLBACK;  -- 撤销事务(如果不想提交)

区别:

  • START TRANSACTION;BEGIN; 只是针对当前事务关闭自动提交,并不会影响整个会话。
  • 当事务提交 (COMMIT) 或回滚 (ROLLBACK) 后,自动提交模式 (AUTOCOMMIT=1) 仍然有效。

事务提交模式总结

方式作用影响范围恢复方式
AUTOCOMMIT = 1(默认)每条 SQL 语句都会立即提交整个会话(每个 SQL 语句)无需恢复
SET AUTOCOMMIT = 0关闭自动提交,手动 COMMIT 事务仅当前会话连接断开后恢复为 1
START TRANSACTION; / BEGIN;仅当前事务手动提交只影响当前事务COMMIT / ROLLBACK 后恢复自动提交

结论

MySQL 默认是自动提交 (AUTOCOMMIT=1),每条 SQL 语句都会自动提交。
如果要手动管理事务,可以用 SET AUTOCOMMIT = 0START TRANSACTION; / BEGIN;
手动管理事务时,需要显式 COMMITROLLBACK,否则事务可能会被锁住,影响并发性能。

如果想在事务中批量执行 SQL 并手动控制提交,建议使用 START TRANSACTION;,这样不会影响整个会话的自动提交行为! 🚀

MyBatis 默认行为

如果在 Java 代码中使用 MyBatis 执行 SQL 语句,而没有显式管理事务(比如没有调用 commit()rollback()),那么默认情况下,MySQL 的 AUTOCOMMIT=1 会生效,每条 SQL 语句都会自动提交。

  • MyBatis 不主动管理事务,而是依赖数据库的默认行为(自动提交)。
  • 这意味着即使执行 INSERTUPDATEDELETE 语句,也不需要显式 COMMIT,数据会立即生效。

不手动管理事务(默认自动提交)

SqlSession sqlSession = sqlSessionFactory.openSession(); // 默认不手动控制事务
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.insertUser(new User(1, "Alice"));  // 这条 SQL 立即提交
sqlSession.close();  // 关闭连接

这里的 insertUser() 方法执行后,SQL 语句会立即提交,因为 MySQL 默认是 AUTOCOMMIT=1

如果想手动控制事务(比如执行多个 SQL 语句后统一提交),需要:

  1. 关闭自动提交openSession(false))。
  2. 手动 commit()rollback() 事务

(2)手动管理事务

SqlSession sqlSession = sqlSessionFactory.openSession(false); // 关闭自动提交
try {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    userMapper.insertUser(new User(2, "Bob"));
    userMapper.updateUser(new User(2, "Bob Updated"));

    sqlSession.commit(); // 统一提交事务
} catch (Exception e) {
    sqlSession.rollback(); // 发生异常则回滚
} finally {
    sqlSession.close(); // 关闭连接
}

这里 openSession(false) 关闭了自动提交,所以:

  1. insertUser()updateUser() 不会立即提交。
  2. 只有 sqlSession.commit() 执行后,数据才会真正写入数据库。
  3. 如果发生异常,会执行 rollback(),撤销事务中的所有操作。

Spring + MyBatis 事务管理

在实际的 Spring 项目中,事务通常是由 Spring 事务管理器@Transactional)来控制的,而不是手动调用 commit()rollback()

(3)使用 Spring 事务管理

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Transactional  // 由 Spring 统一管理事务
    public void createUser() {
        userMapper.insertUser(new User(3, "Charlie"));
        userMapper.updateUser(new User(3, "Charlie Updated"));
        // 发生异常时,Spring 会自动回滚事务
    }
}

Spring 事务管理的特点:

  • @Transactional 让 Spring 统一管理事务,默认是 非自动提交 的(AUTOCOMMIT=0)。
  • 方法执行成功时,Spring 自动提交 事务。
  • 发生异常时,Spring 自动回滚 事务。

结论

MyBatis 默认使用 MySQL 的 AUTOCOMMIT=1,每条 SQL 语句执行后都会自动提交。
如果需要手动管理事务,可以使用 openSession(false) 关闭自动提交,并手动 commit()rollback()
在 Spring 项目中,通常使用 @Transactional 让 Spring 统一管理事务,而不会直接调用 MyBatis 的 commit()

所以,如果 MyBatis 代码里没有 commit(),而数据仍然成功写入数据库,那就是 MySQL 的自动提交功能在起作用! 🚀

SpringBoot 中的事务提交

Spring Boot 中,事务的提交方式主要取决于 是否使用了 Spring 事务管理@Transactional),并不是单纯依赖 MySQL 的 AUTOCOMMIT=1

1. Spring Boot 默认的事务行为

Spring Boot 默认不会自动提交事务,它的事务管理主要依赖于 Spring 事务管理器,而 Spring 事务管理默认是 AUTOCOMMIT=0(手动提交)

在 Spring Boot 项目中,通常有两种情况:

  1. 没有 @Transactional(默认自动提交,每个 SQL 语句执行后都会立即提交,依赖 MySQL AUTOCOMMIT=1)。
  2. 使用 @Transactional(Spring 事务管理,事务方法结束后才会提交,默认 AUTOCOMMIT=0)。

2. 如果没有 @Transactional,会使用 MySQL 自动提交

如果 没有 @Transactional,那么 MyBatis 或 JPA 在执行 SQL 语句时,都会立即提交事务,这时依赖 MySQL 的 AUTOCOMMIT=1

3. 如果使用了 @Transactional,Spring 事务管理接管提交

🚀 @Transactional 作用下:

  • Spring 事务管理会关闭 MySQL 自动提交 (AUTOCOMMIT=0)。
  • 所有 SQL 语句都会等到方法执行结束后才提交。
  • 如果方法中抛出异常,Spring 会自动回滚事务,之前的 SQL 操作也不会生效。

4. 如何验证 Spring Boot 是否在使用事务?

可以在 application.properties 中查看 spring.datasource 配置:

spring.datasource.hikari.auto-commit=false  # Hikari 数据源默认关闭自动提交
  • 如果 auto-commit=false,那么即使没有 @Transactional,数据库操作也不会立即提交,而是等待 commit()
  • 如果 auto-commit=true,则默认会使用 MySQL 的 AUTOCOMMIT=1,每条 SQL 执行后立即提交。

此外,还可以手动检查数据库的 AUTOCOMMIT 状态:

SELECT @@autocommit;

如果返回 1,说明 MySQL 处于自动提交模式,SQL 执行后会立即提交。

在 Spring Boot 代码中看不到 commit(),是因为 Spring 事务管理器帮我们自动管理了事务提交,而不是依赖 MySQL 自动提交。

Spring Boot 代码中不需要手动 commit(),并不一定是因为 MySQL 自动提交,而是 Spring 事务管理器负责了事务的提交和回滚! 🚀

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

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

相关文章

JWT认证服务

JSON Web Token&#xff08;JWT&#xff09;是一种用于在网络应用间安全地传递信息的紧凑、自包含的方式。以下是关于 JWT 认证服务器更详细的介绍&#xff0c;包括其意义、作用、工作原理、组成部分、时效性相关内容、搭建条件以及代码案例。 JWT 的意义与作用 意义&#xf…

手机中的type-C是如何防水的呢?

防水类型的type-C座子&#xff1a; 电子产品防水等级的区分&#xff1a; 这里的“IP”是国际防护标准等级&#xff1b;简而言之&#xff0c;IPXX中“XX”两位数字分别代表防尘和防水等级&#xff0c;其中防尘等级从0&#xff5e;6&#xff0c;防水等级则从0&#xff5e;8。 第…

[Redis]Redis学习开篇概述

欢迎来到啾啾的博客&#x1f431;。 这是一个致力于构建完善 Java 程序员知识体系的博客&#x1f4da;。 它记录学习点滴&#xff0c;分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 欢迎评论交流&#xff0c;感谢您的阅读&#x1f604;。 引言 大家好…

WordPress浮动广告插件+飘动效果客服插件

源码介绍 WordPress浮动广告插件飘动效果客服插件 将源码上传到wordpress的插件根目录下&#xff0c;解压&#xff0c;然后后台启用即可 截图 源码免费获取 WordPress浮动广告插件飘动效果客服插件

Java基础关键_034_网络编程

目 录 一、概述 二、网络编程三要素 1.IP 地址 2.端口号 3.通信协议 &#xff08;1&#xff09;说明 &#xff08;2&#xff09;OSI 七层参考模型 &#xff08;3&#xff09;TCP/IP 四层参考模型 三、网络编程基础类 1.InetAddress 2.URL &#xff08;1&#xff09…

Ubuntu交叉编译器工具链安装

声明 本博客所记录的关于正点原子i.MX6ULL开发板的学习笔记&#xff0c;&#xff08;内容参照正点原子I.MX6U嵌入式linux驱动开发指南&#xff0c;可在正点原子官方获取正点原子Linux开发板 — 正点原子资料下载中心 1.0.0 文档&#xff09;&#xff0c;旨在如实记录我在学校学…

基于Python的招聘推荐数据可视化分析系统

【Python】基于Python的招聘推荐数据可视化分析系统&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 &#x1f680;&#x1f31f; 基于Python的招聘推荐数据可视化分析系统&#xff01;&#x1…

光谱相机在工业中的应用

光谱相机&#xff08;多光谱、高光谱、超光谱成像技术&#xff09;在工业领域通过捕捉物质的光谱特征&#xff08;反射、透射、辐射等&#xff09;&#xff0c;结合化学计量学与人工智能算法&#xff0c;为工业检测、质量控制和工艺优化提供高精度、非接触式的解决方案。以下是…

使用PyQt5绘制水波浪形的柱状显示流量—学习QTimer+QPainterPath

前言&#xff1a;进入学习Python开发上位机界面的第二阶段&#xff0c;学习如何开发自定义控件&#xff0c;从常用的控件入手学习&#xff0c;本期主要学习如何使用PyQt5绘制水波浪形的柱状显示流量。但是最后我放弃了&#xff0c;因为水波的效果达不到我想要的。 1. 明确想要…

C++蓝桥杯实训篇(二)

片头 嗨咯~小伙伴们&#xff01;今天我们来一起学习算法和贪心思维&#xff0c;准备好了吗&#xff1f;咱们开始咯&#xff01; 第1题 数位排序 对于这道题&#xff0c;我们需要自己写一个排序算法&#xff0c;也就是自定义排序&#xff0c;按照数位从小到大进行排序。 举一…

无人驾驶是自动化还是智能化?

这是一个由小米Su-7和人形机器人问题引起的思考&#xff1a;努力决定了下限&#xff0c;认知决定了上限。 一、无人驾驶既涉及自动化&#xff0c;也涉及智能化&#xff0c;这两者在无人驾驶系统中应该是相互融合、相辅相成的1、自动化&#xff08;Automation&#xff09; 自动化…

实操(不可重入函数、volatile、SIGCHLD、线程)Linux

1 不可重入函数 为什么会导致节点丢失内存泄露&#xff1f;main函数在执行insert&#xff0c;但是没执行完就被信号中断了&#xff0c;又进了这个函数里&#xff0c;所以这个insert函数在不同的执行流中&#xff0c;同一个函数被重复进入&#xff0c;如果没有问题&#xff0c;…

【Flask开发】嘿马文学web完整flask项目第2篇:2.用户认证,Json Web Token(JWT)【附代码文档】

教程总体简介&#xff1a;2. 目标 1.1产品与开发 1.2环境配置 1.3 运行方式 1.4目录说明 1.5数据库设计 2.用户认证 Json Web Token(JWT) 3.书架 4.1分类列表 5.搜索 5.3搜索-精准&高匹配&推荐 6.小说 6.4推荐-同类热门推荐 7.浏览记录 8.1配置-阅读偏好 8.配置 9.1项目…

Ubuntu 下搭建 MCU 开发环境全流程指南(以 STM32 为例)

在嵌入式开发中,许多工程师都习惯于在 Windows 平台使用 Keil、IAR 等 IDE。然而,随着对自动化、可定制性以及开放工具链的需求增长,越来越多的开发者开始尝试在 Linux 环境下进行 MCU 开发。 本篇文章将以 STM32F1 系列 为例,手把手带你在 Ubuntu 下搭建一个完整的 MCU 开…

Python----计算机视觉处理(Opencv:道路检测之车道线拟合)

完整版&#xff1a; Python----计算机视觉处理&#xff08;Opencv:道路检测完整版&#xff1a;透视变换&#xff0c;提取车道线&#xff0c;车道线拟合&#xff0c;车道线显示&#xff09; 一、获取左右车道线的原始位置 导入模块 import cv2 import numpy as np from matplot…

如何理解神经网络中的“分段线性单元”,优雅解析前向和反向传播

什么是非线性 非线性本质上指的是一个系统或函数中输入与输出之间的关系不呈现简单的比例关系&#xff0c;也就是说&#xff0c;输出不只是输入的线性组合 ( 比如 y k 1 x 1 k 2 x 2 b ) (比如yk1x1k2x2b) (比如yk1x1k2x2b)。下面详细解释这个概念&#xff1a; 缺乏叠加性…

WVP-GB28181摄像头管理平台存在弱口令

免责声明&#xff1a;本号提供的网络安全信息仅供参考&#xff0c;不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权&#xff0c;请及时与我联系&#xff0c;我将尽快处理并删除相关内容。 漏洞描述 攻击者可利用漏洞获取当前系统管…

开源身份和访问管理方案之keycloak(三)keycloak健康检查(k8s)

文章目录 开源身份和访问管理方案之keycloak&#xff08;三&#xff09;keycloak健康检查启用运行状况检查 健康检查使用Kubernetes下健康检查Dockerfile 中 HEALTHCHECK 指令 健康检查Docker HEALTHCHECK 和 Kubernetes 探针 开源身份和访问管理方案之keycloak&#xff08;三&…

Android学习总结之service篇

引言 在 Android 开发里&#xff0c;Service 与 IntentService 是非常关键的组件&#xff0c;它们能够让应用在后台开展长时间运行的操作。不过&#xff0c;很多开发者仅仅停留在使用这两个组件的层面&#xff0c;对其内部的源码实现了解甚少。本文将深入剖析 Service 和 Inte…

spring mvc异步请求 sse 大文件下载 断点续传下载Range

学习连接 异步Servlet3.0 Spring Boot 处理异步请求&#xff08;DeferredResult 基础案例、DeferredResult 超时案例、DeferredResult 扩展案例、DeferredResult 方法汇总&#xff09; spring.io mvc Asynchronous Requests 官网文档 spring.io webflux&webclient官网文…