当前位置: 首页 > news >正文

Mysql--基础篇--事务(ACID特征及实现原理,事务管理模式,隔离级别,并发问题,锁机制,行级锁,表级锁,意向锁,共享锁,排他锁,死锁,MVCC)

在MySQL中,事务(Transaction)是一组SQL语句的集合,这些语句一起被视为一个单一的工作单元。事务具有ACID特性,确保数据的一致性和完整性。通过事务,可以保证多个操作要么全部成功执行,要么全部不执行,从而避免部分操作成功而另一部分失败的情况。

一、ACID特性介绍

  • 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成。如果事务中的任何一个操作失败,整个事务将被回滚,恢复到事务开始之前的状态。
  • 一致性(Consistency):事务必须确保数据库从一个一致状态转换到另一个一致状态。事务执行前后,数据库的完整性约束不能被破坏。
  • 隔离性(Isolation):并发执行的多个事务之间是相互隔离的,一个事务的中间状态不会影响其他事务。事务的隔离性通过不同的隔离级别来实现。
  • 持久性(Durability):一旦事务提交,其对数据库的更改将永久保存,即使系统发生故障也不会丢失。

1、原子性(Atomicity)

原子性确保事务中的所有操作要么全部成功执行,要么全部不执行。如果事务中的任何一个操作失败,整个事务将被回滚,恢复到事务开始之前的状态。原子性保证了事务是一个不可分割的工作单元。

示例:
假设我们有一个银行转账系统,用户A向用户B转账100元。这个操作涉及两个步骤:
1.从用户A的账户中扣除100元。
2.将100元添加到用户B的账户中。
如果这两个步骤中的任何一个失败,整个转账操作应该被取消,以避免资金丢失或重复转账。

sql:

-- 开始事务
START TRANSACTION;
-- 步骤1:从用户A的账户中扣除100UPDATE accounts SET balance = balance - 100 WHERE user_id = 'A';
-- 步骤2:将100元添加到用户B的账户中
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'B';
-- 提交事务
COMMIT;

如果发生错误:
假设在执行UPDATE accounts SET balance = balance + 100 WHERE user_id = ‘B’;时,系统突然崩溃或出现其他错误。此时,MySQL会自动回滚整个事务,撤销对用户A和用户B账户的所有更改,确保数据的一致性。
回滚事务:

ROLLBACK;

结果:
用户A的账户余额不会减少,用户B的账户余额也不会增加,系统恢复到事务开始之前的状态。

2、一致性(Consistency)

一致性确保事务执行前后,数据库始终处于一致状态。事务必须遵守数据库的完整性约束,如外键约束、唯一约束、检查约束等。事务完成后,数据库的完整性约束不能被破坏。

示例:
假设我们有一个订单系统,订单表orders中有一个外键customer_id,引用客户表customers中的id列。我们希望确保每个订单都必须关联到一个有效的客户。
sql:

-- 创建客户表
CREATE TABLE customers (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(50)
);-- 创建订单表,包含外键约束
CREATE TABLE orders (order_id INT AUTO_INCREMENT PRIMARY KEY,customer_id INT,order_date DATE,FOREIGN KEY (customer_id) REFERENCES customers(id)
);

插入无效的订单数据:
插入的订单数据,该订单的customer_id不存在于customers表中。

INSERT INTO orders (customer_id, order_date) VALUES (999, '2024-01-01')

结果:
MySQL会抛出错误,阻止插入操作,因为customer_id = 999不存在于customers表中。这确保了数据库的一致性,防止了无效数据的插入。
Error Code: 1452. Cannot add or update a child row: a foreign key constraint fails (database_name.orders, CONSTRAINT orders_ibfk_1 FOREIGN KEY (customer_id) REFERENCES customers (id))

插入有效的订单数据:
只有当customer_id存在于customers表中时,插入操作才会成功。

INSERT INTO customers (id, name) VALUES (1, 'Alice');
INSERT INTO orders (customer_id, order_date) VALUES (1, '2024-01-01');

结果:
订单成功插入,数据库保持一致状态。

3、隔离性(Isolation)

隔离性确保多个并发事务之间是相互隔离的,一个事务的中间状态不会影响其他事务。事务的隔离性通过不同的隔离级别来实现。

常见的隔离级别包括:

  • 读未提交(Read Uncommitted)
  • 读已提交(Read Committed)
  • 可重复读(Repeatable Read)
  • 可序列化(Serializable)

示例:
假设我们有两个事务T1和T2,它们同时对同一行数据进行操作。我们将通过不同的隔离级别来展示隔离性的作用。

示例1:读未提交(Read Uncommitted)
在读未提交隔离级别下,事务可以读取其他事务尚未提交的数据,即“脏读”。
sql:

-- 设置隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;-- T1 开始
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'A';  -- 未提交-- T2 开始
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到未提交的数据

结果说明:
T2会读取到了T1尚未提交的数据,导致脏读。如果T1最终回滚,T2读取到的数据实际就是无效的。

示例2:读已提交(Read Committed)
在读已提交隔离级别下,事务只能读取已经提交的数据,避免了脏读。
sql:

-- 设置隔离级别为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;-- T1 开始
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'A';  -- 未提交-- T2 开始
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到旧版本数据-- T1 提交
COMMIT;-- T2 再次读取
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到新版本数据

结果说明:
T2在T1提交之前读取的是旧版本数据,避免了脏读。
T1提交后,T2再次查询才能看到更新后的数据。

示例3:可重复读(Repeatable Read)
在可重复读隔离级别下,事务在整个生命周期内看到的是事务开始时的一致性视图,避免了脏读和不可重复读。
sql:

-- 设置隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;-- T1 开始
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到旧版本数据-- T2 开始
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'A';
COMMIT;-- T1 再次读取
SELECT balance FROM accounts WHERE user_id = 'A';  -- 仍然读取到旧版本数据

结果说明:
T1在整个事务期间看到的始终是事务开始时的快照,即使T2已经提交了更新,T1也不会看到这些更改。这确保了可重复读。

示例4:可序列化(Serializable)
在可序列化隔离级别下,事务按顺序执行,完全隔离并发操作,避免了所有并发问题(如脏读、不可重复读和幻读)。
简单理解:并发的事务会同步执行,后提交的事务必须等待前面的事务提交或回滚后才可以执行。
sql:

-- 设置隔离级别为可序列化
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;-- T1 开始
START TRANSACTION;
SELECT * FROM orders WHERE customer_id = 1;  -- 读取订单-- T2 开始
START TRANSACTION;
INSERT INTO orders (customer_id, order_date) VALUES (1, '2024-01-02');  -- 插入新订单-- T1 再次读取
SELECT * FROM orders WHERE customer_id = 1;  -- 仍然读取到旧版本数据

结果说明:
T2的插入操作会被阻塞,直到T1提交或回滚。T1在整个事务期间看到的始终是事务开始时的快照,避免了幻读。

4、持久性(Durability)

持久性确保一旦事务提交,其对数据库的更改将永久保存,即使系统发生故障也不会丢失。持久性通常通过将事务的日志写入磁盘来实现,确保即使在系统崩溃后,事务的更改也可以恢复。

示例:
假设我们执行了一个事务,向银行账户中存入100元:
sql:

-- 开始事务
START TRANSACTION;-- 向用户 A 的账户中存入 100UPDATE accounts SET balance = balance + 100 WHERE user_id = 'A';-- 提交事务
COMMIT;

解释:

  • 持久性保证:当事务提交时,MySQL会将事务的日志写入磁盘,并确保数据已经安全存储。即使系统在此时发生故障(如断电或崩溃),事务的更改仍然可以通过日志恢复,确保数据不会丢失。
    为了进一步提高持久性,MySQL使用了WAL(Write-Ahead Logging,预写式日志)技术。在事务提交之前,MySQL会先将更改写入日志文件,然后再将数据写入实际的数据库文件。这样,即使系统在事务提交后立即崩溃,日志文件也可以用于恢复事务的更改。

二、事务管理模式

1、显式提交事务

MySQL事务的基本操作包括:

  • 开始事务:使用START TRANSACTION或BEGIN语句显式开始一个事务。
  • 提交事务:使用COMMIT语句提交事务,使所有操作生效并永久保存。
  • 回滚事务:使用ROLLBACK语句回滚事务,撤销所有未提交的操作,恢复到事务开始之前的状态。

提交事务示例:

-- 开始事务
START TRANSACTION;-- 执行多个 SQL 操作
INSERT INTO employees (name, age) VALUES ('Alice', 30);
UPDATE employees SET age = 31 WHERE name = 'Alice';-- 提交事务
COMMIT;

回滚事务示例:
如果在事务执行过程中发生错误,可以使用ROLLBACK来撤销所有操作。

-- 开始事务
START TRANSACTION;-- 执行多个 SQL 操作
INSERT INTO employees (name, age) VALUES ('Bob', 25);
UPDATE employees SET age = -1 WHERE name = 'Bob';  -- 错误操作-- 回滚事务
ROLLBACK;

2、隐式提交事务(Autocommit)

MySQL默认启用了自动提交模式,这意味着每个SQL语句都会被视为一个独立的事务,并且在执行后立即提交。在这种模式下,不需要显式使用START TRANSACTION、COMMIT或ROLLBACK。
自动提交事务标识autocommit为1时,表示启用自动提交事务。为0表示禁用自动提交模式。默认开启,值为1。

查看事务的自动提交状态:

SELECT @@autocommit;

运行结果:
在这里插入图片描述
禁用自动提交模式:

SET autocommit = 0;

