MYSQL事务

介绍

事务(Transaction)是指作为单个逻辑工作单元执行的一系列操作。

特性

  • 原子性(Atomicity)

        表示组成一个事物的多个数据库操作是一个不可分隔的原子单元,只有所有的操作执行成功,整个事务才提交,事务中任何一个数据库操作失败,已经执行的任何操作都必须撤销,让数据库返回到事务开始执行的初始状态。

  • 一致性(Consistency)

        事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。

  • 隔离性(Isolation)

        在并发数据操作时,不同的事务拥有各自数据空间,它们的操作不会对对方产生干扰。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。

  • 持久性(Durabiliy)

        一旦事务提交成功后,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证能够通过某种机制恢复数据。

类型

  • 自动提交事务

        系统默认每个TRANSACT -SQL命令都是一个事务处理,由系统自动开始并提交。

  • 隐式事务

        不需要显示开始事务,需要显示提交,隐式事务是任何单独的INSERT、UPDATE或者DELETE语句构成。当有大量的DDL和DML命令执行时会自动开始,并一直保持到用户明确提交为止。

  •    显示事务

        显示事务是用户自定义事务,以START TRANSACTION(事务开始)开头,以COMMIT(事务提交)或者ROLLBACK(回滚事务)语句结束。

  • 分布式事务

        跨越多个服务器的事务称为分布式事务,从MySQL5.03开始支持分布式事务。

语法

  • 开始事务
START {TRAN | TRANSACTION};
  • 提交事务
COMMIT;
  • 事务回滚
ROLLBACK;
  • 事务设置

        SET AUTOCOMMIT 可以修改当前事务提交的方式,SET AUTOCOMMIT = 0,则需要以明确的命令进行提交或者回滚事务。

隔离级别

  • Read Uncommitted(读未提交)

        事务可以读取其他未提交事务的执行结果。读取未提交的数据,也被称之为脏读(Dirty Read)。

  • Read Committed(读已提交)

        事务只能读取其他事务已经提交的执行结果。本隔离级别支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

  • Repeatable Read(可重复读)

        MySQL默认的事务隔离级别,会给查询的记录做快照,直到事务结束。确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。

  • Serializable(串行化)

        最高的隔离级别,对同一条记录读和修改的多个事务只能结束一个,才能开始下一个。
通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。在每个读的数据行上加上共享锁,可能导致大量的超时现象和锁竞争。

隔离级别的设置

用户可以用SET TRANSACTION语句改变单个会话或者所有新进连接的隔离级别,语法如下:

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};

默认的行为(不带session和global)是为下一个(未开始)事务设置隔离级别。如果使用GLOBAL关键字,在全局对新开始创建的所有新连接设置默认事务级别,需要SUPER权限。使用SESSION关键字为将来在当前连接上执行的事务设置默认事务级别。 任何客户端都能自由改变会话隔离级别,或者为下一个事务设置隔离级别。

查询全局和会话事务隔离级别:

SELECT @@global.tx_isolation; 
SELECT @@session.tx_isolation; 
SELECT @@tx_isolation;

通过mySQL配置文件修改全局事务隔离级别,设置全局会话默认事务隔离级别:

[mysqld]
xxxxxxx
transaction-isolation=read-committed

重启mySQL服务,生效。

准备

创建一张表,包含ID,姓名,年龄,地址,用于后面验证事务的隔离级别:

create database tx_test;
use tx_test;

