MySQL的隔离级别
mysql中的事务
事务:一个事务是一个完整的业务逻辑单元,不可再分,比如说银行的转账,A向B进行转账。需要执行两条update语句
update t_act set balance =balance-10000 where actno = 'tom';
update t_act set balance = balance+10000 where actno = 'jerry';
以上两条SQL语句必须要求同时成功或者是同时失败,不能够一条成功了,另外一条没有成功,保证数据的正确性和完整性。
要想保证上面的两条SQL同时成功或者是同时失败,那么必须使用到MySQL中的事务管理。
只有DML语句才和事务有关系,因为只有DML中的(insert、update、delete)语句都是和数据库中的事务保持关系的。
事务的存在就是为了保证数据的完整性。
如果所有的业务一条DML就能够搞定,那么就不再需要事务了。但是现实就是很复杂,没有办法做到一条SQL语句就能够做到。
现实生活中就是一个业务,关联着多个DML语句的。
有一个业务场景需要来进行完成:
需要先进行一条Insert语句,然后进行一条update语句,最后执行一条delete语句
第一步:开启事务;
第二步:进行insert语句,这条语句执行成功之后,并不是立即就修改掉硬盘中的数据,而是保存中数据库的操作历史中去;
第三步:执行update语句,这条语句执行成功之后,并不是立即就修改掉硬盘中的数据,而是和上面一样,保存到操作历史中去;
第四步:执行delete语句,这条语句执行成功之后,并不是立即就修改掉硬盘中的数据,而是保存到数据库的操作历史中去;
第五步:只有上面的三条DML语句都成功的时候,这个时候提交事务之后,才会真正的修改掉硬盘中的数据,并将操作历史中的文件进行删除。
注意:如果要是有一条语句没有成功,那么将会回滚。最终的结果就是,不会去修改掉磁盘中的数据,同时也会将这些操作历史进行删除。
不管是commit还是rollback,都会将事务进行关闭。
commit比rollback多做了一步,那就是修改硬盘中的数据。
可以在事务中进行设置保存点,savePoint,在进行rollback的时候,可以设置回到哪个保存点,但是保存点这块很少使用,了解下。
MySQL事务又被称之为本地事务。
MySQL的四大特性
ACID
A:Atomicity,原子性。事务是最小的工作单元,不可再进行分离。一组SQL组合起来,要么全部成功,要么全部失败
C:Consistency,一致性。事务要么是成功的(commit),要么是失败的(rollback)。在DML语句中,会导致数据的状态从一种状态转换到另外一个状态上去。
但是要求的是在事务开始到事务结束这期间,其它事务是看不到这一块的数据的。
I:Isolation:隔离性。事务A和事务B具有隔离性,java开发这块难点就是事务和线程,也是为了保证数据的安全。
A干自己的事情,B干自己的事情。这块会影响到一致性。
隔离级别:
但是隔离性破坏了一致性,在这个基础上衍生出来的。
读未提交:读到了其它事务还没有提交的数据;存在问题是脏堵
读已提交:读到了其它事务提交到的数据。存在问题是不可重复读
可重复读:在进行select的时候,对于同一个SQL语句来说,要求读取到的结果必须是相同的。存在幻读
串读:比如有十个事务在进行读取的时候,这些个事务必须排队,一个一个的执行。
D:Durability:持久性。数据最终必须持久化到硬盘文件中去。保证数据是不会丢失的。
但是这块对隔离性又有了级别。
事务隔离性,四种:
1、读未提交
对方事务还没有提交,当前事务可以读取到对方未提交的数据。
存在的问题:脏读,读到了脏的数据
2、读已提交(Oracle的默认级别)
对方事务提交之后的数据可以读到。
存在的问题:不可重复读取。对方已提交,读取的就是新的,读取不到最新的数据。
可以理解是不同的时间节点,读取数据库中的记录条数是不固定的。一会儿是10条,一会儿是8条,一会儿是100条
解决的问题:解决了脏读现象。
3、可重复读(MySQL的默认级别)
当前事务没有结束,读取的永远都是固定的条数,读取的永远都是开启事务时候的记录的条数,无论另外一个事务进行了怎样的操作。
首先是解决了上面的不可重复进行读取的问题。
存在的问题:幻读。因为另外一个事务可能对数据库中的记录进行了增删改,但是当前的事务读取到的还都是开启事务之前的数据
相当于是在数据库做了一个备份。
4、串读
特点:当前事务没有结束之前,其他的事务不能够进行操作,只能够排队进行等待
解决了幻读的现象,但是缺点是效率比较低。
MySQL事务的演示:
mysql默认的是自动提交的,也就是执行任意一条DML语句,那么自动进行提交,所以要进行演示的时候,需要手动的进行关闭。
TCL语句:commit和rollback。
关闭自动提交命令:start transaction,开启事务的意思。手动的来进行提交事务
准备工作:
drop table if exists t_user;
create table t_user(
id int PRIMARY key auto_increment,
username varchar(200)
);
实验一:
开启动作:begin;
insert into t_user (username) values ('liguang');
在输入命令rollback的时候,发现表中已经存在了对应的一条数据
mysql> insert into t_user (username) values ('liguang');
Query OK, 1 row affected (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | liguang |
+----+----------+
使用start transaction来关闭自动提交事务的机制,标志着一个事务开启了。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t_user (username) values ('zhangsan');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | liguang |
| 2 | zhangsan |
+----+----------+
2 rows in set (0.00 sec)
mysql> insert into t_user (username) values ('wangwu');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | liguang |
| 2 | zhangsan |
| 3 | wangwu |
+----+----------+
3 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | liguang |
+----+----------+
1 row in set (0.00 sec)
------------------------------------------------------------------------
总结:在进行设置了开启了事务之后,再次进行了插入数据之后,虽然可以进行查询,但是在回滚了之后,发现数据库中的记录依然没有发生变化,依然是原来保存到数据库中的数据。
验证了原来的结论,rollback之后不会将操作历史数据同步到硬盘中去,而是直接给删除掉了。
用两个事务来演示事务的隔离级别:
第一:演示读未提交,开启两个窗口,代表的是两个连接,开启了两个事务。
// 设置隔离级别
set global transaction isolation level read uncommitted;
// 查看设置的隔离级别
select @@global.transaction_isolation;
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.transaction_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED |
+-----------------------+
1 row in set (0.00 sec)
设置完成窗口之后,将窗口关闭之后进行重新打开。
窗口一:
开启事务A
首先开启事务
mysql> start tranction;
mysql> insert into t_user (username) values ('limeng');
Query OK, 1 row affected (0.00 sec)
// 不进行提交
。
开启事务B:
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | liguang |
| 4 | limeng |
+----+----------+
2 rows in set (0.00 sec)
在这里读去到了未提交的数据
在事务A中开启事务后,然后插入一条对应的数据,然后开启事务B,执行查询操作,可以看到查询出来的值为事务A插入进去的值;
演示二:读取已提交的数据
开启事务A窗口:
// 设置隔离几倍
set global transaction isolation level read committed;
// 查看设置的隔离级别
select @@global.transaction_isolation;
开启事务B窗口:
// 设置隔离几倍
set global transaction isolation level read committed;
// 查看设置的隔离级别
select @@global.transaction_isolation;
下面是具体的代码
// 事务A插入数据,并进行了提交。可以先演示没有commit之后,事务B能否 读取到事务A未提交的数据。不能。
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | liguang |
+----+----------+
1 row in set (0.00 sec)
mysql> insert into t_user (username) values ('lijiayi');
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
在事务B中:
mysql> select * from t_user;
+----+----------+
| id | username |
+----+----------+
| 1 | liguang |
| 5 | lijiayi |
+----+----------+
2 rows in set (0.00 sec)
---------------------------------------------------------------------
可以在事务B中查看得到数据库中的信息
演示第三种:可重复读
在事务A窗口中
开启事务A窗口:
// 设置隔离几倍
set global transaction isolation level repeatable read;
// 查看设置的隔离级别
select @@global.transaction_isolation;
在事务B窗口中:
开启事务A窗口:
// 设置隔离几倍
set global transaction isolation level repeatable read;
// 查看设置的隔离级别
select @@global.transaction_isolation;
在事务A窗口中:
mysql> select * from t_user;
+----+-------------+
| id | username |
+----+-------------+
| 1 | liguang |
| 5 | lijiayi |
| 6 | lining |
| 7 | linxiaoixao |
+----+-------------+
4 rows in set (0.00 sec)
// 把表都来进行删除
mysql> insert into t_user (username) values ('lijing');
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
-------------------------------------------------------------
mysql> select * from t_user;
+----+-------------+
| id | username |
+----+-------------+
| 1 | liguang |
| 5 | lijiayi |
| 6 | lining |
| 7 | linxiaoixao |
| 8 | lijing |
+----+-------------+
5 rows in set (0.00 sec)
在事务B窗口中:
mysql> select * from t_user;
+----+-------------+
| id | username |
+----+-------------+
| 1 | liguang |
| 5 | lijiayi |
| 6 | lining |
| 7 | linxiaoixao |
+----+-------------+
4 rows in set (0.00 sec)
-------------------------------------------------
一直重复进行去读,但是还是没有读到最新的数据。
演示四:串读
在事务A窗口中
开启事务A窗口:
// 设置隔离几倍
set global transaction isolation level serializable;
// 查看设置的隔离级别
select @@global.tx_isolation;
在事务B窗口中:
开启事务A窗口:
// 设置隔离几倍
set global transaction isolation level serializable;
// 查看设置的隔离级别
select @@global.tx_isolation;
在事务A中
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t_user (username) values ('lixiang');
Query OK, 1 row affected (0.00 sec)
事务B中开启事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user;
在事务A中没有进行提交或者是回滚,在事务B中进行查询的时候,事务B会一直卡住,无法进行操作。
MySQL中的事务开启的两种方式:
MySQL事务的开启与提交,autocommit自动提交功能
对于一个MySQL数据库(InnoDB),事务的开启与提交模式无非下面这两种情况:
1、若参数autocommit=0,事务则在用户本次对数据进行操作时自动开启,在用户执行commit命令时提交,用户本次对数据库开始进行操作到用户执行commit命令之间的一系列操作为一个完整的事务周期。
若不执行commit命令,系统则默认事务回滚。总而言之,当前情况下事务的状态是需要手动去提交。
2、若参数autocommit=1(系统默认值),事务的开启与提交又分为两种状态:
2.1、手动提交:autocommit=0
当用户执行start transaction命令时(事务初始化),一个事务开启,当用户执行commit命令时当前事务提交。
从用户执行start transaction命令到用户执行commit命令之间的一系列操作为一个完整的事务周期。
若不执行commit命令,系统则默认事务回滚。
2.2、自动提交:autocommit=1
如果用户在当前情况下(参数autocommit=1)未执行start transaction命令而对数据库进行了操作,系统则默认用户对数据库的每一个操作为一个孤立的事务,
也就是说用户每进行一次操作系都会即时提交或者即时回滚。这种情况下用户的每一个操作都是一个完整的事务周期。
=================================
设置autocommit开启和关闭的方法:
用户可以将自动提交功能强制置为OFF。这样用户执行SQL语句后将不会被提交了,而执行COMMIT命令才提交,执行ROLLBACK命令回滚。
查看当前自动提交功能状况:
show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.06 sec)
临时生效设置方法:
下面是将自动提交功能置为ON以及置为OFF的具体语法。
将自动提交功能置为ON
SET AUTOCOMMIT=1;
将自动提交功能置为OFF
SET AUTOCOMMIT=0;
永久生效设置方法:
通过修改配置文件my.cnf文件,通过vim编辑my.cnf文件,在[mysqld](服务器选项下)添加:
autocommit=0
参考博客: