MySQL 学习心得和知识总结(四)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43949535/article/details/102536321

事务的基本概念

一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元。且MySQL 事务主要是用来处理操作量大,复杂度高的数据操作。

只有当事务中的所有操作都正常执行完了,整个事务才会被提交给数据库;如果有部分事务处理失败,那么事务就要回退到最初的状态,因此,事务中的任何一个SQL要么全部执行成功,要么全部失败。事务执行过程中,有的SQL出现错误,那么事务必须要回滚(rollback)到最初的状态。

  1. 事务是一组SQL语句的执行,要求全部成功 要么全部失败。不能出现部分成功,部分失败。就是要保证事务执行的原子操作。也即:事务 是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
  2. 事务所有的SQL全部执行完成之后,才能提交(commit)事务,将结果写入到磁盘。一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成。
  3. 在SQL执行的过程中,有SQL出现错误,那么事务就得回滚(rollback)到初始状态。事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
  4. 事务仅仅与DML语句有关,也可以说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数也就不同。

如下:

  1. update 变量a:5—》8
  2. 设置保存点 savepoint f1;
  3. select
  4. update a:8—》7

分析上面的过程:假如3 4出现了错误,此时回滚到f1的状态而非最初状态。若是没有这个保存点,则回滚到初始状态。

事务的ACID特性

每一个事务都必须得满足下面的4个特性:

A

原子性:Atomicity. 事务是一个不可分割的整体,事务必须具有原子特性。也就是说事务包含的所有操作要么全部成功,要么全部失败,则回滚。因此事务的操作,若是成功就必须要完全应用到数据库;若是操作失败则不能对数据库有任何影响。尤其是当做数据修改的时候,要么全部成功,要么全部失败。

C

一致性:Consistency. 事务执行之前 之后,数据库数据必须要保证一致性的状态。数据库的一致性状态需要用户来负责的,由并发控制机制实现。例如:网上购物 商品的加入购物车的这个操作:商品的库存减少和商品加入购物车是一个完整性的事务。并发操作 若是用户控制不当的话,就会引起数据的不一致性问题:脏读、不可重复读和幻读(虚读) 等。

I

隔离性:Isolation. 当多个事务在进行并发操作时,事务A执行的结果对事务B的执行过程中是不可见的。要么是A先执行完,B才开始执行;要么是B执行完了才可以看到A的结果;同时执行过程中,是不能够看到的。

或者说:当两个或者多个事务并发执行时,为了保证数据的安全性,将一个事物内部的操作与其它事务的操作隔离起来,不被其它正在执行的事务所看到。例如对任何一对事务A和B,对A而言,B要么在A开始之前已经结束,要么在A完成之后再开始执行。隔离性使得每个事务的更新在它被提交之前,对其它事务都是不可见的。 隔离性是由其不同的级别所确定的:未提交读、已提交读、可重复读、串行化。级别越高,就能够满足 在操作过程中 无法读到的;隔离级别低的话,就可能在提交任务之前 就能够读取到数据的。

扫描二维码关注公众号,回复: 7657272 查看本文章

事务的隔离级别的实现:通过底层的相关的锁:表锁 行锁来实现的。而表锁 行锁的实现:这个锁是作用于有索引的字段,加锁是对有索引的字段数据加锁。InNoDB提供的行锁 并不是真的在数据库上的这一行加锁,而是这个属性必须有索引的 否则,(没有索引是无法加锁的)只能退化成表锁(对整个表加锁)。

D

持久性:Durability. 事务完成之后,就会将数据库中的数据永久的保存在磁盘上。(在服务关闭之后,数据还是存在的)。也即:一个事务一旦完成了,那么DBMS 保证其对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

数据不一致问题

当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,事务在处理过程中,若是 事务处理不经隔离,前提:并发执行事务时通常会发生以下的问题:

脏读(Dirty Read)

