【MySQL】事务

1.事务是什么

在MySQL中,事务就是一组SQL语句,是一个操作单元,事务里的操作是不可分割的。
说人话就是,事务里的SQL语句要么都执行,要么都不执行,这些操作是一个整体。
比如:

START TRANSACTION;	#开启事务
SELECT * FROM student;
UPDATE student SET name = '李华' WHERE id = 3;
COMMIT;				#提交事务

其中SELECT * FROM studentUPDATE student SET name = '李华' WHERE id = 3两条语句就是事务了。
START TRANSACTION是开始事务的语句,TRANSACTION(transaction)是事务的意思。
COMMIT是提交事务的语句,提交了事务,服务器就把数据写入到磁盘中了,数据就永久更改了。
而且服务器要么执行完这些语句,要么一条语句都不执行。

那事务的作用体现在哪里呢?
银行转账就是事务的一个应用场景:
在这里插入图片描述

要保证两个人在转账中,一个人扣了费,一个人收了钱。
就得使用事务来处理,将一个人中的余额扣掉并且将另一个人的余额增加,
这两个操作是一个整体,是一个事务。
当这个事务执行到一半时,如果服务器宕机,也不会造影响。
因为服务器有日志信息,日志信息存在服务器硬盘里,
下次通电后,会根据日志信息选择回滚到事务之前的状态还是继续执行事务。

如果不用事务的话,还是以上两个操作。
服务器先执行扣除一个人余额操作,如果执行完了之后,突然断电!!!
这时候,增加余额的操作没有执行到,另一个人的账户是没有变化的。
这时候问题就出现了,就算通了电,一个人扣了费,另一个人账户余额还是没增加。
因为这两个操作不是一个事务,日志里没信息,没法回到之前的状态。

2.使用事务

2.1开始和提交事务

开始事务语句:begin 或者 start transaction
提交事务语句:commit
现有user表:

id          用户id
name		用户名字
age			用户年龄
balance		用户余额
+---------+-------------+------+-----+---------+----------------+
| Field   | Type        | Null | Key | Default | Extra          |
+---------+-------------+------+-----+---------+----------------+
| id      | int         | NO   | PRI | NULL    | auto_increment |
| name    | varchar(20) | YES  |     | NULL    |                |
| age     | int         | YES  |     | NULL    |                |
| balance | int         | YES  |     | NULL    |                |
+---------+-------------+------+-----+---------+----------------+

下面是数据:
+----+------+-----+---------+
| id | name | age | balance |
+----+------+-----+---------+
|  1 | 小明 |  20 |    1500 |
|  2 | 小红 |  21 |    2000 |
|  3 | 小蓝 |  22 |    1800 |
+----+------+-----+---------+

现在模拟银行转账的情况:
将小明的账户余额减少500块,将小红的余额增减500块。

START TRANSACTION;
UPDATE user SET balance = balance - 500 WHERE name = '小明';
UPDATE user SET balance = balance + 500 WHERE name = '小红';
COMMIT;

事务执行后的结果:

+----+------+-----+---------+
| id | name | age | balance |
+----+------+-----+---------+
|  1 | 小明 |  20 |    1000 |
|  2 | 小红 |  21 |    2500 |
|  3 | 小蓝 |  22 |    1800 |
+----+------+-----+---------+

2.2rollback回滚

使用rollback关键字可以回到事务开始之前的状态。
如果你发现事务里逻辑有问题,可以回滚到事务之前的状态。
这可能比较抽象,看下面的例子,就会懂怎么用事务了。

当前表数据:

+----+------+-----+---------+
| id | name | age | balance |
+----+------+-----+---------+
|  1 | 小明 |  20 |    1000 |
|  2 | 小红 |  21 |    2500 |
|  3 | 小蓝 |  22 |    1800 |
+----+------+-----+---------+

现将小蓝的余额扣除300,小红的余额增加300。
但中途发现只需要将小蓝的余额扣除200就够了,
而且事务都执行了一半了,事务里的数据都改了,怎么办呢?
这时回滚(rollback)就派上用场了。