在这里插入图片描述
启用自动提交模式:

SET autocommit = 1;

在这里插入图片描述
注意事项:

  • 当autocommit设置为0时,所有的SQL语句都会被包含在一个事务中,直到你显式调用COMMIT或ROLLBACK。
  • 如果你希望在禁用自动提交模式的情况下执行单个SQL语句并立即提交,可以使用START TRANSACTION WITH CONSISTENT SNAPSHOT; 或SET autocommit = 1;。

三、并发控制

1、事务的隔离级别

事务的隔离级别确定了多个并发事务之间的可见性和交互方式。MySQL支持四种标准的隔离级别,每种级别提供了不同程度的隔离性,以平衡性能和一致性。

(1)、读未提交(Read Uncommitted)

允许一个事务读取另一个事务尚未提交的数据(即“脏读”)。这是最低的隔离级别,可能会导致读取到不一致的数据。

特点:

  • 可能会出现脏读、不可重复读和幻读。
  • 性能最好,但一致性最差。

适用场景:
很少使用,除非对数据一致性要求非常低。

sql:

-- 设置隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;-- T1 开始
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'A';  -- 未提交-- T2 开始
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到未提交的数据

结果说明:
T2会读取到了T1尚未提交的数据,导致脏读。如果T1最终回滚,T2读取到的数据实际就是无效的。

(2)、读已提交(Read Committed)

一个事务只能读取另一个事务已经提交的数据。它防止了脏读,但仍然可能出现不可重复读和幻读。

特点:

  • 防止脏读。
  • 可能会出现不可重复读和幻读。

适用场景:
适用于大多数应用程序,尤其是那些对数据一致性要求较高但对并发性能有一定要求的场景。

sql:

-- 设置隔离级别为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;-- T1 开始
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'A';  -- 未提交-- T2 开始
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到旧版本数据-- T1 提交
COMMIT;-- T2 再次读取
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到新版本数据

结果说明:
T2在T1提交之前读取的是旧版本数据,避免了脏读。
T1提交后,T2再次查询才能看到更新后的数据。

(3)、可重复读(Repeatable Read)

在同一事务中,多次读取同一行数据的结果是一致的。它防止了脏读和不可重复读,但仍然可能出现幻读。

特点:

  • 防止脏读和不可重复读。
  • 可能会出现幻读。

适用场景:
这是MySQL的默认隔离级别,适用于大多数应用场景,尤其是在需要保证读取一致性的情况下。

sql:

-- 设置隔离级别为可重复读
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;-- T1 开始
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 'A';  -- 读取到旧版本数据-- T2 开始
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'A';
COMMIT;-- T1 再次读取
SELECT balance FROM accounts WHERE user_id = 'A';  -- 仍然读取到旧版本数据

结果说明:
T1在整个事务期间看到的始终是事务开始时的快照,即使T2已经提交了更新,T1也不会看到这些更改。这确保了可重复读。

(4)、可序列化(Serializable)

最高的隔离级别,完全隔离并发事务。它通过锁定机制确保事务按顺序执行,防止任何并发问题(如脏读、不可重复读和幻读)。

特点:

  • 防止脏读、不可重复读和幻读。
  • 性能较差,因为并发度较低。

适用场景:
适用于对数据一致性要求极高的场景,但在高并发环境下可能会影响性能。

sql:

-- 设置隔离级别为可序列化
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;-- T1 开始
START TRANSACTION;
SELECT * FROM orders WHERE customer_id = 1;  -- 读取订单-- T2 开始
START TRANSACTION;
INSERT INTO orders (customer_id, order_date) VALUES (1, '2024-01-02');  -- 插入新订单-- T1 再次读取
SELECT * FROM orders WHERE customer_id = 1;  -- 仍然读取到旧版本数据

结果说明:
T2的插入操作会被阻塞,直到T1提交或回滚。T1在整个事务期间看到的始终是事务开始时的快照,避免了幻读。

2、设置事务隔离级别

查看当前隔离级别:

SELECT @@transaction_isolation;

设置当前会话的事务隔离级别:

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

设置全局隔离级别:

SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

3、事务的并发问题

在并发环境下,多个事务同时访问和修改相同的数据时,可能会出现以下问题:

(1)、脏读(Dirty Read)

一个事务读取了另一个事务尚未提交的数据。如果该事务后来回滚,读取到的数据将是无效的。

(2)、不可重复读(Non-repeatable Read)

在同一个事务中,两次读取同一行数据的结果不同。这通常是由于另一个事务在这两次读取之间修改了该行并提交了更改。

(3)、幻读(Phantom Read)

在同一个事务中,两次查询返回的行数不同。这通常是由于另一个事务在这两次查询之间插入或删除了某些行。

4、解决并发问题的方法

  • 提高隔离级别:通过提高事务的隔离级别(如从READ COMMITTED提升到REPEATABLE READ或SERIALIZABLE),可以减少并发问题的发生。
  • 使用锁:MySQL提供了多种锁机制(如行级锁、表级锁等),可以在必要时手动加锁以确保数据的一致性。
  • 优化查询和索引:通过优化查询和索引,减少事务的执行时间,降低并发冲突的可能性。

5、事务的锁机制

MySQL的锁机制是确保数据一致性和并发控制的核心技术。通过锁,多个事务可以安全地同时访问和修改数据库中的数据,而不会导致数据不一致或冲突。不同的存储引擎(如 InnoDB和MyISAM)支持不同类型的锁机制。
MySQL使用锁机制来确保事务的隔离性和一致性。根据锁的作用范围和粒度,MySQL支持以下几种锁类型:

(1)、行级锁(Row-Level Locking)

作用范围:锁定单个行记录。
优点:允许高并发,多个事务可以同时访问不同的行。
缺点:管理开销较大,适用于更新频繁但并发度较高的场景。
适用存储引擎:InnoDB支持行级锁。

示例:行级锁
假设我们有一个employees表,包含员工的ID、姓名和年龄。我们希望在两个事务中分别更新不同员工的年龄,而不相互阻塞。
sql:

-- 事务 T1:更新员工A的年龄
START TRANSACTION;SELECT * FROM employees WHERE id = 1 FOR UPDATE;  -- 获取行级锁
UPDATE employees SET age = 30 WHERE id = 1;COMMIT;-- 事务 T2:更新员工B的年龄
START TRANSACTION;SELECT * FROM employees WHERE id = 2 FOR UPDATE;  -- 获取行级锁
UPDATE employees SET age = 28 WHERE id = 2;COMMIT;

结果说明:
T1和T2可以同时执行,因为它们锁定的是不同的行。行级锁确保了每个事务只能修改自己锁定的行,而不会影响其他事务对其他行的操作。

扩展一下行级锁:
1、行级排他锁FOR UPDATE

FOR UPDATE是MySQL中用于显式获取行级排他锁的一种方式。当你在查询中使用FOR UPDATE时,MySQL会为查询结果中的每一行加上排他锁(X锁),确保其他事务在这段时间内不能对该行进行读取或写操作。

具体作用:

  • FOR UPDATE获取行级排他锁:其他事务不能对该行加任何类型的锁(包括共享锁和排他锁),直到当前事务提交或回滚。
  • 锁的持续时间:行级锁会在事务结束时自动释放,即当事务提交(COMMIT)或回滚(ROLLBACK)后,锁才会被释放。
  • 查询结束后不会自动释放锁:即使查询已经执行完毕,行级锁仍然会保持,直到事务结束。

示例:FOR UPDATE行级排他锁阻塞行为
假设我们有一个employees表,包含员工的ID、姓名和年龄。我们希望在两个事务中分别更新不同员工的年龄,并确保在这段时间内没有其他事务可以修改这些员工的数据。

-- 事务T1:更新员工A的年龄
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 FOR UPDATE;  -- 获取行级排他锁
UPDATE employees SET age = 30 WHERE id = 1;
COMMIT;-- 事务T2:尝试更新员工A的年龄
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 FOR UPDATE;  -- 被阻塞,直到T1释放锁
UPDATE employees SET age = 35 WHERE id = 1;
COMMIT;

结果说明:
T2在T1持有排他锁期间,无法获取该行的任何类型的锁(包括共享锁和排他锁),因此T2的查询会被阻塞,直到T1提交或回滚事务并释放锁。

FOR UPDATE的工作原理:
(1)、获取排他锁:当你执行SELECT … FOR UPDATE时,MySQL会为查询结果中的每一行加上排他锁(X锁)。这意味着其他事务在这段时间内不能对该行进行读取或写操作。
(2)、锁的持续时间:行级锁会在事务结束时自动释放。也就是说,锁会一直保持,直到你显式地执行COMMIT或ROLLBACK。即使查询已经执行完毕,锁也不会自动释放。
(3)、查询结束后不会自动释放锁:这是非常重要的一个概念。很多开发者误以为查询结束后锁就会自动释放,但实际上,锁会一直保持,直到事务结束。因此,如果你在事务中执行了FOR UPDATE,其他事务将无法对该行进行任何操作,直到你提交或回滚事务。
(4)、避免长时间持有锁:由于FOR UPDATE会锁定行,建议尽量缩短事务的持续时间,以减少对其他事务的影响。长时间持有锁可能会导致其他事务等待,甚至引发死锁。