DROP TABLE IF EXISTS `fruit`;
CREATE TABLE `fruit` (
     `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
     `name` varchar(100) DEFAULT '' COMMENT '名称',
     `price` float(5,2) unsigned DEFAULT '0' COMMENT '价格',
     PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='水果';

insert into fruit values(1, "apple", 35);
insert into fruit values(2, "banana", 35);

上述语句执行之后person表中的数据如下:

设置事务的隔离级别

1.查看会话的隔离级别

在终端中登录到mysql,记为会话1,使用以下命令查看会话1的事务隔离级别:

select @@tx_isolation;

查询结果如下:

 2.修改会话1的事务隔离级别为READ-UNCOMMITTED:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

3.再次查看会话1的事务隔离级别,发现已经被修改为了READ-UNCOMMITTED:

select @@tx_isolation;

查询结果如下:

4. 重新打开一个终端并登录到mysql,记为会话2,查看会话2下的事务隔离级别,发现在会话2下隔离级别仍然是REPEATABLE-READ:

select @@tx_isolation;

查询结果如下:

 总结:通过一下方式设置事务的隔离级别只在当前会话有效。

SET SESSION TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

验证READ-UNCOMMITTED隔离级别

打开一个终端并登录到mysql,记为会话1,设置事务的隔离级别为REPEATABLE-READ:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

查看会话1的事务隔离级别:

select @@tx_isolation;

查询结果如下:

 打开一个终端并登录到mysql,记为会话2,查看事务的隔离级别:

select @@tx_isolation;

查询结果如下:

在会话2中开始一个事务,将fruit表中ID为1的记录的price字段更新为5.6:

use tx_test;
start TRANSACTION;
update fruit set price=5.6 where id=1;

在会话1中使用一下语句查看fruit表中ID为1的记录

select * from fruit where id=1;

price已经被修改为了5.6:

 在会话2中将事务回滚:

ROLLBACK;

在会话1中查看fruit表ID为1的记录:

select * from fruit where id=1;

在会话2中查看fruit表ID为1的记录:

select * from fruit where id=1;

 总结:在READ-UNCOMMITTED隔离级别下,事务1中能读到事务2中未提交的数据。

验证READ COMMITTED隔离级别

打开一个终端并登录到mysql,记为会话1,设置事务的隔离级别为READ COMMITTED

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

执行结果如下:

 查询会话1的事务隔离级别:

select @@tx_isolation;

查询结果如下:

 打开一个终端并登录到mysql,记为会话2,开始一个事务,更新fruit表中ID为1的记录的price字段为5.8:

use tx_test;
start TRANSACTION;
update fruit set price=5.8 where id=1;

执行结果如下:

 在会话1中查看fruit表中ID为1的记录:

select * from fruit where id=1;

查询结果如下:

 在会话2中提交事务:

COMMIT;

执行结果如下:

 在会话1中查看fruit表中ID为1的记录:

select * from fruit where id=1;

查询结果如下:

总结:在 READ COMMITTED隔离级别下,在事务1中无法读到事务2中尚未提交的数据,一旦事务2被提交,则事务1可以读取到相关的数据。

验证REPEATABLE READ隔离级别

打开一个终端并登录到mysql,记为会话1,设置事务的隔离级别为REPEATABLE READ:

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

执行结果如下:

在会话1中查看fruit表中ID为1的记录:

start TRANSACTION;
select * from fruit where id=1;

执行结果如下:

打开一个终端并登录到mysql,记为会话2,将fruit表中ID为1的记录的price字段更新为9.8:

update fruit set price=9.8 where id=1;

执行结果如下:

在会话2中查看fruit表中id为1的记录:

select * from fruit where id=1;

执行结果如下:

在会话1中再次查看fruit表中ID为1的记录:

select * from fruit where id=1;

查询结果如下:

 

 在会话1中提交事务:

COMMIT;

执行结果如下:

 在会话1中再次查看fruit表中ID为1的记录:

select * from fruit where id=1;

查询结果如下:

 总结:在REPEATABLE READ隔离级别隔离级别下,事务1对数据可以重复读,并且读到的数据是一样的,即使事务2对其中的数据做了更新并且提交了事务,在事务1中忍让能读到相同的数据。

验证SERIALIZABLE隔离级别

打开一个终端并登录到mysql,记为会话1,设置事务的隔离级别为SERIALIZABLE:

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

执行结果如下:

 打开一个终端并登录到mysql,记为会话2,开始一个事务,将fruit表中ID为1的记录的price字段更新为7.3:

start TRANSACTION;
update fruit set price=7.3 where id=1;

执行结果如下:

在会话1中开始一个事务,查看fruit表中ID为1的记录:

start TRANSACTION;
select * from fruit where id=1;

 执行结果如下,会话1一直处于等待状态:

 在会话2中提交事务后:

COMMIT;

执行结果如下:

 再次在会话一中查看fruit表中ID为1的记录:

select * from fruit where id=1;

查询结果如下:

总结:在SERIALIZABLE隔离级别下,事务是串行执行的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值