【MySQL】——详解事务
目录
一、前言
二、事务
1、为什么MySQL中会有事务这个概念?
2、什么是事务?
3、事务的版本支持
4、事务的提交方式
5、事务常见操作方式
总结
一、前言
在实际生活中,我们在对MySQL数据库进行CURD[创建(Create)、读取(Read)、更新(Update)和删除(Delete)]的操作时,要考虑到此时此刻并不是只有我们一个用户在对该数据库进行操作,所以这就牵扯到一个问题,数据库中的內容就好像是临界资源,在同时由多个用户进行CURD操作时,会不会出问题?
下面举个例子:还是拿之前在讲到线程互斥的文章中提到的火车站抢票的例子来说,抢票的线程就是同一时刻在操作数据库的用户,假设客户端A检查火车票时发现还有一张票时将票卖掉,在还没有执行更新操作时,客户端B同样检查了票数,发现大于0,于是又卖了一次票,然后A将票数更新回数据库。 所以如果不对数据的CURD进行控制的话,就会出现同一张票被卖出了两次的问题。
当然在这里MySQL结合实际情况复杂的多,并不是在线程互斥中提到的只会导致线程在没有票的时候还在抢这种情况。所以就这个例子我们需要保证的是在买票的时候:
- 买票的过程得是原子的
- 买票不能互相影响
- 买完票要永久有效
- 在买票前和买票后都得需要确定的状态
二、事务
1、为什么MySQL中会有事务这个概念?
在线程控制一文中,针对线程抢火车票会导致同一张票会被多次抢的问题,我们使用了互斥锁,在线程每次进入临界资源(票数)时,都会先上锁,等到操作结束后再解锁,这样就解决了多个线程同时访问临界资源的问题。但是实际中可并不只这一种问题,难道需要我们在每次出现问题的时候都要去针对一种问题想办法吗?这当然是不行的,太麻烦。
所以事务被设计出来,是为了当应用层访问数据库时,事务能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题,当我们使用事务的时候,要么操作成功,那就提交,要么不成功那就回滚,我们并不需要考虑网络异常、服务宕机或者多个客户端同时修改一个数据出问题怎么办。就像银行转账,转账成功则该账户金额减少,对方增加,失败则回滚,该账户和对方金额都不变。
所以说,事务本质上时为了应用层服务的,并不是伴随着数据库系统天生就存在的。
2、什么是事务?
事务就是由一组DML(数据操纵语言,用于检索和操作数据)语句组成,这些语句在逻辑上呈相关性,这一组DML语句要么全部成功要么全部失败,这是一个整体。MySQL提供了一种机制,保证我们达到了上述的效果。且事务还规定了不同的客户端看到的数据是不同的。
事务主要用于处理操作量大,复杂度高的数据。这通常需要多条的MySQL语句构成,如公司的员工离职后,需要从公司的数据库中删除掉该员工的信息,包括姓名电话地址还有曾经在公司获得的职称啊等等,这就需要不止一条MySQL语句,所有的这些操作合起来就构成了一个事务。
且正如上面所说,一个MySQL数据库,在同一时间可不止一个事务在运行,同一时刻甚至会有大量的请求被包装成事务在向MySQL服务器发起事务处理请求,而每一个事务至少一条sql语句,如果大家都访问同样的表数据,且不加以保护,那么就会出现问题,所以一个事务不仅仅是简单的sql语句组合,还需要满足下面四个属性,简称ACID:
- 原子性(Atomicity,或称不可分割性):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样
- 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作
- 隔离性(Isolation,又称独立性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交( Read uncommitted )、读提交( read committed )、可重复读( repeatable read )和串行化( Serializable )
- 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
3、事务的版本支持
需要注意的是在MySQL中,只有使用了 Innodb 数据库引擎的数据库或表才支持事务, MyISAM是不支持的。
4、事务的提交方式
事务的提交方式有两种:
- 手动提交
- 自动提交
5、事务常见操作方式
为了方便接下来的演示,我们先需要对MySQL进行设置,具体为什么这么设置,我们到后面会针对这部分进行详解。
先建表
下面考虑自动提交的情况,观察现象是否对事务产生影响。
1、演示回滚
可以看到在设置了保存点的情况下,如果我们使用 rollback 命令回滚,则是直接回滚到初始点,如果想要回滚到指定的保存点,则后面需要跟上设置的保存点。
2、演示在事务中没有手动提交(commit)的情况下,模拟客户端崩溃,MySQL会自动回滚
可以看到客户端A在遭遇异常崩溃之后,原来在一个事务中写入表中的数据也不会保存,即自动发生了回滚。
3、演示在事务中手动提交(commit)的情况下,模拟客户端崩溃,MySQL数据不会受影响,已经持久化
可以看到在客户端A提交之后,后面再再出现异常终止也不会影响数据了,这就是持久化。
4、在禁止自动提交后,看客户端A的异常崩溃怎么影响表中的数据。
可以看到在禁止自动提交后,客户端A的异常崩溃依然会导致回滚。
5、再验证单条sql语句和自动提交与禁止自动提交之间的关系。
先在自动提交开启后看客户端A异常终止对数据的影响。
可以看到没有回滚。
再看禁止自动提交后是什么效果
可以看到在禁止自动提交后,这里发生了回滚。
总结
- 只要输入begin或者start transaction,事务便必须要通过commit提交,才会持久化,与是否设置set autocommit无关。
- 事务可以手动回滚,同时,当操作异常,MySQL会自动回滚
- 对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。(select有特殊情况,因为MySQL 有 MVCC )
- 从上面的例子,我们能看到事务本身的原子性(回滚),持久性(commit)
感谢阅读!