mysql相关事务的介绍以及应用

 一、mysql相关知识准备

   1.1、mysql事务 

      事务是一组不可分割的mysql语句组,这些语句组要么全部执行成功,要么全部执行失败。               

      事务的四大特性(ACID)

     1.原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,

   要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。

     2.一致性(consistency):数据库总数从一个一致性的状态转换到另一个一致性的状态。

     3.隔离性(isolation):一个事务所做的修改在最终提交以前,对其他事务是不可见的。

     4.持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。

 1.2、mysql事务的相关操作

      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;   

    二、mysql中事务的隔离级别

         2.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');

                 创建多个事务(至少两个进行处理)并设置对应的不同隔离级别来进行演示

                 关闭事务的自动提交

          2.1、read uncommitted
 

  1.  一个事务中可以看到另一个是事务中未提交的数据(脏读)
#设置事务的隔离级别 为未提交读(会发生脏读,不可重复读,和幻读现象)
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)

           2.3、repeatable read(MySQL默认隔离级别)

     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)

 2.4、serializable 

      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、所谓串形化,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。通俗地讲就是,假如两个事务都操作到同一数据行,对于读取操作相关的操作,所有的事务都可以获取其中的共享锁,但是针对其中的增删改的操作只有一个事务会获取其中的排他锁,只有该事务提交后,才会释放其中的锁。避免了不可重复读的操作。

     

              手打不易,如果帮到了你什么,希望您点个赞

                       如果有欠缺,欢迎你在下方留言。

                         你的鼓励是我坚持写的动力。

猜你喜欢

转载自blog.csdn.net/liushangzaibeijing/article/details/82888778
今日推荐