MySQL事务

1.关于事务

(1)什么是事务?

事务是一组具有逻辑关联性的 DML 语句集合,其核心特性是 “要么全部执行成功,要么全部执行失败”—— 即使执行过程中出现错误,也会回滚到事务开始前的状态,以此保证操作的完整性。

同时,事务还具备 “数据隔离” 能力:在多客户端并发访问数据库时,不同事务看到的数据可保持独立,避免相互干扰。

(2)事务的使用场景

事务常用于处理操作量大、逻辑复杂度高的数据处理场景。例如:

学校教务系统需删除一名毕业生的信息时,不仅要删除其 “姓名、电话、籍贯” 等基本数据,还需同步删除关联数据(如各科成绩、在校表现记录、论坛发帖记录等)。这些关联操作需打包成一个事务:若所有删除步骤都成功,事务才确认完成;若其中任意一步(如删除成绩时出错),则所有已执行的删除操作会全部回滚,确保数据库中不会残留 “只删了基本信息、却留下成绩” 的不完整数据。

(3)面临的风险

数据库实际运行中,并非只有单个事务在执行 —— 同一时刻可能有大量请求被包装成事务,并发访问同一批数据。若缺乏保护机制,易出现两类问题:

 - 并发冲突:多事务同时读写同一数据,可能导致数据不一致(如两个事务同时修改同一条成绩记录);

 - 执行中断问题:事务包含多条 SQL,若执行到一半时出错(如网络中断、语法错误),已执行的步骤若不回滚,会导致数据库处于 “半完成” 的混乱状态。

(4)ACID

为解决上述问题,一个完整的事务必须满足ACID 四大属性,这是事务可靠性的根本保障:

 - 原子性 Atomicity

事务中的所有操作不可分割,要么全成、要么全败,无中间状态,出错即回滚。

 - 一致性 Consistency

事务执行前后,数据库的 “完整性规则” 始终成立(如数据格式、关联关系无破坏),数据精度与逻辑合理性不丢失。

 - 隔离性 Isolation

多事务并发执行时,相互隔离、互不干扰,避免因 “交叉执行” 导致数据不一致。MySQL 支持 4 个隔离级别:读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable)。

 - 持久性 Durability

事务一旦执行完成(提交),对数据的修改会永久保存到数据库中,即使后续发生系统故障(如断电、崩溃),数据也不会丢失。

上面四个属性,可以简称为 ACID

(5)为什么要出现事务

事务被 MySQL 编写者设计出来,本质是为了当应用程序访问数据库的时候,事务能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题。试想一下当我们使用事务时,要么提交,要么回滚,我们不会去考虑网络异常,服务器宕机,同时更改一个数据怎么办等问题了。因此事务本质上是为了应用层服务的。而不是伴随着数据库系统天生就有的。

我们后面把 MySQL 中的一行信息,称为一行记录。

2.事务的版本支持

MySQL 中并非所有存储引擎都支持事务,仅 InnoDB 引擎支持事务(MyISAM、Memory 等引擎不支持)。因此使用前需确认表的存储引擎:

查看事务的版本支持:

mysql> show engines; -- 表格显示
mysql> show engines \G -- 行显示
*************************** 1. row ***************************
      Engine: InnoDB  -- 引擎名称
     Support: DEFAULT -- 默认引擎
     Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES    -- 支持事务
          XA: YES
  Savepoints: YES    -- 支持事务保存点
*************************** 2. row ***************************
      Engine: MRG_MYISAM
     Support: YES
     Comment: Collection of identical MyISAM tables
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 3. row ***************************
      Engine: MEMORY  -- 内存引擎
     Support: YES
     Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 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     -- MyISAM不支持事务
          XA: NO
  Savepoints: NO
*************************** 6. row ***************************
      Engine: CSV
     Support: YES
     Comment: CSV storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 7. row ***************************
      Engine: ARCHIVE
     Support: YES
     Comment: Archive storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 8. row ***************************
      Engine: PERFORMANCE_SCHEMA
     Support: YES
     Comment: Performance Schema
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 9. row ***************************
      Engine: FEDERATED
     Support: NO
     Comment: Federated MySQL storage engine
Transactions: NULL
          XA: NULL
  Savepoints: NULL
9 rows in set (0.00 sec)

为了便于演示,先将mysql的默认隔离级别设置成读未提交。

mysql> set global transaction isolation level READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)
mysql> quit
Bye

