事务是一组不可分割的mysql语句组,这些语句组要么全部执行成功,要么全部执行失败。
事务的四大特性(ACID)
1.原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,
要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
2.一致性(consistency):数据库总数从一个一致性的状态转换到另一个一致性的状态。
3.隔离性(isolation):一个事务所做的修改在最终提交以前,对其他事务是不可见的。
4.持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。
1、查询当前会话的事务级别
select @@tx_isolation;
repeatable read(MySQL默认隔离级别)
2、查询全局会话的事务级别
select @@global.tx_isolation
repeatable read(MySQL默认隔离级别)
3、 设置事务的mysql语法
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
4、设置当前会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
当前会话结束后事务的隔离级别又会恢复原样
5、设置全局的事务隔离级别
SET GLOBALTRANSACTION ISOLATION LEVEL READ UNCOMMITTED
全局设置后会保持不变,除非进行更改
6、mysql事务的提交方式
1、查看默认的事务提交方式
#查询事务的提交方式(当前会话的 0表示关闭自动提交,1表示开启自动提交(缺省情况下))
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
2、设置事务的提交方式 关闭自动提交
SET autocommit=0;
创建表来演示不同隔离级别下的脏读,不可重复读,幻读等现象发生准备
建表sql
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` varchar(20) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`weath` float(20,0) DEFAULT NULL COMMENT '拥有的财富',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'xieqx', '1000');
INSERT INTO `user` VALUES ('2', 'xush', '1000');
INSERT INTO `user` VALUES ('3', 'lidn', '1000');
INSERT INTO `user` VALUES ('5', 'Bob', '1000');
创建多个事务(至少两个进行处理)并设置对应的不同隔离级别来进行演示
关闭事务的自动提交
#设置事务的隔离级别 为未提交读(会发生脏读,不可重复读,和幻读现象)
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
2、演示脏读场景。公司发工资 开启两个事务 事务A表示公司 事务B表示个人金额账户
注意场景下 两个事务的开启时间需要一致(或者另一个事务开启要在一个事务在处理 增删改的操作之前开启),因为事务的处理本身就是在并行操作的场景演示(两者同时操作一条记录(竞争资源)) 否则无法实现 切记切记
事务A操作:
#开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
#查询当前个人账号金额 10
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 10 |
+----+-------+-------+
1 row in set (0.00 sec)
//更新个人账户金额 添加1000
mysql> update user set weath = weath + 9000 where id = "1";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#查询金额更新成功
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 9010 |
+----+-------+-------+
##注意此时当前事务还没有结束
事务B操作:
#个人查询账号操作
#开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
#查询订单(公司为更新操作之前)
mysql> select * from user where id ="1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 10 |
+----+-------+-------+
1 row in set (0.00 sec)
#再次查询 查询到了另一个事务中未提交的数据 造成了脏读现象
mysql> select * from user where id ="1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 9010 |
+----+-------+-------+
1 row in set (0.00 sec)
2.2、read committed
1、一个事务A中读取另一个事务B中已经提交的数据。但是另一个事务B中突然回滚,事务A再次读取和原来不一样的数据,所以可能造成多次读取的数据结果不一致(不可重复读,幻读)。
#设置mysql的事务隔离级别 为已经提交读
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
2、演示不可重复读场景 公司发工资,但是客户在同一个事务中获取到两个不同的金额
事务A操作
#开始事务
mysql> select * from user where id ="1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 10 |
+----+-------+-------+
1 row in set (0.00 sec)
#更新 金额
mysql> update user set weath = weath + 9000 where id = "1";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#执行提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
事务B操作:
#个人操作
#在个人事务中
#先进行查询
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 10 |
+----+-------+-------+
1 row in set (0.00 sec)
#公司事务中进行了更新,但是没有提交 查询的结果保持不变 说明在该隔离级别下 避免了脏读现象的发生
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 10 |
+----+-------+-------+
1 row in set (0.00 sec)
#公司的事务已经提交 ,但是还是在个人事务中,查询出来两种截然不同的金额结果 不可重复读的现象发生了
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 9010 |
+----+-------+-------+
1 row in set (0.00 sec)
1、 可以重复读取,但有幻读。读写观点:读取的数据行不可写,但是可以往表中新增数据。在MySQL中,其他事务新增的数据,看不到,不会产生幻读。采用多版本并发控制(MVCC)机制解决幻读问题。
#可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,
#InnoDB默认级别。在#SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
2、幻读场景的演示
事务A操作:
#开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
#查询目前的金额
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 20010 |
+----+-------+-------+
#更新金额
mysql> update user set weath=weath+10000 where id = "1";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#再次查询
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 30010 |
+----+-------+-------+
#提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
事务B操作:
#开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
#A事务 更新金额但是没有进行事务提交 (查询出来原来的结果)
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 20010 |
+----+-------+-------+
1 row in set (0.00 sec)
#A事务提交后,还是在一个B事务中查询金额 和上面的查询保持不变 避免了不可重复读操作
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 20010 |
+----+-------+-------+
幻读发生
事务A
#开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
#查询用户 还有6条记录 最高id 为8
mysql> select * from user;
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 30010 |
| 2 | xush | 10 |
| 3 | lidn | 10 |
| 5 | Bob | 10 |
| 7 | guoqi | 200 |
| 8 | aaa | 100 |
+----+-------+-------+
6 rows in set (0.00 sec)
#在该事务中添加一条记录id为9
mysql> insert into user value("9","bbb",102);
Query OK, 1 row affected (0.00 sec)
#提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
事务B
#事务B操作
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
#在事务B 添加一条记录前查询记录数
mysql> select * from user;
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 30010 |
| 2 | xush | 10 |
| 3 | lidn | 10 |
| 5 | Bob | 10 |
| 7 | guoqi | 200 |
| 8 | aaa | 100 |
+----+-------+-------+
6 rows in set (0.00 sec)
#事务A添加一条记录并提交后 查询还是没有变化
mysql> select * from user;
+----+-------+-------+
| id | name | weath |
+----+-------+-------+
| 1 | xieqx | 30010 |
| 2 | xush | 10 |
| 3 | lidn | 10 |
| 5 | Bob | 10 |
| 7 | guoqi | 200 |
| 8 | aaa | 100 |
+----+-------+-------+
6 rows in set (0.00 sec)
#重新再插入一条数据 发现这条数据id为9的数据已经插入了,命名没有查到id为9的记录,但是插入的时候
#出现记录已经存在 出现了幻读情况的发生
mysql> insert into user value("9","bbb","100");
ERROR 1062 (23000): Duplicate entry '8' for key 'PRIMARY'
#或者 更新数据id为9的数据 更新可以成功再次查询会发现多了一条数据 (好像出现了幻觉)
mysql> update user set weath = 520 where id = 9;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#更新后(自己查询不到的id)结果发现查询出来了这条记录
mysql> select * from user;
+----+--------+-------+
| id | name | weath |
+----+--------+-------+
| 1 | xieqx | 30010 |
| 2 | xush | 10 |
| 3 | lidn | 10 |
| 5 | Bob | 10 |
| 7 | guoqi | 200 |
| 8 | aaa | 100 |
| 9 | bbb | 102 |
+----+--------+-------+
7 rows in set (0.00 sec)
1、可读,不可写。像java中的锁,写数据必须等待另一个事务结束。
#设置事务的隔离级别为 串形化
mysql> SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
1 row in set, 1 warning (0.00 sec)
2、所谓串形化,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。通俗地讲就是,假如两个事务都操作到同一数据行,对于读取操作相关的操作,所有的事务都可以获取其中的共享锁,但是针对其中的增删改的操作只有一个事务会获取其中的排他锁,只有该事务提交后,才会释放其中的锁。避免了不可重复读的操作。