事务的特性
- 原子性:指处于同一个事务中的多条语句是不可分割的。
- 一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如:转账—转账前两个账户余额之和为233¥,转账之后也应该是233¥。
- 隔离性:指多线程环境下,一个线程中的事务不能被其他线程中的事务打扰
- 持久性:事务一旦提交,就应该被永久保存起来。
事务的隔离性:隔离级别分类
如果不考虑事务的隔离性,则可能会出现以下问题:
- 脏读:指一个线程中的事务读取到了另外一个线程中未提交的数据。
- 不可重复读:指一个线程中的事务读取到了另外一个线程中提交的update的数据。
- 虚读:指一个线程中的事务读取到了另外一个线程中提交的insert的数据。
隔离级别:
- 1-READ UNCOMMITTED: 脏读、不可重复读、虚读都有可能发生。
- 2-READ COMMITTED: 防止脏读的发生,不可重复读、虚读都有可能发生。
- 4-REPEATABLE READ: 防止脏读、不可重复读的发生,虚读有可能发生。
- 8-SERIALIZABLE: 防止脏读、不可重复读、虚读的发生。
级别越高,数据越安全,相对就导致性能越低。
拿 MySQL 现身说法
查看当前事务的隔离级别:SELECT @@tx_isolation;
注意:更改事务的隔离级别,一定要在开启事务之前设置。
更改事务:SET transaction isolation level 四个级别之一(单词);
通过一个情景模拟来理解隔离级别:-- 准备工作
create table t_account(
id int(11) primary key,
name varchar(30),
money float
);
insert into t_account(id,name,money) values
(1,'a',1000),
(2,'b',1000);
set transcation isolation level read uncommited;
-- 可打开两个 CMD 窗口连接 MySQL 来模拟两位用户~
时间点 | 线程1-自己 | 线程2-其他人 | 说明 |
---|---|---|---|
t1 | start transacion; | ||
t2 | select * from t_account where name=’a’ |
a查询账户:我有1000元 | |
t3 | start transaction; | ||
t4 | update t_account set money=money+100 where name=’a’; |
其他人:给a存100,逗逗他 | |
t5 | select * from t_account where name=’a’ |
a再次查询账户,1100元(我靠,多了100块)! 读取到了另外一个 线程中未提交的数据,出现脏读 |
|
t6 | commit; | ||
t7 | select * from t_account where name=’a’ |
a又查询账户,发现还是1100元(踩到狗屎啦?)。 前后读到的数据不一致, 读到了另外一个线程中提交的 update 数据, 出现可重复读 |
|
t8 | insert into t_account values(2,’蛤蛤蛤’,233); |
||
t9 | select * from t_account; | 还是让工作人员来确认下吧。 一查总用户,我凑又多了一个人叫“蛤蛤蛤”! 读到了另外一个线程中提交的 insert 数据,出现虚读 |
|
t10 | commit; | 工作人员提交事务,两人一脸懵逼…… |
JDBC 中控制事务的隔离级别:
前提:在开启事务之前设置隔离级别。
通过Connection
中的整型常量代表不同的级别:
设置事务的隔离级别:Connection:void setTransactionIsolation(int level);