##需要重启终端,再进行查看
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+

3.事务的提交方式

事务的提交方式常见的有两种:自动提交、手动提交

查看事务提交方式

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

SET 来改变 MySQL 的自动提交模式:

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

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

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

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

4.常见的操作方式

创建测试表

create table if not exists account(
    id int primary key,
    name varchar(50) not null default '',
    blance decimal(10,2) not null default 0.0
);

(1)证明事务的开始与回滚

设置成自动提交,看看该选项是否影响begin

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+

开始一个事务也可以使用begin,推荐begin

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> savepoint save1;        -- 创建一个保存点save1
Query OK, 0 rows affected (0.00 sec) 

mysql> insert into account values (1, '张三', 100);
Query OK, 1 row affected (0.00 sec)

mysql> savepoint save2;        -- 创建一个保存点save2
Query OK, 0 rows affected (0.00 sec) 

mysql> insert into account values (2, '李四', 10000);
Query OK, 1 row affected (0.00 sec)
mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   |   100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

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

mysql> select * from account;
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> rollback;            -- 直接rollback,回滚在最开始
Query OK, 0 rows affected (0.01 sec)

mysql> select * from account;
Empty set (0.00 sec)

(2)证明未commit,客户端崩溃,MySQL自动会回滚(隔离级别设置为读未提交)

表内无数据并且事务为自动提交

终端A插入记录

mysql> insert into account values (1, '张三', 100);
Query OK, 1 row affected (0.00 sec)

终端B查看

mysql> select * from account;
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

终端A ctrl + \ 异常终止MySQL

mysql> Aborted

终端B

mysql> select * from account;
Empty set (0.00 sec)

(3)证明commit后,客户端崩溃,MySQL数据不会在受影响,已经持久化

表内无数据并且事务为自动提交

终端A插入、提交、异常终止

mysql> insert into account values (1, '张三', 100); -- 插入记录
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> Aborted

终端B查看,commit的作用是将数据持久化到MySQL中

mysql> select * from account;
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

(4)对比试验。证明begin操作会自动更改提交方式,不会受MySQL是否自动提交影响

终端A

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

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values (2, '李四', 10000);
Query OK, 1 row affected (0.00 sec)

终端B

mysql> select * from account; --终端A崩溃前
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> select * from account; --终端A崩溃后,自动回滚
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+----------+

自动提交模式说明
MySQL 默认情况下处于自动提交(autocommit)模式,即每执行一条 DML(数据操纵语言,如INSERT、UPDATE、DELETE )语句,都会立即提交到数据库,对数据库进行永久性修改。

当执行begin语句时,会自动将当前会话的自动提交模式关闭,开启一个事务。 在事务开启后,后续执行的 DML 语句不会立即提交到数据库,而是被暂存起来,直到执行commit(提交事务,将暂存的操作永久性应用到数据库) 或者rollback(回滚事务,撤销事务内所有操作)语句,事务才会结束。

(5)证明单条 SQL 与事务的关系

实验1:

终端A:

mysql> select * from account;
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> set autocommit=0; -- 关闭自动提交
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values (2, '李四', 10000);
Query OK, 1 row affected (0.00 sec)

mysql> Aborted

终端B:

mysql> select *from account;    --终端A崩溃前
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   |   100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> select *from account;    --终端A崩溃后
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   | 100.00 |
+----+--------+--------+

实验2:

终端A:

mysql> show variables like 'autocommit'; --开启默认提交
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> select * from account;
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> insert into account values (2, '李四', 10000);
Query OK, 1 row affected (0.01 sec)

mysql> Aborted

终端B:

终端A崩溃后,并不影响,已经持久化,autocommit起作用

mysql> select *from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   |   100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> select *from account;    
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   |   100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

结论:

 - 只要输入begin或者start transaction,事务便必须要通过commit提交,才会持久化,与是否设置set autocommit无关。

 - 事务可以手动回滚,同时,当操作异常,MySQL会自动回滚

 - 对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。(select有特殊情况,因为
MySQL 有 MVCC )

事务操作注意事项:

事务未提交时,若未设置保存点,执行ROLLBACK会将事务完整回滚到开启时的状态(即撤销所有已执行的操作);若事务已执行COMMIT(提交),则ROLLBACK不再生效,无法撤销已持久化的变更。

从上面的例子,我们能看到事务本身的原子性(回滚),持久性(commit),那么接下来介绍隔离性、一致性。

5.事务隔离级别