START TRANSACTION;
#操作
UPDATE user SET balance = balance - 300 WHERE name = '小蓝';
UPDATE user SET balance = balance + 300 WHERE name = '小红';
#查表
SELECT * FROM user;
#发现问题,回滚
ROLLBACK;
#再次查表
SELECT * FROM user;

结果:

回滚之前
+----+------+-----+---------+
| id | name | age | balance |
+----+------+-----+---------+
|  1 | 小明 |  20 |    1000  |
|  2 | 小红 |  21 |    2800 |
|  3 | 小蓝 |  22 |    1500    |
+----+------+-----+---------+

回滚之后
+----+------+-----+---------+
| id | name | age | balance |
+----+------+-----+---------+
|  1 | 小明 |  20 |    1000 |
|  2 | 小红 |  21 |    2500 |
|  3 | 小蓝 |  22 |    1800 |
+----+------+-----+---------+

在回滚之后,数据又恢复原样了,之后可以再开启事务进行操作。

2.3savepoint保存点

其实,如果在事务里发现了逻辑错误的话,但是其中有一些逻辑是正确的呀,
直接回滚那不是麻烦了吗,又要重新开启一个事务,重新写一遍代码???😂
当然了,我们亲爱的MySQL设计者肯定不会这样做。

所以,在MySQL中,可以回到保存点时的状态而不用回滚事务。
MySQL中有savelpoint关键字来定义保存点。
语法如下:

savepoint name;

比如定义一个名为point1的保存点:

savepoint point1;

回滚到保存点的语法还不仅仅是直接rollback这么简单
下面时回滚到保存点的语法:

rollback to savepoint 保存点名

比如:

rollback to savepoint point1;

注意:保存点只在事务内使用

光说不练假把式,下面就是一个案例:

#开启事务
START TRANSACTION;
#修改数据
UPDATE user SET balance = balance - 300 WHERE name = '小蓝';
UPDATE user SET balance = balance + 300 WHERE name = '小红';
#设置保存点
SAVEPOINT my_point;
#删除小蓝的数据
DELETE FROM user WHERE name = '小蓝';
#查表
SELECT * FROM user;
#回滚到保存点时的状态
ROLLBACK TO SAVEPOINT my_point;
#提交事务
COMMIT;

#再次查表
SELECT * FROM user;

执行结果:

执行事务前
+----+------+-----+---------+
| id | name | age | balance |
+----+------+-----+---------+
|  1 | 小明 |  20 |    1000 |
|  2 | 小红 |  21 |    2500 |
|  3 | 小蓝 |  22 |    1800 |
+----+------+-----+---------+

执行事务后
+----+------+-----+---------+
| id | name | age | balance |
+----+------+-----+---------+
|  1 | 小明 |  20 |    1000 |
|  2 | 小红 |  21 |    2800 |
|  3 | 小蓝 |  22 |    1500 |
+----+------+-----+---------+

可以看到小蓝的数据并没有被删除,因为回滚到了删除之前的状态。

总结:
在事务中设置保存点是个好习惯,可以让你的程序容错率更高。
特别是在进行删除数据这种危险的操作时,设置保存点是非常有必要的。

3.事务的四大特性

当你把前面的内容都看过了,下面这几种性质就不难理解了。
事务的四大特性还是要掌握的,面试中也是经常问的。

3.1原子性(Atomicity)

事务中的全部操作是一个整体,这些操作要么都执行,要么都不执行。
这些操作是一个单元,就像原子一样不可分割,这就是所谓的原子性(Atomicity)。

3.2一致性(Consistency)

一致性(Consistency)讲的就是,最终表里的数据要么跟执行事务之前的数据保持一致,
要么跟执行事务之后的数据保持一致。

比如,张三有100元,李四有200元。
现在要转账,其实也就是一个事务,张三转给李四100块,李四收到一百块。
如果转账成功,即事务执行完毕,结果为张三剩0元,李四剩300元。
如果转账失败,即事务没执行完毕,结果为张三剩100元,李四剩200元。
不会出现张三剩0元,李四剩200元的情况,这就是事务的一致性。

3.3持久性(Durability)

持久性(Durability)就更好理解了,提交事务后,服务器就把数据写入到磁盘中了,对数据的更改是持久性的。

3.4隔离性(Isolation)