一个事务读取了另一个事务未提交的数据。例如当 事务A和事务B并发执行时,当事务A更新后,事务B查询读取到A尚未提交的数据,此时事务A回滚,则事务B读到的数据就是无效的脏数据。(事务B在处理过程中 读取了事务A尚未提交的数据

不可重复读(NonRepeatable Read)

一个事务的操作导致另一个事务前后两次读取到不同的数据。例如当事务A和事务B并发执行时,当事务B查询读取数据后,然后事务A更新了事务B查询读取到的数据,此时事务B再次去读该数据,发现前后两次读的数据不一样。(事务B读取了事务A已提交的数据)(事务A更新,提交了;事务B还没有完成 还在执行过程中)

不可重复读和脏读的区别是:脏读是B事务读取了A事务未提交的脏数据,而不可重复读则是读取了A事务提交的数据。虽然说在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在也许其他的一些情况下就有可能发生问题。

虚读(Phantom Read)/幻读

一个事务的操作导致另一个事务前后两次查询结果的数据量不同。例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A新增或者删除了一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了。(事务B读取了事务A新增加的数据或者读不到事务A删除的数据) 例如事务A对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务B又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务A的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务B中添加的,就好像产生幻觉一样,这就是发生了幻读。

(不可重复读和幻读的两次读 是针对同一个事务的操作过程中,并发地读而又由于没有做好隔离,第一次读到的结果,因为当前的事务还在执行当中 另一个事务对它进行了修改,此时再去进行读的时候 和刚才的读是在同一个事务当中的 结果发现数据被改变掉了。)幻读和不可重复读都是读取了另一条已经提交的事务(这点与脏读不同),他俩的不同点在于:不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

事务的隔离级别

补充:由于多个线程会请求相同的数据,事务之间通常都会用锁互相隔离,由于数据库支持不同类型的锁,在JDBC编程中,Condition对象定义了5种 事务隔离级别:

  1. Transation_None:表示不支持事务
  2. Transation_Read_Uncommited:未提交读:说明在提交前 一个事务可以看到另一个事务的变化,这样读 “脏数据” (脏读、不可重复读、幻读都是存在的)
  3. Transation_Read_Commited:已提交读:读取未提交的数据是不行的(可以避免脏读,但是仍然允许 不可重复读和幻读)
  4. Transation_Repeatable_Read:可重复读:事务保证能够读取相同的数据而不会失败。(可以避免脏读、不可重复读,但是仍然允许 幻读)
  5. Transation_Serializable:可序列化/串行化:事务间串行处理,最高的事务级别(可以处理脏读 不可重复读 幻读)

注:事务的隔离级别越高,为避免冲突所消耗的性能也是比较高的。
在这里插入图片描述
当然,针对上面可能存在的问题,InnoDB 提供了四种不同级别的机制保证数据隔离性:未提交读、已提交读、可重复读、串行化。其中如上的那个Condition对象的,InnoDB 提供的这四种隔离级别最高的是Serializable串行化级别,最低的是未提交读级别。如下所示:在这里插入图片描述

MySQL中事务处理的命令

查看当前会话的数据库隔离级别

注:这是MySQL8下的
在这里插入图片描述
注:在MySQL中,支持上面四种隔离级别,默认的为Repeatable-read (可重复读);而在Oracle中,只支持Serializable (串行化)级别和Read-committed (已提交读)这两种级别,且默认的为Read-committed级别。

设置事务的隔离级别

在这里插入图片描述

  1. 设置数据库的隔离级别一定要是在开启事务之前进行的。
  2. 如上的设置不是持久地修改,也即隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言(一个窗口就相当于一个链接),当前窗口设置的隔离级别只对当前窗口中的事务有效。

查看MySQL是否自动提交事务

在这里插入图片描述
1代表的意思:表示自动提交事务(默认)
0代表的意思:手动

修改手动/自动提交事务:set autocommit =0;

begin;开启一个事务
commit;提交一个事务
rollback;回滚一个事务到初始位置
savepoint point1;设置一个名字为point1的保存点
rollback to point1;回滚到point1位置

事务的提交和回滚

上面也说过了,无论是哪一条DML语句的执行(包括:插入 删除 更新),都代表着这个事务的开启;且只有全部SQL的成功结束,将此过程中所有的DML语句执行记录和内存数据进行同步,才可以成为commit。而此过程中一旦有SQL执行失败,则整个事务失败,则将所有的DML语句执行记录删除(不可提交) 称为回滚。

换句话说:在事务的执行过程中 没有完成结束之前,任何的DML语句都不可以更新底层的数据信息,当然只有在成功结束之后 才会去更新底层的磁盘文件里的数据内容。如上图可见:MySQL数据库的事务默认是自动提交的(执行一条DML语句就开启一个事务,并且还是自动提交事务)。如下,我们操作一把:
背景如下:在这里插入图片描述
好了 把自动提交关掉:在这里插入图片描述
注:在MySQL上,默认存储引擎是InNoDB,只有当使用start transaction或者begin时,事务就不会自动提交了,也只有明确使用commit才可以进行提交(在这之前可以使用rollback进行回滚更新操作)。

注:此时我再打开一条MySQL命令窗口,称之为窗口2
在这里插入图片描述
如下所示:我已经关闭了窗口1的自动提交,但是没有使用start transaction或者begin 而是直接在窗口1里面insert一条数据,在窗口2 里面进行select。也使用rollback命令也回滚更新操作了,但是无效的。
在这里插入图片描述
而下面我要做的是:使用begin来开启事务,同时并向student表中插入一条记录,但是不进行commit提交(只有明确执行了COMMIT命令后才会被提交),而是rollback
在这里插入图片描述
进行了rollback:在这里插入图片描述
rollback之后的情景:在这里插入图片描述
如上 rollback了,新的数据没有被插入表中。
¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
我接下来要做的是:使用start transaction开启事务,并插入数据,使用commit进行提交

插入数据了,在窗口2里面查询没有更新:
在这里插入图片描述
使用commit提交事务
在这里插入图片描述
此时在窗口2和1里面查询到更新。

此时我做的实验:在窗口1里面rollback,发现窗口2和1里面的数据都是查询到新的,没有回滚。在这里插入图片描述
如上的事务的隔离性:是通过锁机制得以实现的,不同于MyISAM使用表锁,InnoDB采用更细粒度的行锁,提高了数据表的性能。InnoDB的锁通过锁定索引来实现,若是查询条件中有主键则锁定主键,如果有索引则先锁定对应索引然后再锁定对应的主键(可能造成死锁),如果连索引都没有则会锁定整个数据表。

猜你喜欢

转载自blog.csdn.net/weixin_43949535/article/details/102536321