FOR UPDATE的应用场景:

  • 防止并发修改:当你需要确保某个行在一段时间内不被其他事务修改时,可以使用FOR UPDATE来获取排他锁。例如,在银行转账系统中,确保同一笔资金不会被多个事务同时修改。
  • 防止幻读:虽然FOR UPDATE主要用于防止并发修改,但它也可以防止其他事务插入新的行,从而避免幻读问题。幻读是指在同一事务中,两次查询返回的行数不同,通常是由于其他事务插入了新行。
  • 批量更新:当你需要批量更新某些行时,可以使用FOR UPDATE来确保这些行在这段时间内不会被其他事务修改。
2、行级共享锁SHARE MODE

除了FOR UPDATE,MySQL还提供了另一种锁机制LOCK IN SHARE MODE,它用于获取行级共享锁(S锁)。

示例:共享锁LOCK IN SHARE MODE

-- 事务 T1:获取共享锁
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 LOCK IN SHARE MODE;  -- 获取共享锁
-- 读取员工信息
SELECT name, age FROM employees WHERE id = 1;
COMMIT;-- 事务 T2:尝试获取共享锁
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 LOCK IN SHARE MODE;  -- 可以获取共享锁
-- 读取员工信息
SELECT name, age FROM employees WHERE id = 1;
COMMIT;-- 事务 T3:尝试获取排他锁
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 FOR UPDATE;  -- 被阻塞,直到所有共享锁被释放
UPDATE employees SET age = 35 WHERE id = 1;
COMMIT;

结果说明:
T1和T2可以同时持有共享锁,读取同一行数据。然而,T3在T1和T2持有共享锁期间,无法获取排他锁,因此T3的查询会被阻塞,直到T1和T2提交或回滚事务并释放共享锁。

3、两种行级锁区别

在这里插入图片描述

  • FOR UPDATE:获取排他锁,阻止其他事务对该行进行读取或写操作。
  • LOCK IN SHARE MODE:获取共享锁,允许多个事务同时读取该行,但阻止其他事务对该行进行写操作。
4、行级锁总结
  • FOR UPDATE:获取行级排他锁,防止其他事务对该行进行读取或写操作。锁会一直保持,直到事务提交或回滚,而不是在查询结束后自动释放。
  • 锁的持续时间:从FOR UPDATE执行开始,直到事务结束。
  • 查询结束后不会自动释放锁:锁会一直保持,直到事务结束。
  • 避免长时间持有锁:尽量缩短事务的持续时间,以减少对其他事务的影响。
(2)、表级锁(Table-Level Locking)

在MySQL中,表级锁(Table-Level Locking)是一种锁定整个表的机制,确保在事务执行期间没有其他事务可以对该表进行读取或写入操作。表级锁适用于需要对整个表进行独占访问的场景,例如批量插入、删除或更新数据。

MySQL的不同存储引擎对表级锁的支持有所不同:

  • MyISAM:默认使用表级锁。
  • InnoDB:支持行级锁,但在某些情况下也可以获取表级锁(如LOCK TABLES和ALTER TABLE操作)。

作用范围:锁定整个表
优点:实现简单,适合批量操作。
缺点:并发度较低,锁定整个表会阻塞其他事务对该表的访问。
适用存储引擎:MyISAM支持表级锁。

示例:表级锁
假设我们有一个orders表,包含订单信息。我们希望在事务中插入多条订单记录,并确保在这段时间内没有其他事务可以修改该表。
sql:

-- 事务T1:插入订单并获取表级锁
START TRANSACTION;
LOCK TABLE orders WRITE;  -- 获取写锁,阻止其他事务读写该表
INSERT INTO orders (order_id, customer_id, order_date) VALUES (1, 1001, '2024-01-01');
INSERT INTO orders (order_id, customer_id, order_date) VALUES (2, 1002, '2024-01-02');
UNLOCK TABLES;  -- 释放表级锁
COMMIT;

结果说明:
在T1持有表级锁期间,其他事务无法对该表进行任何读写操作,直到T1释放锁。这确保了T1的操作是独占的,避免了并发冲突。

扩展一下表级锁:
1、显式获取表级锁

使用LOCK TABLES和UNLOCK TABLES显示设置和释放表级别锁。

LOCK TABLES命令用于显式地获取表级锁,而UNLOCK TABLES用于释放这些锁。你可以为一个或多个表指定不同的锁类型,具体包括:

  • 读锁(Read Lock):允许其他事务读取该表,但不允许写入。
  • 写锁(Write Lock):禁止其他事务对该表进行任何读取或写入操作,确保当前事务对该表有独占访问权。
(1)、获取表级读锁

假设我们有一个employees表,包含员工的ID、姓名和年龄。我们希望在事务中读取所有员工的信息,但不希望其他事务在此期间修改该表的数据。

sql:

-- 获取读锁
LOCK TABLES employees READ;-- 读取员工信息
SELECT * FROM employees;-- 释放锁
UNLOCK TABLES;

结果说明:
在事务持有读锁期间,其他事务可以继续读取employees表中的数据,但不能对该表进行任何写操作(如插入、更新或删除)。当执行完成UNLOCK TABLES,锁将被释放,其他事务才可以恢复对该表的写操作。

注意:

  • 读锁允许多个事务同时持有,因此多个事务可以并发读取同一张表。但是会阻止其他事务对该表进行写操作。
(2)、获取表级写锁

假设我们有一个orders表,包含订单信息。我们希望在事务中插入多条订单记录,并确保在这段时间内没有其他事务可以修改该表。

sql:

-- 获取写锁
LOCK TABLES orders WRITE;
-- 插入多条订单记录
INSERT INTO orders (order_id, customer_id, order_date) VALUES (1, 1001, '2024-01-01');
INSERT INTO orders (order_id, customer_id, order_date) VALUES (2, 1002, '2024-01-02');
-- 释放锁
UNLOCK TABLES;

结果说明:
在持有写锁期间,其他事务无法对该表进行任何读/写操作,直到当前事务释放锁。这确保了当前事务对该表有独占访问权,避免了并发冲突。

注意:
写锁是排他性的,只能由一个事务持有。写锁会阻止其他事务对该表进行任何读取或写入操作,即使是读取也不允许。

(3)、同时获取多个表的锁

你可以在同一个LOCK TABLES语句中为多个表指定不同的锁类型。例如,假设我们有两个表employees和departments,我们希望在事务中读取employees表并更新departments表。

sql:

-- 获取employees表的读锁和departments表的写锁
LOCK TABLES employees READ, departments WRITE;-- 读取员工信息
SELECT * FROM employees;-- 更新部门信息
UPDATE departments SET location = 'New York' WHERE department_id = 1;-- 释放锁
UNLOCK TABLES;

结果说明:
在持有锁期间,其他事务可以继续读取employees表,但不能对其进行写操作;同时,其他事务无法访问departments表,直到当前事务释放锁。

注意:

  • 多个表的锁 可以在同一个LOCK TABLES语句中指定,但必须确保锁的顺序不会导致死锁。
  • 锁的兼容性:读锁和写锁之间是不兼容的,确保在设计事务时避免潜在的锁冲突。
2、隐式获取表级锁

除了显式使用LOCK TABLES和UNLOCK TABLES来获取表级锁,MySQL在某些操作中会隐式地获取表级锁。以下是一些常见的隐式获取表级锁的操作:

(1)、ALTER TABLE操作

当你对表结构进行修改时,MySQL会隐式地获取表级锁,以确保在修改过程中没有其他事务对该表进行读取或写入操作。例如,添加或删除列、修改索引等操作都会触发表级锁。

sql:

-- 添加新列
ALTER TABLE employees ADD COLUMN email VARCHAR(100);
-- 删除列
ALTER TABLE employees DROP COLUMN email;

结果说明:
在ALTER TABLE操作执行期间,MySQL会隐式地获取表级锁,阻止其他事务对该表进行任何读取或写入操作,直到操作完成。

注意:

  • ALTER TABLE操作通常会锁定整个表,尤其是在MyISAM存储引擎中。对于InnoDB,某些ALTER TABLE操作可能会使用更细粒度的锁,但这取决于具体的MySQL版本和优化设置。
(2)、TRUNCATE TABLE操作

TRUNCATE TABLE用于快速删除表中的所有数据。与DELETE不同,TRUNCATE TABLE会隐式地获取表级锁,并且不会触发触发器或生成回滚日志。

sql:

-- 清空表中的所有数据
TRUNCATE TABLE employees;

结果说明:
在TRUNCATE TABLE操作执行期间,MySQL会隐式地获取表级锁,阻止其他事务对该表进行任何读取或写入操作,直到操作完成。

注意:

  • TRUNCATE TABLE操作通常比DELETE更快,因为它不会逐行删除数据,而是直接删除表的数据文件。然而,它也会隐式地获取表级锁,影响并发性能。
(3)、CREATE INDEX操作

当你为表创建索引时,MySQL会隐式地获取表级锁,以确保在创建索引的过程中没有其他事务对该表进行修改。

sql:

-- 创建索引
CREATE INDEX idx_age ON employees (age);

结果说明:
在CREATE INDEX操作执行期间,MySQL会隐式地获取表级锁,阻止其他事务对该表进行任何写入操作,直到索引创建完成。

注意:

  • CREATE INDEX操作通常会锁定整个表,尤其是在MyISAM存储引擎中。对于InnoDB,某些版本的MySQL支持在线创建索引(即在创建索引的同时允许其他事务继续访问表),但这取决于具体的MySQL版本和配置。
3、表级锁的注意事项

虽然表级锁可以确保数据的一致性和独占访问,但它也会影响并发性能。以下是使用表级锁时需要注意的几点:

