一步步走进Sql中的事务及事务隔离级别

Posted 刷书小将

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一步步走进Sql中的事务及事务隔离级别相关的知识,希望对你有一定的参考价值。

 1.事务定义

事务是按照逻辑执行的一组任务序列,这一组任务序列要么都执行,要么都不执行。

事务是优先基于会话配置的的(session),其次是基于全局配置的(global)。

 

2.事务的提交和回滚

提交commited,即一组任务序列,正常执行后,提交给数据库。

回滚rollback,即一组任务序列中,无论哪一个任务环节出现异常,都会把所有的任务序列全部恢复到原始状态。

事务的提交可以分为手动提交和自动提交。

 

3.查看和设置提交方式

session和global中都有变量autocommit,

autocommit为1的时候自动提交,

autocommit为0的时候手动提交。

select @@session.autocommit;

 

查看全局配置中的默认提交方式

select @@global.autocommit;

 

关闭当前会话的自动提交,改为手动提交。

set @@session.autocommit=0;

 

 4.创建数据库,测试提交方式。

设计一个简单的数据库,往数据库中加一个表格。

表格中分别是,id(自增),用户名,用户余额。

USE transdemo;

DROP TABLE IF EXISTS users;

CREATE TABLE users (
  uid INT(11) AUTO_INCREMENT,
  uname VARCHAR(10) DEFAULT NULL,
  money INT(11) DEFAULT NULL,
  PRIMARY KEY (uid)
)

 

插入一条数据

INSERT INTO users VALUES(NULL,\'zhanghao\',1000)

mysql自增字段(id)插入时,用null即可。

 

结果:

 

自动提交:

 

 手动提交:

 为了测试手动提交,打开另外一个命令窗口并连接mysql,即开启了另外一个会话窗口B(Windows PowerShell)

这是原本的会话窗口A(Windows PowerShell),关闭自动提交,改为手动提交。

此时A中会话看似一切正常。

 

在B中,再次读取users表。

问题出来了,zhanghao用户的money还是900,表格数据并没有被更新。

 

此时,我们在A会话(原本会话)中,提交commit;

 

 

此时,我们在B会话(新开的会话窗口)再次查询,

数据被更新。

 

总结:自动提交时,无需commit即可自动更新改变数据;手动提交时,必须要commit,才能更新改变数据库中的数据。

 

5.事务隔离级别

事务中的隔离级别共有四种:

1.读未提交 read uncommited

2.读已提交 read commited

3.可重复读 repeatable read

4.串行化 serializable

 

 

首先查看当前会话的事务隔离级别

select @@session.transaction_isolation;


-- 8.0以下版本
-- select @@session.tx_isolation;

 

 

 再看一下全局的

select @@global.transaction_isolation;


-- 8.0以下版本
-- select @@global.tx_isolation;

 

从上面测试可以看出,mysql的事务隔离级别,默认是repeatable-read,即可重复读。

 

我们可以修改mysql的事务隔离级别,仍然基于当前会话(session)

-- 将当前会话的事务隔离级别设置为 : read uncommitted,即读未提交
set session transaction isolation level read uncommitted;

-- 查看当前会话的事务隔离级别
select @@session.transaction_isolation;

 

 此时,事务的隔离级别变为的read uncommitted即读未提交。

 

5.1 读未提交

两个连接窗口AB(Windows PowerShell),测试读未提交。

按上文方法,将A连接设置为读未提交。B连接不设置隔离级别,采用默认。

首先在B连接中,开始事务,并将zhanghao用户的money减100。

此时,在事务中,可以正常的显示zhanghao的money为900,成功被减去100。

但是在数据库中查看

zhanghao的money仍为1000。

这也不奇怪,因为B连接的事务并没有被提交。

但是,在B连接中的事务没有提交,数据库users表中zhanghao的money为1000的时候,

在A会话窗口中(已被设置为读未提交),开始事务并读取zhanghao的money时,却读到了未提交的900。

这就是读未提交。

导致的问题:脏读。即另外一个事务尚未提交的时候(数据库中的数据没被改变的时候),在A窗口事务中,已经读到了未提交的数据。

 

 

5.2 读已提交

仍然开始两个Windows PowerShell窗口分别连接mysql,实现两个会话。

首先在A窗口中,设置事务隔离级别为读已提交。

set session transaction isolation level read committed;

 

B窗口采用默认的隔离级别,不做任何改动即可。

 

数据库初始值

 

在B窗口中,开启事务,并把money减去100,不提交。

 

然后,在A窗口中,开始事务并查询users表,不提交

 

 然后,在B窗口中,提交。

 

然后,在A窗口中,再次查询users表。

 

本次流程A窗口的完整运行结果如下

第一次查询的时候,由于B窗口的update未提交,所以为money1000,

B窗口提交之后,A窗口再次查询的时候,money变为了900。

这就是读已提交。

导致的问题:不可重复读。从上图可以明显看出,在一个事务中,两次读到的结果不相同。

 

 

5.3 可重复读

仍然开始两个Windows PowerShell窗口分别连接mysql,实现两个会话。

首先在A窗口中,设置事务隔离级别为可重复读。

 

B窗口采用默认的隔离级别,不做任何改动即可。

 

数据库初始值

 

首先在B窗口中,开启事务。并将zhanghao的money减去100,不提交。

 

然后,在A窗口中,开启事务并查看users表,不提交。

 

然后B窗口提交。

 

然后再次在A窗口中查看users表。

 

 发现,两次查询的结果,是一致的。即使B窗口已经将money改为900并提交,

但是在A窗口的一个事务中,两次查询结果是一致的

 

这就是可重复读,即在一个事务中,重复读数据时不会变的。

但是这就完美了吗?

 

此时A窗口仍未提交,B窗口已提交。

在B窗口,再次开启一个事务,并且添加一条用户,lisi,500。

此时,A窗口再次查询users表时,诡异的多了一条lisi的数据,

也就是像魅影一样的出现了,这就是幻读。

mysql8中,已经对幻读增加了防范措施,低版本mysql仍存在幻读问题。

可重复读导致的问题:幻读

 

 

5.4 串行化

同上,开启A、B两个Windows PowerShell窗口连接mysql,

在AB窗口中,都设置事务隔离方式为串行化

set session transaction isolation level seriable;

 

首先在A窗口中开启事务,并查询users表

 

 

然后再B窗口中开启事务,并将zhanghao的money减去100,却发现窗口卡住了。

 

回到A窗口,提交事务

 

此时B窗口,更新操作顺利完成

 

这就是串行化,

即当A连接中的事务访问一个表的时候,

B连接中的事务想要修改表中的数据,

B连接就会卡住,等待A连接中的事务提交或回滚后,才继续执行B连接中的修改事务。

即谁先执行的事务,必须等这个事务执行完,才能执行另一个事务。

缺点:性能非常低。

 

以上是关于一步步走进Sql中的事务及事务隔离级别的主要内容,如果未能解决你的问题,请参考以下文章

Java Web 事务,安全问题及隔离级别

mysql,oracle,sql server中的默认事务隔离级别查看,更改

mysql,oracle,sql server中的默认事务隔离级别查看,更改

CRUD工程师——数据库事物ACID特性及隔离级别

MySQL 事物及隔离级别

数据库事务与四种事务隔离级别详解及对比