事务管理是针对数据库的一组操作。由一条或多条SQL语句组成,这些语句在逻辑上具有强烈的相关性,如果其中一条语句无法执行,那么所有的语句都不会执行。
1 事务管理
原子性 | 指一个事务必须被视为一个不可分割的最小单元。只有事务中所有的数据操作都执行成功,才算整个事务都执行成功。 |
一致性 | 事务执行前后,数据库的状态(所有的约束条件、完整性规则和触发器)必须保持一致。 |
隔离性 | 每个事务的执行都应该与其他事务相互隔离,使得每个事务感觉不到其他事务的存在。 |
持久性 | 一旦事务提交,其所做的修改将会永久保存在数据库中。即使系统故障或重启后也不会丢失。 |
表 事务的四个特性
事务有以下的优点:
- 并发控制,事务可以管理并发访问数据库的能力。保证多个并发执行的事务不会相互干扰或产生不一致的结果。确保每个事务的操作在逻辑上相互隔离,避免了数据竞争和冲突。
- 错误恢复,如果事务执行过程发生错误,事务可以被回滚,撤销之前的操作,将数据库状态恢复到事务开始之前的状态。
- 高效性,通过将多个相关操作组合在一个事务中,可以减少与数据库的交互次数,提高效率。
1.1 事务基本用法
START TRANSACTION; -- 开启事务
SAVEPOINT label1; -- 保存回滚节点
ROLLBACK TO label1; -- 回滚到具体的节点
ROLLBACK; -- 回滚整个事务
COMMIT; -- 提交事务
START TRANSACTION;INSERT teacher(id,name) VALUES(4,"刘老师");SAVEPOINT label1;INSERT teacher(id,name) VALUES(5,"张老师");ROLLBACK TO label1;COMMIT; -- 最终,只有第一条信息会被插入id=4
1.2 事务并发引发的问题
1)脏读。一个事务处理过程中读取了另一个未提交的事务中的数据。
当数据库的事务隔离级别为读未提交(RU,READ-UNCOMMITTED)时,会出现这个问题。
-- 事务隔离级别为“读未提交”START TRANSACTION;UPDATE teacher SET `name`=CONCAT(`name`,"(未提交事务)") WHERE id = 4;ROLLBACK -- 先不执行,先在其他事务查询这条数据,发现name="刘老师(未提交事务)",然后再执行回滚
将数据库隔离级别设置为其他的级别(比如读已提交),则不会有这个问题。
2)不可重复读。在一个事务中,多次读取同一个数据时,读取的数据不一致。(数据被更新了update)。
-- 隔离级别为“读已提交”START TRANSACTION;SELECT `name` FROM teacher WHERE id = 4;SELECT SLEEP(30); -- 休眠30s,在这个时间在另一个事务中执行update操作,来跟新id=4的数据SELECT `name` FROM teacher WHERE id = 4;COMMIT;
将隔离级别设置为可重复读或可串行化则不会出现这个问题。
3)幻读。读取某一范围的数据时,在一个事务中多次读,结果不一致。(发生在插入或删除数据时。)
-- 隔离级别为 "读已提交",注意mysql 的“可重复读”不会出现这个问题。START TRANSACTION;SELECT * FROM teacher WHERE name = '刘老师';SELECT SLEEP(20); -- 休眠20s,在这段时间在另一个事务执行insert操作,来插入新刘老师的数据SELECT * FROM teacher WHERE name = '刘老师';COMMIT;
mysql 将隔离级别设置为可重复读或可串行化则不会出现这个问题。
1.3 事务的隔离级别
读未提交 RU READ UNCOMMITTED | 可以读取到事务未提交的数据,隔离性差。 |
读已提交 RC READ COMMITTED | 读取事务已提交的数据,隔离性一般。 |
可重复读 RR REPEATABLE READ | 默认。在一个事务中多次读取同一个数据时,能够保证读取到的数据一致(即使其他事务修改了该数据)。 |
可串行化 SR SERIALIZABLE | 最高隔离级别。保证所有事务之间的执行顺序按照某个顺序执行,避免了所有并发问题。事务并发性最差。 |
表 事务的四种隔离级别
读未提交 | 读已提交 | 可重复读 | 可串行化 | |
脏读 | x | √ | √ | √ |
不可重复读 | x | x | √ | √ |
幻读 | x | x | √ | √ |
表 不同隔离级别下所解决的问题
注意:MySQL 隔离级别为可重复读解决了“幻读”的问题。
SHOW VARIABLES LIKE "transaction_isolation"; -- 查看数据库的隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 设置全局隔离级别为“读未提交”。需要重新连接数据库才会生效。
1.4 当前读和快照读
当前读,读取最新提交的数据。(隔离级别为可串行化)。
快照读(一致性读),读取某一时间点的数据。(隔离级别为读提交或可重复读)。
在读提交隔离级别下,每次SELECT都会建立新的快照。
在可重复读隔离级别下,建立快照的时机为:
- 事务启动后,首次SELECT。
- 事务启动时选择了with consistent snapshot,则在启动时建立快照。
- 基于旧数据的修改操作(或insert及delete操作),会重新建立快照。
-- 隔离级别为 "可重复读"START TRANSACTION;SELECT * FROM teacher WHERE name LIKE '%刘老师%'; -- 建立快照SELECT SLEEP(20); -- 休眠20s,在这段时间在另一个事务执行insert操作,来插入新刘老师的数据INSERT INTO teacher(`name`) VALUES('刘老师'); -- 数据插入或删除会建立新的快照UPDATE teacher SET `name` = '刘老师(旧数据修改)' WHERE id = 2; -- 旧数据修改,会建立新的快照SELECT * FROM teacher WHERE name LIKE '%刘老师%';COMMIT;
注意,上面查询的结果虽然不一致,这是因为在同一个事务中进行修改或插入操作。因此不是幻读。 而如果在休眠的20s中在其他事务中执行了插入操作,依旧不会影响查询结果。因为快照读是基于MVCC(多版本并发控制)实现的。