(1)、避免长时间持有锁
  • 原因:长时间持有表级锁会导致其他事务等待,甚至引发死锁。表级锁会阻塞其他事务对该表的访问。因此,应尽量避免在事务中长时间持有锁,减少对其他事务的影响。
  • 建议:将事务中的操作简化,避免在事务中执行耗时的操作。尽量在最短的时间内完成必要的操作,然后尽快释放锁。如果可能,使用行级锁而不是表级锁,以提高并发性能。
(2)、合理选择锁类型
  • 原因:不同的锁类型(读锁和写锁)有不同的影响。读锁允许多个事务并发读取,但阻止写操作;写锁则是排他性的,阻止所有其他事务的访问。
  • 建议:根据实际需求选择合适的锁类型。如果只需要读取数据,使用读锁;如果需要修改数据,使用写锁。
(3)、避免死锁
  • 原因:当两个或多个事务相互等待对方释放锁时,就会发生死锁。虽然MySQL会自动检测并解决死锁,但频繁的死锁会影响系统性能。
  • 建议:尽量按照固定的顺序获取锁,避免循环等待。如果可能发生死锁,捕获死锁错误并在应用程序中重试事务。
4、表级锁总结

MySQL的表级锁机制是确保数据一致性和独占访问的重要工具。通过显式的LOCK TABLES和UNLOCK TABLES语句,你可以手动获取和释放表级锁;而在某些操作(如ALTER TABLE、TRUNCATE TABLE和CREATE INDEX)中,MySQL会隐式地获取表级锁。

  • 显式获取表级锁:使用LOCK TABLES和UNLOCK TABLES显式地获取和释放表级锁,适用于需要独占访问整个表的场景。
  • 隐式获取表级锁:某些DDL操作(如ALTER TABLE、TRUNCATE TABLE和CREATE INDEX)会隐式地获取表级锁,确保在操作期间没有其他事务对该表进行访问。
  • 注意事项:尽量缩短锁的持续时间,避免长时间持有锁,合理选择锁类型,并避免死锁。
(3)、意向锁(Intention Locks)

意向锁(Intention Locks)是MySQL中用于协调行级锁和表级锁之间冲突的一种机制。它本身并不直接锁定数据行或表,而是表示事务打算对表中的某些行加锁。通过意向锁,MySQL 可以更高效地管理锁的兼容性和冲突检测。

1、什么是意向锁?
  • 意向锁是一种元锁(meta-lock),它不直接锁定数据行或表,而是表示事务打算对表中的某些行加锁。
  • 意向锁的作用是告诉其他事务:当前事务打算对表中的某些行加锁,因此其他事务在考虑对该表进行表级操作时需要注意。
  • 意向锁主要用于协调行级锁和表级锁之间的冲突,确保不会发生意外的锁升级或死锁。

简单理解:
意向锁本身并不是实质意义上的锁,而是一种事务之间协调使用锁的机制。当一个事务使用行锁或表锁时,Mysql会自动为该表添加对应的意向锁。这样其他的事务访问该表时,就能得知该表的当前状态,由Mysql决定其他事务是否能操作表。

2、意向锁的类型

MySQL支持两种主要的意向锁:

  • 意向共享锁(Intention Shared Lock, IS):
    • 表示事务打算对表中的某些行加共享锁(S锁)。
    • 它允许其他事务对该表进行读取操作,但阻止其他事务对该表加表级排他锁(X锁)。
  • 意向排他锁(Intention Exclusive Lock, IX):
    • 表示事务打算对表中的某些行加排他锁(X锁)。
    • 它允许其他事务对该表进行读取操作,但阻止其他事务对该表加表级排他锁(X锁)或 表级共享锁(S锁)。
3、意向锁的工作原理

当你在事务中使用SELECT … FOR UPDATE或SELECT … LOCK IN SHARE MODE时,MySQL会自动为该表添加相应的意向锁。

具体来说:

  • 如果你使用SELECT … FOR UPDATE,MySQL会为表添加意向排他锁(IX锁),并为查询到的每一行加行级排他锁(X锁)。
  • 如果你使用SELECT … LOCK IN SHARE MODE,MySQL会为表添加意向共享锁(IS锁),并为查询到的每一行加行级共享锁(S锁)。

关键点:

  • 意向锁是一种元锁,它不直接锁定数据行或表,而是表示事务打算对表中的某些行加锁。
  • 意向锁的存在是为了协调行级锁和表级锁之间的冲突,确保不会发生意外的锁升级或死锁。
4、意向锁与行级锁的结合示例

sql:

START TRANSACTION;SELECT * FROM employees WHERE id = 1 FOR UPDATE;  -- 获取意向排他锁 + 行级排他锁COMMIT;

解释:

  • 获取行级排他锁:
    当你执行SELECT * FROM employees WHERE id = 1 FOR UPDATE时,MySQL会为查询到的行(即id = 1的行)加行级排他锁(X锁)。这意味着其他事务在这段时间内不能对该行进行读取或写操作。
  • 同时获取意向排他锁:
    当你执行SELECT * FROM employees WHERE id = 1 FOR UPDATE行时,除了获取行级排他锁,MySQL还会为整个employees表加意向排他锁(IX锁)。这表示当前事务打算对表中的某些行加排他锁,其他事务在考虑对该表进行表级操作时需要注意。

具体执行:

  • 意向排他锁(IX锁)并不直接锁定表中的任何行,但它告诉其他事务:当前事务打算对表中的某些行加排他锁。因此,其他事务不能对该表加表级排他锁(X锁)或表级共享锁(S锁)。
  • 行级排他锁(X锁)直接锁定id = 1的行,确保其他事务在这段时间内不能对该行进行任何操作。
5、意向锁的作用
(1)、协调行级锁和表级锁

意向锁确保了行级锁和表级锁之间的兼容性。例如,如果一个事务已经持有意向排他锁(IX锁),其他事务就不能对该表加表级排他锁(X锁)或表级共享锁(S锁),从而避免了潜在的锁冲突。

(2)、防止锁升级

意向锁有助于防止不必要的锁升级。如果没有意向锁,MySQL可能在某些情况下将行级锁 升级为表级锁,这会导致性能下降。通过意向锁,MySQL可以更细粒度地管理锁,避免不必要的锁升级。

(3)、提高并发性能

意向锁允许多个事务同时对同一张表的不同行加锁,而不会相互阻塞。例如,两个事务可以分别对id = 1和id = 2的行加行级排他锁(X锁),而不会相互影响,因为它们都只持有意向排他锁(IX锁),而不是表级排他锁(X锁)。

6、意向锁与表级锁的冲突示例

假设我们有两个事务T1和T2:

-- 事务T1:获取意向排他锁 + 行级排他锁
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 FOR UPDATE;  -- 获取意向排他锁(IX锁)+ 行级排他锁(X锁)
COMMIT;-- 事务T2:尝试获取表级排他锁
START TRANSACTION;
LOCK TABLE employees WRITE;  -- 被阻塞,直到T1释放意向排他锁(IX锁)
UNLOCK TABLES;
COMMIT;

结果说明:
T2在T1 持有意向排他锁(IX锁)期间,无法获取表级排他锁(X锁),因此T2的LOCK TABLE 操作会被阻塞,直到T1提交或回滚事务并释放 意向排他锁(IX锁)。

7、意向锁总结
  • 意向锁是一种元锁,它不直接锁定数据行或表,而是表示事务打算对表中的某些行加锁。
  • 意向锁的作用是协调行级锁和表级锁之间的冲突,确保不会发生意外的锁升级或死锁。
  • 意向排他锁(IX锁)表示事务打算对表中的某些行加排他锁(X锁),而意向共享锁(IS锁)表示事务打算对表中的某些行加共享锁(S锁)。
  • 意向锁的存在使得MySQL可以更高效地管理锁的兼容性和冲突检测,避免不必要的锁升级和死锁。
(4)、共享锁(Shared Locks, S锁)

允许多个事务同时读取同一行数据,但不允许其他事务对该行进行写操作。

获取方式:

 SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE;

示例:共享锁
假设我们有两个事务T1和T2,它们都想要读取同一个员工的信息,但不希望其他事务在此期间修改该员工的数据。
sql:

-- 事务T1:获取共享锁
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 LOCK IN SHARE MODE;  -- 获取共享锁
-- 读取员工信息
SELECT name, age FROM employees WHERE id = 1;
COMMIT;-- 事务T2:获取共享锁
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 LOCK IN SHARE MODE;  -- 获取共享锁
-- 读取员工信息
SELECT name, age FROM employees WHERE id = 1;
COMMIT;

结果说明:
T1和T2可以同时持有共享锁,读取同一行数据。然而,如果有第三个事务尝试对该行加排他锁(例如进行更新操作),它将被阻塞,直到所有共享锁被释放。

(5)、排他锁(Exclusive Locks, X锁)

阻止其他事务对该行进行读取或写操作,确保只有当前事务可以修改该行。

获取方式:

 SELECT * FROM table_name WHERE id = 1 FOR UPDATE;

示例:排他锁
假设我们有两个事务T1和T2,T1想要更新某个员工的年龄,而T2想要读取该员工的信息。
sql:

-- 事务T1:获取排他锁并更新员工年龄
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 FOR UPDATE;  -- 获取排他锁
UPDATE employees SET age = 30 WHERE id = 1;
COMMIT;-- 事务T2:尝试获取共享锁
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 LOCK IN SHARE MODE;  -- 被阻塞,直到 T1 释放排他锁
COMMIT;

结果说明:
T2在T1持有排他锁期间,无法获取共享锁或排他锁,因此T2的查询会被阻塞,直到T1提交或回滚事务并释放锁。

