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 是实现该目标的「技术手段」
204

被折叠的 条评论
为什么被折叠?