隔离性(Isolation)是针对数据资源的并发访问,当有多个事务操作同一个表时,不能被其他事务所干扰
这就是隔离性。

4.事务的四个隔离级别

在说事务的四个隔离级别之前,必须先说说脏读,不可重复读幻读

  • 脏读
    有事务A和事务B,事务A对数据进行了修改,然后事务B读取了事务A里未提交的数据
    但事务A进行了回滚,事务B读取到的数据就是无效的,这就是脏读。

  • 不可重复读
    事务A和事务B,事务B先读取了一个表中的数据,然后事务A对这个表的数据进行了修改
    事务B之后读取到的数据跟先前读取的数据不一样,这就是不可重复读。
    比如:在一个事务中刚开始读一个表时,有一项数据为30,可能由于有其他事务提交了,后来又变成了40。

  • 幻读
    幻读是针对插入操作来说的,事务B两次读取相同范围内的数据,但事务A插入了一些数据,导致事务B读取数据
    的时候,又多出来一些数据。

    比如:
    在事务B中,你想在学生表中查18岁到20岁的学生,然后进行了一次查询。
    事务A在表中添加了几个18岁到20岁的学生,然后事务B再次查询,发现多了几条记录
    而且这几条记录还可能跟之前的纪律一样,就很魔幻,这就是幻读。

注意:不可重复读针对的是数据的修改,而幻读针对的是数据的增加。

4.1读未提交

读未提交(read uncommited),指在一个事务中可以读取到另一个未提交事务的数据。

比如:
事务A修改数据修改数据。                                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                事务B读取事务A中未提交的数据

读未提交会导致脏读,不可重复读和脏读。

4.2读已提交

读已提交(read commited)
指的是一个事务中,不能读取还未提交事务的数据,只能读取已提交事务的数据。

事务A先查询未修改的数据                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                               事务B修改数据后,提交事务。
在查询一次已修改的数据。

读已提交解决了脏读的问题,但是会导致不可重复读和幻读。

4.3可重复读

可重复读(repeatable read),在一个事务中,读取一个范围的数据时,这些数据不会因为其他事务的提交而发生改变。
这也是MySQL的默认隔离级别。

事务A在事务B提交之前先查询了数据,                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                              事务B修改了数据,并把事务提交了。
事务A在事务B提交之后又查询了数据。

可重复读不会导致脏读和不可重复读,但是幻读的问题仍然未解决。

4.4序列化

序列化解决了脏读,幻读,和不可重复读,相当于一个事务一个事务地执行。
这里不在做演示。

各个隔离级别会导致的问题:

脏读可重复读幻读
read uncommited
read commited×
repeatable read××
serializable×××
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关于mysql事务处理 public static void StartTransaction(Connection con, String[] sqls) throws Exception { if (sqls == null) { return; } Statement sm = null; try { // 事务开始 System.out.println("事务处理开始!"); con.setAutoCommit(false); // 设置连接不自动提交,即用该连接进行的操作都不更新到数据库 sm = con.createStatement(); // 创建Statement对象 //依次执行传入的SQL语句 for (int i = 0; i < sqls.length; i++) { sm.execute(sqls[i]);// 执行添加事物的语句 } System.out.println("提交事务处理!"); con.commit(); // 提交给数据库处理 System.out.println("事务处理结束!"); // 事务结束 //捕获执行SQL语句组中的异常 } catch (SQLException e) { try { System.out.println("事务执行失败,进行回滚!\n"); con.rollback(); // 若前面某条语句出现异常时,进行回滚,取消前面执行的所有操作 } catch (SQLException e1) { e1.printStackTrace(); } } finally { sm.close(); } } 通常都是上述的写法, 在mysql 不支持事务的时候 , 中间的 setAutoCommit 的事务操作是不是都不生效. 现在innoDB支持 事务了, 上述的 java 代码是否能实现 以下的 事务隔离的 操作, 在修改的时候处于锁定状态 或者 只可以通过存储过程来实现, 单行的锁定 BEGIN; SELECT book_number FROM book WHERE book_id = 123 FOR UPDATE; --这里for update , 以前用Oracle的时候也是有这个行锁 // ... UPDATE book SET book_number = book_number - 1 WHERE book_id = 123; COMMIT;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值