(6)、锁的冲突规则
  • S锁与S锁:相容,多个事务可以同时持有同一行的共享锁。
  • S锁与X锁:不相容,持有共享锁的事务会阻止其他事务获取排他锁。
  • X锁与X锁:不相容,只有一个事务可以持有某一行的排他锁。

简单记忆:
排他锁X和谁都不兼容。

(7)、事务的死锁处理

当两个或多个事务相互等待对方释放锁时,就会发生死锁。MySQL会自动检测死锁,并选择其中一个事务进行回滚,以解除死锁。

死锁检测:
MySQL使用死锁检测算法来识别死锁。当检测到死锁时,MySQL会选择一个代价最小的事务进行回滚。

死锁回滚:
被选中的事务将被回滚,释放其持有的所有锁,从而使其他事务可以继续执行。

死锁预防:
虽然无法完全避免死锁,但可以通过以下方法减少死锁的发生:

  • 尽量缩短事务的持续时间。
  • 尽量减少事务中的锁数量。
  • 按照固定的顺序获取锁,避免循环等待。

处理死锁的建议:

  • 捕获死锁错误:在应用程序中捕获死锁错误(通常为错误码1213),并在捕获后重试事务。
  • 优化事务逻辑:尽量减少事务的复杂度,避免长时间持有锁。
  • 使用适当的隔离级别:选择合适的隔离级别,既能保证数据一致性,又能提高并发性能。
(8)、锁的调试与监控

为了更好地理解和优化表级锁的使用,MySQL提供了一些工具和命令来监控和调试锁的状态。

1、SHOW OPEN TABLES

这个命令可以显示当前打开的表及其锁状态。你可以通过查询information_schema数据库中的TABLES表来查看哪些表被锁定。

sql:

SHOW OPEN TABLES WHERE In_use > 0;

示例:
先获取employees表的读锁。

LOCK TABLES employees READ;

在这里插入图片描述
查看锁定的表:

SHOW OPEN TABLES WHERE In_use > 0;

在这里插入图片描述
释放表锁:

UNLOCK TABLES;

在这里插入图片描述
再次查询锁定的表:

SHOW OPEN TABLES WHERE In_use > 0;

在这里插入图片描述
解释:
该命令会返回当前被锁定的表及其锁状态。In_use列表示有多少线程正在使用该表。

2、performance_schema

MySQL的performance_schema提供了对锁的详细监控功能。你可以通过查询 performance_schema中的相关表来获取表级锁的使用情况。

相关的内置表:

  • data_locks:显示当前持有的锁。
  • data_lock_waits:显示当前的锁等待情况。
  • innodb_lock_waits:显示InnoDB的锁等待情况。

示例:查询当前的表级锁等待情况

SELECT * FROM performance_schema.data_lock_waits;

结果说明:
该查询会返回当前正在等待表级锁的事务及其相关信息,帮助你分析锁的竞争情况。

3、SHOW ENGINE INNODB STATUS

这个命令可以显示InnoDB存储引擎的当前状态,包括锁的等待队列、死锁检测等信息。

sql:

SHOW ENGINE INNODB STATUS;

输出内容:
该命令会输出详细的InnoDB状态信息,包括锁的等待队列、最近发生的死锁、事务的状态等。
运行结果:
在这里插入图片描述

四、事务的存储引擎支持

并不是所有的MySQL存储引擎都支持事务。常见的存储引擎及其事务支持情况如下:

  • InnoDB:支持事务,提供完整的ACID特性。它是MySQL的默认存储引擎,广泛用于需要事务支持的场景。
  • MyISAM:不支持事务,仅支持表级锁。适用于读多写少的场景,如日志记录、报表生成等。
  • MEMORY:不支持事务,数据存储在内存中,适用于临时数据的快速查询。
  • NDB Cluster:支持事务,适用于分布式数据库环境。

五、事务的性能优化

虽然事务提供了强大的数据一致性和完整性保证,但不当使用事务可能会对性能产生负面影响。以下是一些优化事务性能的建议:

  • 尽量缩短事务的持续时间:事务越长,锁定的资源越多,导致其他事务等待的时间越长。尽量将事务中的操作简化,减少不必要的查询和更新。
  • 批量处理数据:如果需要对大量数据进行插入、更新或删除操作,尽量使用批量操作(如 INSERT … VALUES (…)或UPDATE … WHERE IN (…)),而不是逐条执行。
  • 合理使用索引:确保查询和更新操作使用了适当的索引,以减少扫描的行数,提高查询效率。
  • 避免长时间持有锁:尽量减少事务中的锁数量,避免长时间持有锁,以提高并发性能。
  • 选择合适的隔离级别:根据应用的需求选择合适的隔离级别,既能保证数据一致性,又能提高并发性能。

六、多版本并发控制 (MVCC)

MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种用于数据库管理系统的技术,旨在提高并发性能的同时保持事务的隔离性。通过MVCC,多个事务可以同时读取和写入数据,而不会相互阻塞,从而减少了锁的竞争,提高了系统的吞吐量。

在MySQL的InnoDB存储引擎中,MVCC是实现可重复读(Repeatable Read)和读已提交(Read Committed)隔离级别的核心技术。它允许不同事务看到不同的数据版本,确保事务之间的隔离性,同时避免了长时间持有锁带来的性能问题。

1、数据行的多个版本

在MVCC中,每个数据行可以有多个版本。当一个事务对某一行进行更新或删除时,不会立即修改该行的现有版本,而是创建一个新的版本。旧版本的数据仍然保留,直到没有事务需要访问它为止。

具体实现如:

  • 插入操作:为表中添加新行时,直接插入新版本。
  • 更新操作:更新某一行时,不会覆盖原有版本,而是创建一个新版本,并将旧版本标记为历史版本。
  • 删除操作:删除某一行时,不会立即物理删除该行,而是将其标记为“已删除”,并创建一个新版本表示删除状态。

2、隐藏列:事务ID和回滚指针

为了支持多版本并发控制,InnoDB在每行数据中添加了两个隐藏列:

  • DB_TRX_ID:记录最后一次对该行进行插入或更新的事务ID。每次事务对行进行修改时,都会更新这个字段。
  • DB_ROLL_PTR:指向回滚段(rollback segment),存储了该行的历史版本信息。通过回滚指针,可以追溯到该行的旧版本。

这些隐藏列使得InnoDB能够跟踪每个数据行的不同版本,并根据事务的隔离级别决定哪个版本是可见的。

3、MVCC工作模式

快照读 (Snapshot Read)
也称为一致性读(Consistent Read),在可重复读隔离级别下,事务在整个生命周期内看到的是事务开始时的一致性视图。即使其他事务在此期间修改了数据,当前事务也不会看到这些更改。
快照读会在事务开始时生成一个一致的快照,事务在读取数据时只会看到该快照中的数据版本,而不会受到其他事务的影响。
在可重复读和读已提交的隔离级别下,事务执行的快照读操作不会加锁。

当前读(Current Read)
某些操作(如SELECT … FOR UPDATE、SELECT … LOCK IN SHARE MODE、INSERT、UPDATE、DELETE)会执行当前读,即读取最新的数据版本,并加锁以防止其他事务修改同一行。

4、垃圾回收(Purge)

随着事务的不断执行,数据库中会积累大量的旧版本数据。为了释放空间并保持数据库的高效运行,InnoDB会定期执行垃圾回收操作,删除不再需要的历史版本。

垃圾回收的条件是:
当所有活跃事务都不再需要某个旧版本时,该版本可以被安全地删除。

5、MVCC的工作原理

假设我们有两个事务T1和T2,它们同时对同一行数据进行操作。

(1)、可重复读隔离级别下的 MVCC
1T1开始:- T1执行START TRANSACTION;,事务ID100- T1执行SELECT * FROM table WHERE id = 1;,读取到行id = 1, value = 'old',此时该行的DB_TRX_ID99(表示该行是由事务99插入的)。2T2开始:- T2执行START TRANSACTION;,事务ID101- T2执行UPDATE table SET value = 'new' WHERE id = 1;,创建了一个新的版本id = 1, value = 'new',并将DB_TRX_ID设置为1013T1再次读取:- T1再次执行SELECT * FROM table WHERE id = 1;,仍然读取到id = 1, value = 'old',因为T1的快照是在事务开始时生成的,它看不到T2创建的新版本,读取数据任然是DB_TRX_ID99的行。4T2提交:- T2执行COMMIT;,新版本id = 1, value = 'new'成为最新版本。5T1结束:- T1执行COMMIT;,事务结束。从现在起,之后所有新的事务都将看到id = 1, value = 'new'
(2)、读已提交隔离级别下的 MVCC
1T1开始:- T1执行START TRANSACTION;,事务ID100- T1执行SELECT * FROM table WHERE id = 1;,读取到行id = 1, value = 'old',此时该行的DB_TRX_ID992T2开始:- T2执行START TRANSACTION;,事务ID101- T2执行UPDATE table SET value = 'new' WHERE id = 1;,创建了一个新的版本id = 1, value = 'new',并将DB_TRX_ID设置为1013T1再次读取:- T1再次执行SELECT * FROM table WHERE id = 1;,仍然读取到 id = 1, value = 'old',因为在读已提交隔离级别下,T1只能看到已经提交的事务所做的更改,而T2尚未提交。4T2提交:- T2执行COMMIT;,新版本id = 1, value = 'new'成为最新版本。5T1再次读取:- T1再次执行SELECT * FROM table WHERE id = 1;,这次读取到id = 1, value = 'new',因为T2已经提交,T1现在可以看到T2的更改(即当前读模式)。6T1结束:- T1执行COMMIT;,事务结束。

