75-MySQL

75-MySQL,第1张

1、事务核心概念

MyISAM不支持事务(每一个SQL自动就提交了,不能做转账这种多条SQL组成的业务啊)!
InnoDB支持事务,支持行锁 !(InnoDB最大的2个特点)

事务的概念

  • 一个事务是由一条或者多条对数据库 *** 作的SQL语句所组成的一个不可分割的单元;
  • 只有当事务中的所有 *** 作都正常执行完了,整个事务才会被提交给数据库;
  • 如果有部分事务处理失败,那么事务就要回退到最初的状态,因此,事务要么全部执行成功,要么全部失败。

有的业务需要1条以上的SQL语句共同完成,只有这些SQL都成功了才算业务成功了。

类似于银行的转账,商品的入库出库的场景:

转账业务由2条SQL组成。 没有一部分成功一部分失败这一说。

begin开启事务,如果这2句SQL都成功了,那么commit提交一个事务。

事务的三个状态:

  • 开启事务(begin):所有sql语句执行成功了;
  • 提交事务(commit):提交事务,表示事务生效了;
  • 回滚事务(rollback):数据恢复到事务开始前的状态。

  • 图中的是捕捉任何类型的异常。
  • 如果其中任意一句SQL由于突然停电,或者系统出错,执行出错了,那么事务就没有提交,事务就回滚,回滚就是数据恢复到事务开始前的状态。

这是存储引擎来保证的(redo log 和undo log保证的)。

mysql的默认引擎是innoDB;

  • 默认是自动提交。
  • 改成 0 ,手动提交事务,在代码上进行控制,做业务,业务都成功,提交1个事务,如果业务中间出现失败,就回滚1个事务

    所以记住事务的几个基本概念,如下:
  • 事务是一组SQL语句的执行,要么全部成功,要么全部失败,不能出现部分成功,部分失败的结果。保证事务执行的原子 *** 作。
  • 事务的所有SQL语句全部执行成功,才能提交(commit)事务,把结果写回磁盘上。
  • 事务执行过程中,有的SQL出现错误,那么事务必须要回滚(rollback)到最初的状态(事务开始前的状态)
2、ACID特性

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

事务的原子性(Atomic):

  • 事务是一个不可分割的整体,事务必须具有原子特性,及当数据修改时,要么全执行,要么全不执行,即不允许事务部分的完成。

事务的一致性(Consistency):

  • 一个事务执行之前和执行之后,数据库数据必须保持一致性状态。数据库的一致性状态必须由用户来负责,由并发控制机制实现。
  • 就拿网上购物来说,你只有让商品出库,又让商品进入顾客的购物车才能构成一个完整的事务。

事务的隔离性(Isolation):

  • 当两个或者多个事务并发执行时,为了保证数据的安全性,将一个事物内部的 *** 作与其它事务的 *** 作隔离起来,不被其它正在执行的事务所看到,使得并发执行的各个事务之间不能互相影响。
  • 隔离级别:数据的安全性和事务的并发性。
  • 隔离越严格,安全性越高,并发性越低。

事务的持久性(Durability):

  • 事务完成(即事务commit成功)以后,DBMS保证它对数据库中的数据的修改是永久性的,即使数据库因为故障出错,也应该能够恢复数据!
  • (DB写数据都是先在cache缓存上写的,因为速度快,然后 *** 作系统通过磁盘I/O往磁盘上写,当事务提交后,cache往磁盘上提交数据是要花时间的,这个花时间的过程中如果停电了,或者宕机了,或者自动重启了,那么此时数据就丢了,commit通知应用事务提交成功了,用户理所应当认为该修改的数据都修改成功了,但是由于不可控因素-硬件问题,导致缓存上的数据向磁盘上写的时候没写完。因为commit不是同步 *** 作,commit只要提交事务成功就返回了,并不会等着缓存上的数据向磁盘全部写完才返回。因为我们用户会写很多很多的数据,commit是不会等着这些数据从缓存全部写到磁盘,毕竟要经过磁盘I/O,业务上不可能让commit去等那么长时间。)

但是MySQL的redo log重做日志机制,进行数据恢复,可以保证数据库的永久性,还有undo log回滚日志)

所以说,MySQL最重要的是日志,不是数据!

3、事务并发存在的问题—脏读&不可重复读&幻读

事务处理如果不经隔离,并发执行事务时通常会发生以下的问题:

1、脏读(Dirty Read):

  • 一个事务读取了另一个事务未提交的数据。
  • 例如当事务A和事务B并发执行时,当事务A更新后,事务B查询读取到A尚未提交的数据,此时如果事务A回滚了,则事务B读到的数据就是无效的脏数据。
  • (事务B读取了事务A尚未提交的数据)
  • (脏读必须杜绝,其他下面的情况是事务已经提交了,不能算有问题,解不解决是看业务的需求!!!通过设置不同的隔离级别!!!)

2、不可重复读(NonRepeatable Read):

  • 一个事务的 *** 作导致另一个事务前后两次读取到不同的数据。
  • 例如 当事务A和事务B并发执行时,当事务B查询读取数据后,事务A更新 *** 作更改事务B查询到的数据,此时事务B再次去读该数据,发现前后两次读的数据不一样。
  • (事务B读取了事务A已提交的数据)
  • 不可重复读不能说是错的,正常现象,我们可以做到可以让它产生或者不让他产生,业务上能接受就行。

