mysql数据库事务隔离级别演示
关键词:
ACID
隔离级别
事务
脏读
幻读
不可重复读
DDL
一、基本概念
事务的概念:事务是逻辑上一组操作,组成这组操作各个逻辑单元,要么一起成功,要么一起失败。
二、事务的四个特性(ACID)
- 原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。
- 一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。指数据的规则,在事务前/后应保持一致。
- 隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰。简单点说,某个事务的操作对其他事务不可见的。
- 持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。当事务提交完成后,其影响应该保留下来,不能撤消。
注意:
- 建表的时候,选择 Innodb引擎才支持事务
- 默认情况下, MySQL 是自动提交事务, 每次执行一个 SQL 语句时, 如果执行成功, 就会向数据库自动提交,而不能回滚。 如果某一组操作需要在一个事务中, 那么需要使用 start transaction(或 begin), 一旦 rollback 或 commit 就结束当次事务, 之后的操作又自动提交。
- 如果需要在当前会话的整个过程中都取消自动提交事务, 进行手动提交事务, 就需要设置 set autocommit = false(或 set autocommit = 0),那样的话每一句 SQL 都需要手动 commit 提交才会真正生效。 rollback 或 commit 之前的所有操作都视为一个事务, 之后的操作视为另一个事务, 还需要手动提交或回滚。
- 和 Oracle 一样, DDL 语句是不能回滚的, 并且部分的 DDL 语句会造成隐式的提交, 因此最好事务中不要涉及DDL 语句。
DDL:数据定义语言(Data Definition Language, DDL) 是SQL语言集中负责数据结构定义与数据库对象定义的语言,由CREATE、ALTER与DROP三个语法所组成
三、事务的用法
3.1 相关命令
命令 | 执行语句 |
---|---|
开启一个事务 | start transaction(begin) |
提交事务 | commit |
回滚事务 | rollback |
查看全局事务隔离级别: | SELECT @@global.tx_isolation; |
设置全局事务隔离级别 | set global transaction isolation level read committed(set global tx_isolation =‘read-committed’) |
查看当前会话事务隔离级别 | SELECT @@tx_isolation(show variables like ‘tx_isolation’) |
设置当前会话事务隔离级别 | set session transaction isolation level read committed(set tx_isolation =‘repeatable-read’) |
查看mysql默认自动提交状态 | select @@autocommit(show variables like ‘autocommit’) |
设置mysql默认自动提交状态 | set autocommit = 0(set autocommit = false;【不自动提交】) |
在事务中创建一个保存点 | save point tx1 |
回滚到保存点 | rollback to tx1 |
3.2 使用步骤
- 开启事务(start transaction)
- 执行 sql 操作(普通 sql 操作)
- 提交/回滚(commit/rollback)
注意:
执行 sql 操作:普通 sql 操作,非DDL操作,DDL语句是不能回滚的, 并且部分的DDL语句会造成隐式的提交, 因此最好事务中不要涉及DDL 语句
四、数据库的隔离级别
对于同时运行的多个事务(多线程并发) , 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制,就会导致各种并发问题: (问题的本质就是线程安全问题, 共享数据的问题)
事务并发引起一些读的问题:
脏读: 一个事务可以读取另一个事务未提交的数据
不可重复读: 一个事务可以读取另一个事务已提交的数据 单条记录前后不匹配
幻读: 一个事务可以读取另一个事务已提交的数据 读取的数据前后多了点或者少了点
并发写:使用mysql默认的锁机制(独占锁)
解决读问题:设置事务隔离级别
隔离级别越高,性能越低,并发副作用越小。
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。 一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
隔离级别 | 脏读 | 不可重复读 | 幻读 | 读数据一致性 |
---|---|---|---|---|
读未提交(read uncommitted) | 是 | 是 | 是 | 最低级别 |
读已提交(read committed) | 否 | 是 | 是 | 语句级 |
可重复读(repeatable read) | 否 | 否 | 是 | 事务级 |
可串行化(serializable) | 否 | 否 | 否 | 最高级别,事务级 |
一般情况下:脏读是不可允许的,不可重复读和幻读是可以被适当允许的。
五、示例演示(每组事务结束手动commit)
隔离级别 | 描述 |
---|---|
读未提交(read uncommitted) | 允许事务读取其他事务未提交的数据, 脏读、 不可重复读、 幻读的问题都会出现 |
读已提交(read committed) | 只允许事务读取其他事务已经提交的数据, 可以避免脏读, 但是不可重复读、 幻读的问题仍然会出现 |
可重复读(repeatable read) | 确保事务可以多次从一个字段中读取相同的值, 好比在事务开启时对现有的数据进行了拍照, 其他事务对数据的修改, 不管事务是否提交, 我这里读取的是拍照下来的数据, 可以避免脏读和不可重复读, 但幻读的问题仍然存在。注意:INNODB 使用了 MVCC (Multiversion Concurrency Control), 即多版本并发控制技术防止幻读。 真正的像拍照一样, 其他事务新插入或删除的记录也看不出来。 |
可串行化(serializable) | 确保事务可以从一个表中读取相同的行, 在这个事务持续期间, 禁止其他事务对该表执行插入、 更新、 删除操作, 所有并发问题都可以避免, 但是性能十分低下。 |
建一张简单的表即可
5.1 设置事务隔离级别为read uncommitted
脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
事务隔离级别为read uncommitted,出现数据的脏读现象(包括不可重复读和幻读)
①开启两个客户端,设置事务隔离级别为read uncommitted
②开启两个事务T1和T2
③执行sql语句,出现脏读现象(如下图)
5.2 设置事务隔离级别为read committed
不可重复读: 对于两个事务 T1, T2, T1 读取了一个字段, 然后 T2 更新并提交了该字段. 之后, T1再次读取同一个字段, 值就不同了.
①设置事务隔离级别为read committed
②开启两个事务T1和T2(如下图)
③执行sql语句,解决了数据脏读现象,不能解决数据的不可重复读和幻读
5.3 设置事务隔离级别repeatable read
幻读: 对于两个事务 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入、 删除了一些新的行。之后,如果 T1 再次读取同一个表, 就会多出、 少了几行
①设置事务隔离级别为repeatable read
②开启两个事务T1和T2
③执行sql语句,解决了数据的脏读和不可重复读的现象,不能解决数据的幻读现象
5.4 设置事务隔离级别为serializable
①设置事务隔离级别为serializable
②开启两个事务T1和T2
③T1执行sql语句,只要T1没有提交,就会禁止T2进行数据库操作
T1不结束事务,T2报错,ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
所有并发问题都可以避免, 但是性能十分低下