mysql 事务特性和隔离级别

事务的目的是保证数据库的数据一致性和完整性和隔离性。在关系型数据库中,事务是基于ACID模型的。在实际应用中,我们通常使用事务来执行一组对数据库的操作,以保证数据的正确性和完整性。这样数据就不会被破坏,结果也不会因软件崩溃和硬件故障等异常情况而扭曲。例如,在银行转账操作中,我们需要保证两个账户之间的转账操作是一个事务,要么同时成功,要么同时失败回滚。

事务四大特性

原子性(atomicity)

一个事务被视为不可分割的一个最小单元,要么全部执行成功,要么全部失败回滚。即使在执行过程中出现故障,所有对数据库的修改都应该被撤销,使数据状态回到事务发生前的状态。

一致性(consistency)

数据库总是从一个一致性状态转换到另一个一致性状态。在执行事务之前和之后,数据库应该满足所有的完整性约束。

隔离性(isolation)

多个并发事务的执行互不干扰,每个事务都应该像是在独立运行,保证每个事务都能得到正确的结果。通常情况一个事务所作的变更在事务提交前对其它事务是不可见的。

持久性(durability)

在事务完成提交后,对数据所做的修改应该永久保存在数据库中,即使系统崩溃或重启也不会丢失。

隔离级别

隔离级别是对不同场景下隔离性的控制力度。用于在多个事务同时进行更改和执行查询时微调性能与可靠性、一致性和结果可再现性之间的平衡。

InnoDB提供了以下4种隔离级别READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ和SERIALIZABLE默认的隔离级别是可重复读(REPEATABLE READ)。

隔离级别设置

用户可以根据实际场景来修改隔离级别。

查看当前事务隔离级别

低版本

mysql> show variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |

高版本

SELECT @@GLOBAL.transaction_isolation;
SELECT @@SESSION.transaction_isolation;

修改事务隔离级别

mysql> SET [GLOBAL|SESSION] TRANSACTION 隔离级别;

这里看到了修改隔离级别有两个可选的范围GLOBAL和SESSION。

GLOBAL:全局对所有的语句和后续所有建立的session会话都有有效,但是对此刻前已经存在的其它会话无效。设置全局事务需要有超级用户权限。

SESSION:对当前会话有效

不加范围修饰:如果设置隔离级别时候global和session都不加,则设置的隔离级别只会影响当前session的下一个事务,下一个事务结束后,事务隔离级别会还原为session原来的隔离级别。值得注意的一点是,如果当前在事务种,不可设置事务隔离级别

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
ERROR 1568 (25001): Transaction characteristics can't be changed while a transaction is in progress

四种隔离级别特性

在说明四种隔离级别前先准备一个测试表,用来演示效果:

CREATE TABLE account (
 id int(11) NOT NULL AUTO_INCREMENT,
 balance int(11) DEFAULT NULL,
 PRIMARY KEY (id)
) ENGINE=InnoDB;
insert into account(balance) values(0),(0),(0);

mysql> select * from account;
+----+---------+
| id | balance |
+----+---------+
|  1 |       0 |
|  2 |       0 |
|  3 |       0 |
+----+---------+
3 rows in set (0.00 sec)

读未提交 (READ UNCOMMITTED)

事务中的修改,即使没有提交,对其它事务也是可见的。事务可以读取未提交的数据,被称为脏读(dirty read),这也违反了一致性。

开启两个会话A和B

session1 session2
T1 设置事务隔离级别为读未提交
set session transaction isolation level read uncommitted;
T2 开启事务,更新数据不提交
start transaction;
update account set balance=1 where id=1;
T3 mysql> select balance from account where id=1;
这里可以看到session2未提交的值,balance=1
T4 rollback;
T5 select balance from account where id=1;
查到的数据也是回滚后的数据,balance=0

这里session1能看到session2未提交的数据。这个时候就发生了脏读。一旦会话B修改的数据由于某种原因回滚,所有的修改都会被回退,此时读到的数据就是无效数据。

读已提交(READ COMMITTED)

这个隔离级别能满足隔离性的基本要求,一个事务可以读取到其它事务提交的记录。该隔离级别会存在一个现象是不可重复读。就是在当前事务执行过程中,其它事务对要读取的数据进行了修改,导致两次读取数据不一致。

简单演示下不可重复读现象。

还是A、B两个会话

session1 session2
T1 设置隔离级别为读已提交
set session transaction isolation level read committed;
开启事务查询数据
start transaction;
select balance from account where id=1;
这是查询数据结果为balance=0
T2 开启事务修改数据不提交
start transaction;
update account set balance=1 where id=1;
T3 查询数据
select balance from account where id=1;
这里还是balance=0
T4 commit;
T5 查询数据
select balance from account where id=1;
这里可以读到session2提交的数据,balance=1

这里发现session1对于session2未提交的数据是看不到的,可以看到会话B提交的修改信息,但是这导致了会话A在同一个事务中两次查询结果不一样,导致了不可重复读。

可重复读(REPEATABLE READ)

可重复读是InnoDB的默认隔离级别。该隔离级别保证了在同一个事务中多次读取同样的记录结果是一致的,解决了读已提交的不可重复读问题。

场景一:可重复读

session1 session2
T1 设置隔离级别为可重复读
set session transaction isolation level repeatable read;
开启事务查询数据
start transaction;
select balance from account where id=2;
这是查询数据结果为balance=0
T2 开启事务修改数据提交
start transaction;
update account set balance=2 where id=2;
commit;
T3 查询数据
select balance from account where id=2;
这里还是balance=0

这里看到会话A此时看不到会话B提交的修改数据,两次查询数据一致,解决了不可重复读问题。

场景二:数据更新

session1 session2
T1 设置隔离级别为可重复读
set session transaction isolation level repeatable read;
开启事务查询数据
start transaction;
select balance from account where id=3;
这是查询数据结果为balance=0
T2 开启事务修改数据提交
start transaction;
update account set balance=1 where id=3;
commit;
T3 查询数据
select balance from account where id=3;
这里还是balance=0
T4 更新提交
update account set balance=balance+1 where id=3;
commit;
T5 查询数据
select balance from account where id=3;
这里数据变成了balance=2

这里观察数据,查询做到了可重复读,但是更新的时候使用了会话B更新后的数据为基础,导致了balance变成了2。具体这是怎么导致的,还要看mysql的多版本并发控制部分,后面再说。

串行化(SERIALIZABLE)

这个就是并发事务完全串行化执行,和开发语言多线程锁似的。最高隔离级别,会大大降低数据库并发处理性能。

这四种隔离级别常用的是读已提交和可重复读。像oracle默认事务隔离级别是读已提交,mysql这里默认事务隔离级别是可重复读。

你可能感兴趣的:(mysql学习记录,mysql,数据库)