6、MVCC的优点

  • 提高并发性能:通过允许多个事务同时读取和写入数据,减少了锁的竞争,提高了系统的吞吐量。
  • 减少锁冲突:快照读操作不需要加锁,避免了读写冲突,使得多个事务可以并行执行。
  • 保证数据一致性:事务在读取数据时,始终看到的是事务开始时的一致性视图,确保了数据的隔离性和一致性。
  • 简化应用程序开发:开发者不需要频繁使用显式锁来保护数据,减少了复杂性。

7、MVCC的局限性

  • 额外的存储开销:由于每个数据行可能有多个版本,数据库需要额外的空间来存储历史版本。随着事务的增多,可能会导致磁盘空间的增加。
  • 垃圾回收的开销:虽然垃圾回收机制可以清理不再需要的历史版本,但频繁的垃圾回收操作可能会对性能产生一定的影响。
  • 幻读问题:在可重复读隔离级别下,MVCC无法完全解决幻读问题。幻读是指在同一事务中,两次查询返回的行数不同,通常是由于其他事务插入或删除了数据。

8、MVCC与不同隔离级别的关系

  • 读未提交(Read Uncommitted):不使用MVCC,允许脏读,事务可以看到未提交的数据。
  • 读已提交(Read Committed):使用MVCC,事务只能看到已经提交的数据,避免了脏读。每次读取时,事务都会获取最新的提交版本。
  • 可重复读(Repeatable Read):使用MVCC,事务在整个生命周期内看到的是事务开始时的一致性视图,避免了脏读和不可重复读。然而,幻读仍然可能发生。
  • 可序列化(Serializable):不依赖MVCC,通过严格的锁定机制确保事务按顺序执行,避免了所有并发问题,但并发性能较低。

9、MVCC总结

MVCC(多版本并发控制)是MySQL InnoDB存储引擎中实现高并发和事务隔离的核心技术。通过为每个数据行维护多个版本,并结合快照读和当前读机制,MVCC允许多个事务同时读取和写入数据,而不会相互阻塞。这不仅提高了系统的并发性能,还确保了数据的一致性和隔离性。

七、ACID的实现原理

事务的ACID特性是数据库系统中确保数据一致性和可靠性的核心原则。每个特性都对应了数据库在处理事务时必须满足的特定要求。下面介绍下MySQL(特别是InnoDB存储引擎)是如何实现这些特性的。

1、原子性~实现原理

原子性确保一个事务中的所有操作要么全部成功,要么全部失败。如果事务中的任何一个操作失败,整个事务将被回滚,恢复到事务开始之前的状态。

实现方式:
日志记录(Redo Log,Undo Log) + 二阶段提交。
(1)、日志记录(Redo Log和Undo Log)
InnoDB使用重做日志(Redo Log)和撤销日志(Undo Log)来实现原子性。

  • Redo Log:用于记录事务对数据页的修改。即使在事务提交之前,数据页的修改已经被写入内存或磁盘,但只有当Redo Log被持久化后,事务才算真正提交。如果系统崩溃,可以通过Redo Log恢复未完成的事务。
  • Undo Log:用于记录事务的修改前的旧值。如果事务失败或被回滚,可以通过Undo Log恢复到事务开始之前的状态。
    (2)、两阶段提交(Two-Phase Commit, 2PC)
    InnoDB在提交事务时使用两阶段提交协议,确保事务的原子性。具体步骤如下:
    1、准备阶段:事务的所有修改都被写入Redo Log和Undo Log,但尚未提交。此时,事务处于“准备”状态。
    2、提交阶段:如果所有操作都成功,事务会被标记为已提交,Redo Log被持久化,事务正式生效。如果任何操作失败,事务会被回滚,使用Undo Log恢复到事务开始之前的状态。

示例:

START TRANSACTION;
-- 修改数据
UPDATE employees SET salary = 5000 WHERE id = 1;
-- 如果这里发生错误,事务将被回滚
INSERT INTO departments (department_id, name) VALUES (1, 'HR');
COMMIT;

说明:
如果INSERT操作失败,整个事务将被回滚,salary的修改也会被撤销。

2、一致性~实现原理

一致性确保事务执行前后,数据库的状态始终满足所有的约束条件和规则(如外键约束、唯一性约束等)。事务不会破坏数据库的完整性。

实现方式:
约束检查:在事务执行过程中,MySQL会自动检查并强制执行各种约束条件,如主键、外键、唯一性、非空性等。如果事务违反了这些约束,事务将被回滚,确保数据库的一致性。
MVCC(多版本并发控制):InnoDB使用多版本并发控制(MVCC)来实现一致性。MVCC 允许多个事务同时读取和写入数据,而不会相互干扰。通过为每个事务提供数据的不同版本,确保事务看到的数据是一致的。

示例:

-- 插入一条违反外键约束的数据
INSERT INTO employees (id, department_id, name) VALUES (1, 999, 'John Doe');

说明:
如果department_id = 999不存在于departments表中,插入操作将失败,事务将被回滚,确保数据库的一致性。

3、隔离性~实现原理

隔离性确保多个并发事务之间的操作相互隔离,避免相互干扰。不同事务之间不能看到彼此未提交的修改,除非它们显式地依赖于其他事务的结果。

实现方式:
(1)、隔离级别
MySQL提供了四种不同的隔离级别,每个级别决定了事务之间可见性的程度。隔离级别越高,事务之间的隔离性越强,但也可能会影响并发性能。

  • 读未提交(Read Uncommitted):事务可以看到其他事务未提交的修改。这是最低的隔离级别,容易引发脏读、不可重复读和幻读问题。
  • 读已提交(Read Committed):事务只能看到其他事务已经提交的修改。这可以防止脏读,但仍然可能出现不可重复读和幻读。
  • 可重复读(Repeatable Read):事务在整个事务期间看到的数据是一致的,即事务开始后,其他事务的修改对当前事务不可见。这是MySQL的默认隔离级别,可以防止脏读和不可重复读,但仍然可能出现幻读。
  • 可序列化(Serializable):这是最高的隔离级别,事务完全隔离,不允许任何并发操作。它通过加锁的方式确保事务的顺序执行,防止所有类型的并发问题(脏读、不可重复读和幻读)。

(2)、MVCC(多版本并发控制)
InnoDB使用MVCC来实现隔离性。MVCC通过为每个事务提供数据的不同版本,确保事务在读取数据时不会受到其他事务的影响。具体来说:

  • 快照读(Snapshot Read):事务读取的是事务开始时的数据快照,而不是最新的数据。这意味着事务可以看到的数据是它开始时的状态,而不受其他事务的修改影响。
  • 当前读(Current Read):某些操作(如SELECT … FOR UPDATE或SELECT … LOCK IN SHARE MODE)会获取最新的数据,并加锁以确保其他事务不能修改这些数据。

示例:

-- 事务T1:读取员工信息
START TRANSACTION;
SELECT * FROM employees WHERE id = 1;  -- 读取的是事务开始时的数据快照-- 事务T2:更新员工信息
START TRANSACTION;
UPDATE employees SET salary = 6000 WHERE id = 1;
COMMIT;-- 事务T1:再次读取员工信息
SELECT * FROM employees WHERE id = 1;  -- 仍然读取的是事务开始时的数据快照,看不到 T2的修改

结果说明:
因为Mysql默认的事务隔离级别为可重复读。在这种隔离级别下,T1在整个事务期间看到的数据是一致的,不会受到T2的修改影响。这是因为T1使用了MVCC数据快照。

4、持久性~实现原理

持久性确保一旦事务提交成功,其修改将永久保存在数据库中,即使系统发生故障也不会丢失。

实现方式:
(1)、Redo Log(重做日志):InnoDB使用Redo Log来确保事务的持久性。当事务提交时,Redo Log会被持久化到磁盘。即使系统崩溃,MySQL可以通过Redo Log恢复未完成的事务,确保数据不会丢失。

(2)、双写缓冲区(Doublewrite Buffer):为了防止部分页面写入失败导致数据损坏,InnoDB使用双写缓冲区。在将数据页写入磁盘之前,先将数据页的副本写入双写缓冲区。如果系统崩溃,MySQL可以通过双写缓冲区恢复损坏的数据页。

(3)、同步机制:MySQL提供了多种同步机制,确保Redo Log和数据页能够及时持久化到磁盘。例如,innodb_flush_log_at_trx_commit参数可以控制Redo Log的同步频率:

  • = 0:每秒同步一次Redo Log,性能较好,但可能存在最多1秒的数据丢失。
  • = 1(默认):每次事务提交时同步Redo Log,确保数据的持久性,但性能稍差。
  • = 2:每次事务提交时将Redo Log写入操作系统缓存,但不立即同步到磁盘,性能较好,但可能存在数据丢失的风险。

sql:

START TRANSACTION;
-- 修改数据
UPDATE employees SET salary = 7000 WHERE id = 1;
COMMIT;

解释:
一旦事务提交成功,Redo Log会被持久化到磁盘,确保即使系统崩溃,数据也不会丢失。

5、ACID实现原理总结

  • 原子性(Atomicity):通过Redo Log和Undo Log实现,确保事务中的所有操作要么全部成功,要么全部失败。
  • 一致性(Consistency):通过约束检查和MVCC实现,确保事务执行前后数据库的状态始终满足所有的约束条件。
  • 隔离性(Isolation):通过隔离级别和MVCC实现,确保多个并发事务之间的操作相互隔离,避免相互干扰。
  • 持久性(Durability):通过Redo Log和双写缓冲区实现,确保事务提交后的修改永久保存在数据库中,即使系统发生故障也不会丢失。