3、虚读(Phantom Read)幻读:

  • 一个事务的 *** 作导致另一个事务前后两次查询的结果数据量不同。
  • 例如 当事务A和事务B并发执行时,当事务B查询读取数据后,事务A新增或者删除了一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了。
  • (事务B读取了事务A新增加的数据或者读不到事务A删除的数据)
  • 幻读也不能说是错的,正常现象,我们可以做到可以让它产生或者不让他产生,业务上能接受就行。

脏读是事务B读到了A还没有提交的数据;
不可重复读和幻读,事务B读到的都是A已经提交的数据

4、事务的隔离级别(对事务并发执行的控制)

MySQL支持的四种隔离级别是:

1、TRANSACTION_READ_UNCOMMITTED。未提交读。

  • 说明在提交前一个事务可以看到另一个事务的变化。这样读脏数据,不可重复读和虚读都是被允许的。

2、TRANSACTION_READ_COMMITTED。已提交读(oracle默认的)。

  • 说明读取未提交的数据是不允许的。防止脏读。但是这个级别仍然允许不可重复读和虚读产生。

3、TRANSACTION_REPEATABLE_READ。可重复读。

  • 说明事务保证能够再次读取相同的数据而不会失败,即使其他的事务把这个数据改了,你也不会看到前后两次查询的数据的不同。但是虚读仍然会出现。

4、TRANSACTION_SERIALIZABLE。串行化,无并发。

  • 是最高的事务级别,它防止读脏数据,不可重复读和虚读。串行执行,相当于是单线程 *** 作。所以并发能力最低。

    备注:
  • 事务隔离级别越高,为避免冲突所花费的性能也就越多(表示效率就越低)。
  • 在“可重复读”级别,实际上可以解决部分的虚读问题,但是不能防止update更新产生的虚读问题,要禁止虚读产生,还是需要设置串行化隔离级别。
5、MySQL的事务处理命令

打开MySQL的Command命令行窗口,测试以下命令:
1、SELECT @@AUTOCOMMIT; 查看MySQL是否自动提交事务

  • 0表示手动提交事务,1表示自动提交事务;
  • 一般我们业务上如果要考虑到事务处理,我们就要设置事务提交方式为手动提交方式,否则的话一句SQL就表示自动提交成功了。

    这个set *** 作是和session有关,只是改变当前的session会话。

BEGIN; 开启一个事务
COMMIT; 提交一个事务
ROLLBACK; 回滚一个事务到初始的位置
SAVEPOINT point1; 设置一个名字为point1的保存点
ROLLBACK TO point1; 事务只回滚到保存点point1,而不是回滚到初始状态
SET TX_ISOLATION=‘REPEATABLE-READ’; 设置事务的隔离级别
SELECT @@ TX_ISOLATION; 查询事务的隔离级别

6、实践测试验证

开启2个mysql客户端:

将autocommit修改为0——不自动提交。(从上图可以看出autocommit不是全局的,按会话来的)

将两个会话均改为手动提交:

MySQL默认的隔离的级别是:可重复读(允许幻读,不允许脏读和不可重复读)

1、测试第1隔离级别(未提交读)

我们先设置最低的隔离级别:(TRANSACTION_READ_UNCOMMITTED。未提交读)

两个客户端全部修改:

脏读是会发生的!

左边客户端开启的为事务A,右边为事务B。

事务A还没有提交。
事务B此时再查看一下。

事务B第二次读,读出了事务A尚未提交的数据,zhangsan的年龄为21岁。
事务A可以回滚。

但是刚才事务B做业务呢,已经来不及了。

事务A回滚:
年龄还是20岁。

注意: 未提交读 隔离级别,脏读发生了,那么不可重复读和幻读肯定都发生了。(这里不演示了)

2、测试第2隔离级别(已提交读)


事务B看不到事务A未提交的数据,解决了脏读的问题;(看不到修改,这是由事务并发的MVCC版本控制)

现在事务A提交了,现在事务B再查看,可以看到事务A已提交的数据。

  • 这种现象就是不可重复读,两次读的数据不一样。

    这种隔离级别,脏读不会发生了,不可重复读发生了,幻读不用演示了,肯定会发生。
3、测试第3隔离级别(可重复读)

支持可重复读。

设置第3隔离级别;

  • 事务B先查询zhangsan的年龄为21岁;
  • 事务A将zhangsan的年龄修改为22岁;

    再次查看事务B zhangsan的年龄,脏读没有发生,还是21;

事务B看不到事务A提交后的数据。可以重复读!!(事务B读的都是自己原始的数据,只要自己没有改过,看的都是同样的数据)

在可重复读的隔离级别下,脏读和不可重复读问题都解决了,幻读解决不了。

测试幻读:

insert插入一个数据,发现并没有出现幻读的现象。

可重复读在一定意义上可以放置幻读的出现。

我们看到,第3隔离级别是可以防止insert,delete *** 作后的幻读。
但是不能防止update *** 作后的幻读

现在事务B update

出现幻读现象!

4、测试第4隔离级别(串行化)

彻底解决幻读问题

设置第4隔离级别:然后启动事务。

事务B正在读数据,相当于给数据加了一把读锁;

事务A要向表中写数据;

事务A线程直接被阻塞住了。串行化了(间隙锁)。

相当于给事务B加了读锁。
造成死锁了。


但是死锁是带有时间的。超时,就会返回错误,是业务上出现问题,所以是不会让MySQL-server永远阻塞,造成永远死锁的。

所以 *** 作串行执行,允许并发的读读,读写,写读都是不允许的但是其他情况都不允许。

串行化是由一把读写锁实现的。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/733852.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存