天天看点

JDBC专题(七)-事务的隔离级别

1.事务的四大特性(ACID)

  • 原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败

  • 一致性(Consistency)

    官网上事务一致性的概念是:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性。

  • 隔离性(Isolation)

    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

  • 持久性(Durability)

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

    事务的四大特性中最麻烦的是隔离性,下面重点介绍一下事务的隔离级别.

2.事务的隔离级别

多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

2.1事务不考虑隔离性可能会引发的问题

2.1.1.编写测试SQL脚本,如下:

/*创建账户表*/
create table account(
    id int primary key auto_increment,
    name varchar(40),
    money float
);

/*插入测试数据*/
insert into account(name,money) values('A',1000);
insert into account(name,money) values('B',1000);
insert into account(name,money) values('C',1000);      

下面我们在MySQL数据库中模拟A——B转帐这个业务场景来说明事务问题

2.1.2.脏读

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

  • 例如:

    A工资为1000,事务A中把他的工资减去100,再把B的工资加上100,但事务A尚未提交。

    与此同时,

    事务B正在查询工资表,读取到A的工资为900,B的工资为1100。

    随后,

    事务A发生异常,而回滚了事务。A的工资又回滚为1000,B的工资也回滚为1000

    最后,

    最终事务B读取到A的工资为900,B的工资为1100,事务B做了一次脏读。

这是非常危险的,假设A向B转帐100元,对应sql语句如下所示

JDBC专题(七)-事务的隔离级别
脏读最严重的事情、读取数据是不真实的
2.1.2.不可重复读

一个事务读取另外一个提交过的数据。造成另外一个事务,多次读取的内容不一致,数据的内容的改变。—update

一个事务,读取了另外一个事务提交了的数据。-----同一行数据。

数据的改变。—update

JDBC专题(七)-事务的隔离级别
2.1.3.虚读(幻读)

虚读:一个事务读取另外一个事务已经提交的数据。但是这里面强调的数据数目的改变。insert,delete。

一个事务读取另外一个事务已经提交过的数据。 强调的是条目数的改变insert,delete

2.1.4.虚读和不可重复读对象
  • 不可重复度–update

    同一条记录 内容的改变

  • 虚读—insert delete

    条目数的改变

3.数据库的事务隔离级别

3.1.数据库4种隔离级别

MySQL数据库共定义了四种隔离级别:

1.Read uncommitted(读未提交):最低级别,以上情况均无法保证。

2.Read committed(读已提交):可避免脏读情况发生。Oracle的默认隔离级别

3.Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。MySQL默认

4.Serializable(串行化):可避免脏读、不可重复读、幻读情况的发生。

不同的隔离级别对并发问题的解决情况如图:

JDBC专题(七)-事务的隔离级别
注意:事务的隔离级别和数据库并发性是成反比的,隔离级别越高,并发性越低。

3.2.数据库4种隔离级别演示

3.2.1.查询数据库的隔离级别

mysql数据库查询当前事务隔离级别:

select @@tx_isolation

mysql数据库默认的事务隔离级别是:

Repeatable read(可重复读)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set      
3.2.2.设置数据库的隔离级别

mysql数据库设置事务隔离级别:

set session transaction isolation level 隔离级别

示例:

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set

mysql> set session  transaction isolation level Read uncommitted;
Query OK, 0 rows affected

mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set      
3.2.3.Read uncommitted

当把事务的隔离级别设置为read uncommitted时,会引发脏读、不可重复读和虚读

  • A窗口
--设置A用户的数据库隔离级别为Read uncommitted(读未提交)
set transaction isolation level Read uncommitted;
--开启事务
start transaction;
--查询A账户中现有的钱,转到B窗口进行操作
update account set money = money -100 where name = 'a';
update account set money = money +100 where name = 'b';
-- 转到B窗口进行操作      
  • B窗口
--设置B用户的数据库隔离级别为Read uncommitted(读未提交)
set transaction isolation level Read uncommitted;
--开启事务
start transaction;
--查询工资表,这时候B读到了A未提交的数据(脏读)
select * from account;      
3.2.4.Read committed

当把事务的隔离级别设置为read committed时,会引发不可重复读和虚读,但避免了脏读

  • A窗口
set transaction isolation level Read committed;
start transaction;
--发现a帐户是1000元,转到b窗口
select * from account;
--发现a帐户多了100,这时候,a读到了别的事务提交的数据,两次读取a帐户读到的是不同的结果(不可重复读)
select * from account;      
  • B窗口
set transaction isolation level Read committed;
start transaction;
update account set money=money+100 where name='A';
commit;--转到a窗口      
3.2.5.Repeatable read

当把事务的隔离级别设置为repeatable read(mysql默认级别)时,会引发虚读,但避免了脏读、不可重复读。

  • A窗口
set transaction isolation level repeatable read;
start transaction;
select * from account;--发现表有3个记录,转到b窗口
select * from account;--可能发现表有4条记录,这时候发生了a读取到另外一个事务插入的数据(虚读)      
  • B窗口
start transaction;
insert into account(name,money) values('ggg',1000);
commit;--转到a窗口      
注意:mysql对虚读已经进行了优化处理。所以展示不出虚读的发生。
3.2.6.Serializable
  • A窗口
set transaction isolation level Serializable;
start transaction;
select * from account;--转到b窗口      
  • B窗口
set transaction isolation level Serializable;
start transaction;
--发现不能插入,只能等待a结束事务才能插入
insert into account(name,money) values('ggg',1000);      

3.3.小结

  • 性能比较

    Serializable 性能最差:事务一个一个执行的。排队。

Serializable < Repeatable read < Read committed < Read uncommitted
  • 安全性比较

    Serializable 安全性最好:所有问题避免掉。

Serializable > Repeatable read > Read committed > Read uncommitted

mysql (默认)-- Repeatable read;

oracle(默认) – Read committed;