5.1.如何理解隔离性

 - MySQL服务可能会同时被多个客户端进程(线程)访问,访问的方式以事务方式进行

 - 一个事务可能由多条SQL构成,也就意味着,任何一个事务,都有执行前,执行中,执行后的阶

段。而所谓的原子性,其实就是让用户层,要么看到执行前,要么看到执行后。执行中出现问题,

可以随时回滚。所以单个事务,对用户表现出来的特性,就是原子性。

 - 但是,毕竟所有事务都要有个执行过程,那么在多个事务各自执行多个SQL的时候,就还是有可能会出现互相影响的情况。比如:多个事务同时访问同一张表,甚至同一行数据。

 - 数据库中,为了保证事务执行过程中尽量不受干扰,就有了一个重要特征:隔离性

 - 数据库中,允许事务受不同程度的干扰,就有了一种重要特征:隔离级别

5.2.隔离性查看与设置

查看

mysql> select @@global.tx_isolation;    --查看全局隔离级别
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select @@session.tx_isolation;    -- 查看当前会话隔离级别
+------------------------+
| @@session.tx_isolation |
+------------------------+
| SERIALIZABLE           |
+------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select @@tx_isolation;    -- 默认隔离级别,结果与当前会话隔离级别一致
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set, 1 warning (0.00 sec)

设置

SET [SESSION | GLOBAL] transaction isolation LEVEL 
{ read uncommitted | READ committed | repeatable READ | serializable }

设置当前会话隔离性,只影响当前会话

mysql> set session transaction isolation level serializable; -- 串行化
Query OK, 0 rows affected (0.00 sec)

mysql> select @@global.tx_isolation;    -- 全局隔离性还是RR
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| SERIALIZABLE           |
+------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set, 1 warning (0.00 sec)

设置全局隔离性,另起一个会话,会被影响

mysql> set global transaction isolation level READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED      |
+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED       |
+------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)

5.3.隔离级别

 - 读未提交【Read Uncommitted】: 在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际生产中不可能使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等,我们上面为了做实验方便,用的就是这个隔离性。

 - 读提交【Read Committed】:该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默
认的)。它满足了隔离的简单定义:一个事务只能看到已经提交的事务所做的改变。这种隔离级别会引起不可重复读,即一个事务执行时,如果多次 select, 可能得到不同的结果。

 - 可重复读【Repeatable Read: 这是 MySQL 默认的隔离级别,它确保同一个事务,在执行
中,多次读取操作数据时,会看到同样的数据行。但是会有幻读问题。

 - 串行化【Serializable】:这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争(这种隔离级别太极端,实际生产基本不使用)

隔离级别如何实现:隔离,基本都是通过锁实现的,不同的隔离级别,锁的使用是不同的。常见有,表锁,行锁,读锁,写锁,间隙锁(GAP),Next-Key锁(GAP+行锁)等。

5.3.1.读未提交

几乎没有加锁,虽然效率高,但是问题太多,严重不建议采用

终端A:

设置隔离级别为 读未提交

mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

--重启客户端

mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   |   100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> update account set blance=20000 where id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

终端B:

读到终端A更新但是未commit的数据(insert,delete同样)

mysql> begin;
mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 20000.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

一个事务在执行中,读到另一个执行中事务的更新(或其他操作)但是未commit的数据,这种现象叫做脏读。

5.3.2.读提交

终端A:

mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
--重启客户端

mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 20000.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update account set blance=11111 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;        --commit提交!
Query OK, 0 rows affected (0.01 sec)

终端B:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;    --终端A commit之前
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 20000.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> select * from account;    --commit之后
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 11111.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

但是!此时还在当前事务中,并未commit,那么就造成了,同一个事务内,同样的读取,在不同的时间段(依旧还在事务操作中!),读取到了不同的值,这种现象叫做不可重复读(non reapeatable read)!!

5.3.3.可重复读

情景1:

终端A:

mysql> set global transaction isolation level repeatable read; 
--关闭终端重启
mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 11111.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update account set blance=12345 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

终端B:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 11111.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

-- 终端A中事务 commit之后,查看当前表中数据,数据未更新
mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 11111.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

可以看到,在终端B中,事务无论什么时候进行查找,看到的结果都是一致的,这叫做可重复读!