乘风破浪会有时,直挂云帆济沧海!!!

相关文章:

Mysql--基础篇--事务(ACID特征及实现原理,事务管理模式,隔离级别,并发问题,锁机制,行级锁,表级锁,意向锁,共享锁,排他锁,死锁,MVCC)

在MySQL中,事务(Transaction)是一组SQL语句的集合,这些语句一起被视为一个单一的工作单元。事务具有ACID特性,确保数据的一致性和完整性。通过事务,可以保证多个操作要么全部成功执行,要么全部不…...

Android系统定制APP开发_如何对应用进行系统签名

前言 当项目开发需要使用系统级别权限或frame层某些api时,普通应用是无法使用的,需要在AndroidManifest中配置sharedUserId: AndroidManifest.xml中的android:sharedUserId“android.uid.system”,代表的意思是和系统相同的uid&a…...

Vue篇-06

1、路由简介 vue-rooter:是vue的一个插件库,专门用来实现SPA应用 1.1、对SPA应用的理解 1、单页 Web 应用(single page web application,SPA)。 2、整个应用只有一个完整的页面 index.html。 3、点击页面中的导航链…...

学习threejs,导入wrl格式的模型

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.VRMLLoader wrl模型加…...

winform第三方界面开源库AntdUI的使用教程保姆级环境设置篇

1. AntdUI 1.1. 导入项目 1.1.1. 首先新建一个空白的基于.net的Winfrom项目1.1.2. 复制AntdUI中src目录到我们的解决方案下面1.1.3. 解决方案下添加现有项目1.1.4. 添加项目引用 1.2. 编写代码 1.2.1. 改写Form1类,让其继承自public partial class Form1 : AntdUI.W…...

likeshop同城跑腿系统likeshop回收租赁系统likeshop多商户商城安装及小程序对接方法

前言:首先likeshop是一个开发平台,是一个独创的平台就像TP内核平台一样,你可以在这个平台上开发和衍生出很多伟大的产品,以likeshop为例,他们开发出商城系统,团购系统,外卖点餐系统,…...

java -jar启动项目报错:XXX.jar中没有主清单属性

XXX.jar中没有主清单属性 1、错误复现2、错误原因3、解决方案 java -jar启动项目报错:XXX.jar中没有主清单属性 1、错误复现 今天使用springboot给项目打了jar包,使用命令启动时报错,截图如下: 2、错误原因 项目的pom文件配置如…...

浅谈云计算03 | 云计算的技术支撑(云使能技术)

云计算的技术支撑 一、定义与内涵1.1 定义与内涵 二、云计算使能技术架构2.1 宽带网络和 Internet 架构2.2 数据中心技术2.3 虚拟化技术2.4 Web 技术2.5 多租户技术2.6 服务技术 一、定义与内涵 1.1 定义与内涵 云计算技术包含一些基础的关键技术,这里称为使能技术…...

使用LinkedList手撕图的邻接表

主要是学习图的邻接表的核心结构等等&#xff0c;话不多说直接上代码&#xff1a; import java.util.LinkedList;public class GrapAdj {private int n ; // 表示图中顶点的数量。private LinkedList<Integer>[] adj;public GrapAdj(int n){this.n n;adj new LinkedLis…...

学习RocketMQ

1.为什么要用MQ&#xff1f; 消息队列是一种“先进先出”的数据结构 其应用场景主要包含以下4个方面&#xff1a; 1.1 异步解耦​ 最常见的一个场景是用户注册后&#xff0c;需要发送注册邮件和短信通知&#xff0c;以告知用户注册成功。传统的做法有以下两种&#xff1a; …...

sap mm学习笔记

1. 业务流程 2. 组织架构 3. 物料主数据 4.采购主数据 5. 采购管理 6. 库存管理 7.物料主数据 8. 采购申请 ME51N...

Dockerfile 构建java镜像并运行部署java项目

目录 1. 前提准备 2. 什么是Dockerfile 3. 步骤 3.1. 创建目录 3.2. 编写dockerfile文件 3.3. 构建镜像 3.4. 运行容器 3.5. 验证 4. 总结 1. 前提准备 (1) 准备一个jar打包文件,已为大家准备(我这里的包名为 app.jar) 资源链接&#xff0c;点我下载 (2) 已经下载…...

【C语言系列】函数递归

函数递归 一、递归是什么&#xff1f;1.1尾递归 二、递归的限制条件三、递归举例3.1举例一&#xff1a;求n的阶乘3.2举例二&#xff1a;顺序打印一个整数的每一位 四、递归与迭代4.1举例三&#xff1a;求第n个斐波那契数 五、拓展学习青蛙跳台问题 一、递归是什么&#xff1f; …...

windows10 安装 Golang 版本控制工具g与使用

下载包&#xff1a;https://github.com/voidint/g/releases 解压&#xff0c; 并添加到环境变量 g 常用命令 查询当前可供安装的stable状态及所有的 go 版本 # stable 版本 g ls-remote stable# 所有版本 g ls-remote安装目标 go 版本1.23.4g install 1.23.4切换到已安装的…...

WordPress开发进群V2主题,多种引流方法,引私域二次变现

介绍&#xff1a; 全新前端UI界面&#xff0c;多种前端交互特效让页面不再单调&#xff0c;进群页面群成员数&#xff0c;群成员头像名称&#xff0c;每次刷新页面随机更新不重复&#xff0c;最下面评论和点赞也是如此随机刷新不重复 进群页面简介&#xff0c;群聊名称&#…...

在 CentOS/Red Hat Linux 中安装 Docker

在 Red Hat Linux 中安装 Docker 在 Red Hat Linux (RHEL) 中安装 Docker 需要一些准备工作&#xff0c;尤其是针对不同版本的系统&#xff08;如 RHEL 7、8、9&#xff09;。以下是具体的安装步骤&#xff1a; 步骤 1&#xff1a;检查系统版本 在安装前&#xff0c;确认系统…...

【DAPM杂谈之二】实践是检验真理的标准

本文主要分析DAPM的设计与实现 内核的版本是&#xff1a;linux-5.15.164&#xff0c;下载链接&#xff1a;Linux内核下载 主要讲解有关于DAPM相关的知识&#xff0c;会给出一些例程并分析内核如何去实现的 /**************************************************************…...

关于使用FastGPT 摸索的QA

近期在通过fastGPT&#xff0c;创建一些基于特定业务场景的、相对复杂的Agent智能体应用。 工作流在AI模型的基础上&#xff0c;可以定义业务逻辑&#xff0c;满足输出对话之外的需求。 在最近3个月来的摸索和实践中&#xff0c;一些基于经验的小问题点&#xff08;自己也常常…...

虚拟文件系统 VFS

目录 虚拟文件系统 VFS 文件系统挂载过程 虚拟文件系统 VFS 统一标准的系统调用接口&#xff1a; VFS定义了一组标准的文件操作API&#xff0c;如open(), read(), write(), close()等&#xff0c;使得用户空间的应用程序无需关心底层文件系统的具体类型。 下层文件系统必须实现…...

React Fiber框架中的Render渲染阶段——workLoop(performUnitOfWork【beginWork与completeWork】)

触发渲染过程——renderRoot renderRoot 是一个函数&#xff0c;用于触发渲染工作。它通常会调用并递归地执行一系列的渲染任务&#xff0c;直到完成整个更新过程。这个过程包括执行 Fiber 树中的 beginWork 和 completeWork&#xff0c;以及渲染新状态或 DOM。 function ren…...

Xcode 正则表达式实现查找替换

在软件开发过程中&#xff0c;查找和替换文本是一项常见的任务。正则表达式&#xff08;Regular Expressions&#xff09;是一种强大的工具&#xff0c;可以帮助我们在复杂的文本中进行精确的匹配和替换。Xcode 作为一款流行的开发工具&#xff0c;提供了对正则表达式的支持。本…...

【opencv】第8章 图像轮廓与图像分割修复

8.1 查找并绘制轮廓 一个轮廓一般对应一系列的点&#xff0c;也就是图像中的一条曲线。其表示方法可能 根据不同的情况而有所不同。在OpenCV 中&#xff0c;可以用findContours()函数从二值图 像中查找轮廓 8.1.1 寻找轮廓&#xff1a; findContours() 函数 findContours) 函…...

excel VBA 基础教程

这里写目录标题 快捷键选择所有有内容的地方 调试VBA录制宏&#xff0c;打开VBA开发工具录制宏,相当于excel自动写代码&#xff08;两个表格内容完全一致才可以&#xff09; 查看宏代码保持含有宏程序的文件xlsm后缀&#xff08;注意很容易有病毒&#xff09;宏文件安全设置 使…...

2008-2019年各省城镇人口数据

2008-2019年各省城镇人口数据 1、时间&#xff1a;2008-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、城镇人口 4、范围&#xff1a;31省 5、指标解释&#xff1a;城镇人口是指居住在城镇范围内的全部常住人口。 6、下…...

【机器学习】在不确定的光影中:机器学习与概率论的心灵共舞

文章目录 概率与统计基础&#xff1a;解锁机器学习的数据洞察之门前言一、概率论基础1.1 概率的基本概念与性质1.1.1 概率的定义1.1.2 样本空间与事件1.1.3 互斥事件与独立事件1.1.4 概率的计算方法 1.2 条件概率与独立性1.2.1 条件概率1.2.2 独立事件 1.3 随机变量1.3.1 随机变…...

