事务

image.png

ACID

  1. 原子性

事务被视为不可分割的最小单元,一个事务里的操作要么全部完成要么全部回滚。 原子性可以用undo 日志来实现,undo 日志记录所有修改操作,回滚的时候反向执行。

  1. 一致性

数据库在事务执行前后保持一致性。一致性状态下,所有的事务对数据的读取结果都是相同的。
或者说事务从一个状态到另一个状态一定是一致的,不能出现 A 向 B 转账,A扣了钱,但是B没有收到

  1. 隔离性

事务所做的修改,在最终提交以前,对其他事务都是不可见的。

  1. 持久性

事务一旦提交,所做的修改将永远保存在数据库中, 即使系统发生崩溃,数据也不会丢失。可以通过数据库的备份和恢复来实现。

  • 只有满足一致性, 事务的执行结果才是正确的
  • 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。
  • 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
  • 事务满足持久化是为了能应对数据库崩溃的情况。

注: Mysql 默认采用autocommit 自动提交模式,如果不显示调用START TRANSACTION 语句来执行一个事务,那么所有的操作都会被当作一个事务自动提交

并发一致性问题

修改丢失

一个事务的修改被另一个事务所覆盖
image.png

赃读

T2 读取了 T1 修改过,但是为提交或者回滚的数据
image.png

不可重复读

T2 读取一个数据,T1 修改了数据,T2 再次读取数据,两次读取数据不一致
image.png

幻读

T2 读取某个范围的数据,T1 在这个范围内再插入或删除数据,数据量发生变化
image.png

并发不一致是因为破坏了事务的隔离型,解决方法是通过并发控制来保证隔离型。 用户可以通过锁来实现,数据库提供了事务的隔离级别来实现

锁的粒度

Mysql 支持行锁和表锁。 锁的粒度越小,并发量越大,性能开销越大(获取锁,释放锁,检查锁的状态)。
都会增加性能开销。

数据库锁的类型

读写锁

  1. 排它锁:简称为X锁,又称为写锁
  2. 共享锁:S锁,又称为读锁

锁的兼容关系如下:
截屏2022-01-17 下午4.24.04.png

意向锁

在行锁和表锁的情况下,事务T 如果要对表A加X锁,就要先判断是否有事务对表A加了X表锁或X行锁, 需要遍历表A的每一行,效率低。
引入IX, IS, 事务T要对表A加锁,只需检测是否有其他事务对表A加了IX/IS锁,如果有,则加锁失败,效率更高。

  • 一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁;
  • 一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁。

截屏2022-01-17 下午4.39.12.png

  • 任意 IS/IX 锁之间都是兼容的,因为它们只是表示想要对表加锁,而不是真正加锁;
  • S 锁只与 S 锁和 IS 锁兼容,也就是说事务 T 想要对数据行加 S 锁,其它事务可以已经获得对表或者表中的行的 S 锁。

封锁协议

三级锁协议

一级锁

事务T要修改表A时必须加X锁,直到T结束才能释放锁。 解决修改丢失问题,因为不能有两个事务同时修改数据,那么事务的修改就不会被覆盖
截屏2022-01-17 下午6.27.27.png

二级锁协议

在一级的基础上,要求读数据时必须加S锁,读完马上释放S锁。 因为事务T修改表A时,已经加了X锁,就不能加S锁了,所以不会出现赃读
截屏2022-01-17 下午6.33.44.png

三级锁协议

读取表A时加上S锁,直到事务结束才能释放S锁。解决不可重复读问题。因为读表A的时候,其他表不能对A加X锁,从而避免了数据修改。
截屏2022-01-17 下午6.38.27.png

事务隔离级别

  1. 读未提交 : 事务中的修改,即使没有提交,对其它事务也是可见的
  2. 读已提交: 一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的
  3. 可重复读: 保证在同一个事务中多次读取同样数据的结果是一样的。
  4. 串行化: 强制事务串行执行

截屏2022-01-17 下午7.09.15.png

数据库三大范式

各种范式是条件递增的关系,

  1. 第一范式:每个列都不可拆分,强调列的原子性,第一范式要求数据库中的表都是二维的
  2. 第二范式:在第一范式的基础上,要求一个表必须要有一个主健,非主键列只依赖于主键,而不能依赖于主键的一部分。
  3. 第三范式L:非主键列只依赖于主键,不能依赖于其他非主键

    数据库连接泄漏的含义

    数据库连接泄露指的是如果在某次使用或者某段程序中没有正确地关闭 Connection、Statement 和 ResultSet 资源,那么每次执行都会留下一些没有关闭的连接,这些连接失去了引用而不能得到重新使用,因此就造成了数据库连接的泄漏。数据库连接的资源是宝贵而且是有限的,如果在某段使用频率很高的代码中出现这种泄漏,那么数据库连接资源将被耗尽,影响系统的正常运转。
    解决方法可以是黑名单机制,单位时间内请求次数限制,超出某个值就做服务熔断或者服务降级。

    什么是触发器

    触发器(trigger)是与表相关的数据库对象,是用户定义在关系表上的一类由事件驱动的特殊的存储过程,在满足定义条件时触发,并执行触发器中定义的 语句集合。触发器的这种特性可以协助应用在数据库端确保 数据库的完整性。

使用场景

  1. 可以通过数据库中的相关表实现级联更改
  2. 实时监控某张表某个字段的更改,并作出相应的处理