mysql> commit;    -- 结束
Query OK, 0 rows affected (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;    --再次查看,看到最新的更新数据
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 12345.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

如果将上面的终端A中的 update 操作,改成 insert 操作发现终端A在对应事务中 insert 的数据,在终端B的事务周期中,也没有什么影响,也符合可重复的特点。

但是,一般的数据库在可重复读情况的时候,无法屏蔽其它事务insert的数据(为什么?因为隔离性实现是对数据加锁完成的,而 insert 待插入的数据因为并不存在,那么一般加锁无法屏蔽这类问题),会造成虽然大部分内容是可重复读的,但是 insert 的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录,就如同产生了幻觉。这种现象,叫做幻读。(phantom read)。

MySQL在RR级别的时候,是解决了幻读问题的(解决的方式是用Next-Key锁 (GAP+行锁)解决的),如下,并未产生幻读

情景2:

终端A:

mysql> select * from account;
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 12345.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account (id,name,blance) values(3, '王五', 5432.0);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

终端B:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;    --终端Acommit前查看
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 12345.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> select * from account;    --终端Acommit后查看
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 12345.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;   --看到更新
+----+--------+----------+
| id | name   | blance   |
+----+--------+----------+
|  1 | 张三   | 12345.00 |
|  2 | 李四   | 10000.00 |
|  3 | 王五   |  5432.00 |
+----+--------+----------+
3 rows in set (0.00 sec)

5.3.4.串行化

串行化要求事务之间完全串行执行,即多个事务不能同时操作相同的数据,必须一个事务执行完毕后,另一个事务才能开始。

串行化对所有操作全部加锁,能完全避免脏读、不可重复读和幻读等并发问题,但是只要串行化,效率很低,几乎完全不会被采用。

终端A:

两个读取不会串行化

终端B:

终端B提交之后,终端A中的update才会提交。

总结:

 - 其中隔离级别越严格,安全性越高,但数据库的并发性能也就越低,往往需要在两者之间找一个平衡点。

 - 不可重复读的重点是修改和删除:同样的条件,读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增:同样的条件,第1次和第2次读出来的记录数不一样

 - 说明: mysql 默认的隔离级别是可重复读,一般情况下不要修改

 - 上面的例子可以看出,事务也有长短事务这样的概念。事务间互相影响,指的是事务在并行执行的时候,即都没有commit的时候,影响会比较大

一致性(Consistency)

事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务 成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中 断,未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不一 致)的状态。因此一致性是通过原子性来保证的。

其实一致性和用户的业务逻辑强相关,一般MySQL提供技术支持,但是一致性还是要用户业务逻辑做支撑,也就是,一致性,是由用户决定的。

而技术上,通过AID保证C

原子性(A)----》避免部分修改
隔离性(I)----》避免脏读/幻读
持久性(D)----》维持最终一致                                
结论:一致性是事务的「最终目标」,AID 是实现该目标的「技术手段」   

MySQL 事务是指一组数据库操作,这些操作要么全部执行,要么全部不执行,其目的是保证在并发环境下,数据的一致性和完整性。MySQL 事务具有 ACID 性质,即原子性、一致性、隔离性和持久性。 MySQL 中使用事务需要使用 BEGIN、COMMIT 和 ROLLBACK 语句,其中 BEGIN 表示开启一个事务,COMMIT 表示提交事务,ROLLBACK 表示回滚事务事务的基本语法如下: ``` BEGIN; -- 执行一组数据库操作 COMMIT; -- 提交事务 -- 或者 ROLLBACK; -- 回滚事务 ``` 在 MySQL 中,事务的隔离级别分为四个等级,分别是 Read Uncommitted、Read Committed、Repeatable Read 和 Serializable。隔离级别越高,数据的一致性和完整性越高,但同时也会影响数据库的性能。 MySQL 事务的 ACID 性质有以下含义: 1. 原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部失败回滚,不会只执行其中的一部分操作。 2. 一致性(Consistency):事务执行前后,数据库中的数据必须保持一致性状态,即满足数据库的约束条件和完整性规则。 3. 隔离性(Isolation):事务之间应该是相互隔离的,一个事务的执行不应该被其他事务干扰,保证事务之间的数据相互独立。 4. 持久性(Durability):事务提交后,对数据库的修改应该是永久性的,即使出现系统故障或电源故障,也不应该对数据产生影响。 总之,MySQL 事务是一组数据库操作,具有 ACID 性质,可以通过 BEGIN、COMMIT 和 ROLLBACK 语句来实现,隔离级别越高,数据的一致性和完整性越高,但同时也会影响数据库的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值