vscode使用Marscode编程助手

下载 vscode 在插件里下载Marscode编程助手 插件完成 在这里点击安装&#xff0c;点击后这里出现AI编程插件。...

谷歌开放语音命令数据集,助力初学者踏入音频识别领域

在人工智能的浪潮中&#xff0c;语音识别技术正逐渐成为我们日常生活的一部分。从智能助手到语音控制设备&#xff0c;语音识别的应用场景越来越广泛。然而&#xff0c;对于初学者来说&#xff0c;进入这一领域往往面临诸多挑战&#xff0c;尤其是缺乏合适的开源数据集和简单的…...

Diffchecker图像比较工具介绍

Diffchecker图像比较工具介绍 网站地址&#xff1a; Diffchecker图像比较 主要功能&#xff1a; 图像差异比较&#xff1a; 该工具允许用户上传两张图片&#xff0c;系统会自动识别并高亮显示这两张图片之间的差异。简单易用&#xff1a; 用户只需将图片拖放到指定区域或点击浏…...

后端开发 Springboot整合Redis Spring Data Redis 模板

目录 redis 配置 RedisConfig 类 完整代码 代码讲解 1. 类定义和注解 2. 定义 RedisTemplate Bean 3. 配置 JSON 序列化 4. 配置 Redis 的 key 和 value 序列化方式 5. 完成配置并返回 RedisTemplate 总结 redis 服务接口实现类 类级别 注入 RedisTemplate 常用 Re…...

极狐GitLab 正式发布安全版本17.7.1、17.6.3、17.5.5

本分分享极狐GitLab 补丁版本 17.7.1, 17.6.3, 17.5.5 的详细内容。这几个版本包含重要的缺陷和安全修复代码&#xff0c;我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLab SaaS&#xff0c;技术团队已经进行了升级&#xff0c;无需用户采取任何…...

策略模式详解与应用

策略模式&#xff08;Strategy Pattern&#xff09;&#xff0c;是一种行为型设计模式&#xff0c;它定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以互相替换&#xff0c;而应用程序可以在运行时选择使用哪一个算法。策略模式使得算法的变化独立于使…...

Gateway怎么实现限流的

Gateway怎么实现限流的 在API网关&#xff08;如Spring Cloud Gateway、Kong、Nginx等&#xff09;中实现限流是为了控制服务请求的频率&#xff0c;从而避免系统过载&#xff0c;确保稳定性和可用性。限流可以通过多种策略实现&#xff0c;常见的方法包括基于请求次数、时间窗…...

OpenCV实现Kuwahara滤波

Kuwahara滤波是一种非线性的平滑滤波技术&#xff0c;其基本原理在于通过计算图像模板中邻域内的均值和方差&#xff0c;选择图像灰度值较为均匀的区域的均值来替代模板中心像素的灰度值。以下是Kuwahara滤波的详细原理说明&#xff1a; 一、基本思想 Kuwahara滤波的基本思想…...

【DevOps】Jenkins使用Pipeline构建java代码

使用Pipeline发布java项目 文章目录 使用Pipeline发布java项目资源列表基础环境一、准备gitlab1.1、部署gitlab1.2、创建chinanews项目1.3、提交代码1.4、查看上传的代码 二、准备Jenkins2.1、部署Jenkins2.2、安装maven2.3、修改Maven源2.4、准备chinanews 三、Jenkins配置工具…...

【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论和实操考试题

文章目录 选择题理论题 解释RIP协议中的“水平分割”机制&#xff0c;并说明其目的。 可以防止路由器错误地将从邻居学到的路由再发送回给该邻居&#xff0c;从而避免路由环路的发生。实操题 【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论…...

Entity 的材质(棋盘、条纹、网格)

Entity 的材质 普通物体的材质 import { nextTick, onMounted, ref } from vue import * as Cesium from cesium // console.log(Cesium, Cesium)const viewer ref<any>(null)onMounted(() > { ... })let material Cesium.Color.YELLOW.withAlpha(0.5)Cesium.Colo…...

shell脚本编写练习3

1、shell 脚本写出检测 /tmp/size.log 文件如果存在显示它的内容&#xff0c;不存在则创建一个文件将创建时间写入。 #!/bin/bash # 定义文件路径变量 file_path"/tmp/size.log"# 使用if语句检查文件是否存在 if [ -e "$file_path" ] # 检查变量file_path…...

事务的隔离级别和MDL

文章目录 说明不同隔离级别可能发生的现象关键现象解释MDL&#xff08;元数据锁&#xff0c;Metadata Lock&#xff09;MDL 的作用MDL 的工作原理MDL 锁的常见场景如何避免 MDL 阻塞 说明 本文章由大模型对话整理而来&#xff0c;如果有错误之处&#xff0c;请在评论区留言指正…...

用户界面软件05

已知应用 几乎所有的流行的用户界面架构都使用这种模式。我在这里举三个例子&#xff1a; 1. Seeheim 用户界面架构的特点是有一个应用核心的领域层和一个用户界面层。后者 被分为两层&#xff0c;叫做表示层和对话控制层。因为这个架构和面向事务系统有渊源&#xff0c;没有…...

基于Springboot + vue实现的办公用品管理系统

&#x1f942;(❁◡❁)您的点赞&#x1f44d;➕评论&#x1f4dd;➕收藏⭐是作者创作的最大动力&#x1f91e; &#x1f496;&#x1f4d5;&#x1f389;&#x1f525; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 &#x1f525;&#x1f525;&…...

17_Redis管道技术

Redis管道(Pipeline)技术是一种在 Redis 客户端与服务器之间进行高效数据交互的技术。 1.Redis管道技术介绍 1.1 传统请求响应模式 在传统的请求-响应模式下,客户端每发送一个命令后会等待服务器返回结果,然后再发送下一个命令。这种方式在网络延迟较高的情况下会导致性…...

【环境搭建】Metersphere v2.x 容器部署教程踩坑总结

前言 Metersphere部署过程中遇到的问题有点多&#xff0c;原因是其容器的架构蛮复杂的&#xff0c;比较容易踩坑&#xff0c;所以记录一下。 介绍 MeterSphere 是开源持续测试平台&#xff0c;遵循 GPL v3 开源许可协议&#xff0c;涵盖测试管理、接口测试、UI 测试和性能测…...

​Vue虚拟DOM:如何提高前端开发效率

前言 随着前端技术的不断发展&#xff0c;越来越多的框架和库涌现出来&#xff0c;其中Vue.js成为了最受欢迎的前端框架之一。Vue.js采用了响应式数据绑定和组件化的思想&#xff0c;让开发者可以更加高效地构建交互式的用户界面。而Vue.js的底层原理涉及到许多概念和技术&…...

【C】预处理详解

在上一篇文章中&#xff0c;简单讲解了一个C程序是如何从一句句C代码变为一个个二进制指令&#xff0c;并最终变成可执行程序成功运行。在预处理、编译、汇编、链接四个步骤中&#xff0c;预处理阶段做的事情特别多&#xff0c;接下来我们就来讲解一下在预处理阶段处理的一些预…...

CES Asia 2025:VR/AR/XR引领科技新潮流

在全球科技领域蓬勃发展的大背景下&#xff0c;CES Asia 2025&#xff08;赛逸展&#xff09;即将在京盛大开幕&#xff0c;VR/AR/XR技术作为前沿科技的代表&#xff0c;将在本次展会上大放异彩&#xff0c;展现出令人瞩目的发展趋势和巨大潜力&#xff0c;同时政策优势也将为其…...

Lua调用C#

目录 创建C#入口 Lua调用类 Lua调用枚举 Lua调用数组&#xff0c;列表&#xff0c;字典 Lua调用C#拓展方法 Lua调用C#Ref与Out知识 Lua调用C#函数重载 Lua调用C#委托与事件 Lua调用C#二维数组 Lua调用C#中nil与null的差距 Lua调用C#中让系类型与lua能够互相访问 Lua调用…...

EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施

靶场搭建 当我们考虑到攻击他人服务器属于违法行为时&#xff0c;我们需要思考如何更好地保护我们自己的服务器。为了测试和学习&#xff0c;我们可以搭建一个专门的靶场来模拟文件上传漏洞攻击。以下是我搭建靶场的环境和一些参考资料&#xff0c;供大家学习和参考&#xff0…...

springboot使用Easy Excel导出列表数据为Excel

springboot使用Easy Excel导出列表数据为Excel Easy Excel官网&#xff1a;https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write 主要记录一下引入时候的pom&#xff0c;直接引入会依赖冲突 解决方法&#xff1a; <!-- 引入Easy Excel的依赖 -->&l…...

现代 CPU 的高性能架构与并发安全问题

现代 CPU 的设计&#xff08;如多级缓存、指令重排&#xff09;为了提升性能&#xff0c;引入了许多优化机制&#xff0c;但这些机制可能导致并发场景下的安全性问题。并发安全性主要体现在三个方面&#xff1a;原子性、有序性 和 可见性。这些问题在底层通过 CAS&#xff08;C…...

【数模学习笔记】插值算法和拟合算法

声明&#xff1a;以下笔记中的图片以及内容 均整理自“数学建模学习交流”清风老师的课程资料&#xff0c;仅用作学习交流使用 文章目录 插值算法定义三个类型插值举例插值多项式分段插值三角插值 一般插值多项式原理拉格朗日插值法龙格现象分段线性插值 牛顿插值法 Hermite埃尔…...