MySQL基础02-MySQL并发事务 & 锁

由 Geooo 发布于 February 22, 2022

事务特性(ACID)

原子性 Atomicity

一个事务的一些列操作都是原子性,要不都执行,要不都回滚。

一致性 Consistency

事务要保证数据库数据的完整性和业务数据的一致性,事务成功提交时则整体数据修改,事务错误则回滚到数据原来的状态。

隔离性 Isolation

事务与事务之间都是相互独立互不影响的,多个事务操作一个对象时会以串行等待的方式保证事务之间是隔离的。

持久性 Durability

一旦事务提交了就会把对事物的修改进行持久化,不会因为数据库宕机导致数据丢失

事务并发导致的问题

脏读

在事务并发时,一个事务可以读取到另一个正在进行的事务的数据,导致脏读问题 解决方法:将事务隔离级别置为 “读已提交”

不可重复读

一个事务内多次读取同一个数据集合,两次读取到的数据是不一样的,这种成为”不可重复读” 解决方法:将事务隔离级别置为 “可重复读”

幻读

在同一个事务下,连续执行两次相同的SQL语句可能导致不同的结果,第二次的SQL语句可能返回之前不存在的行,就好像凭空产生,这种叫幻读。 解决方法

  • 将事务隔离级别置为 “串行化”
  • 对于当前读:加 间隙锁 Gap Lock
  • 对于快照读:使用mvcc多版本控制协议进行读取,避免幻读的产生 (幻读的本质在于,没有对查询范围内的数据进行加锁,包括不存在的数据,从而导致其他事务可以在这个范围内进行数据插入,所以对表加上间隙锁,可以将这个范围的数据都锁上,其他事务要在这个范围添加数据的时候就会被阻塞,等待加锁事务完成再进行数据插入)

事务隔离级别

读未提交 (READ_UNCOMMITTED)

一个事务还没提交时,它做的变更就能被别的事务看到 解决问题:脏写 存在问题:脏读,不可重复度,幻读

读已提交(READ_COMMITTED)

一个事务提交之后,它做的变更才会被其他事务看到 解决问题:脏写,脏读 存在问题:不可重复度,幻读

可重复度(REPEATABLE_READ)

保证在同一个事务中多次读取同一数据的结果是一样的。

解决问题:脏写,脏读,不可重复读,幻读(MySQL的InnoDB才解决了幻读) 存在问题:幻读(如果是Mysql的innodb则不存在)。

SERIALIZABLE

不管读取还是修改所有的事务串行化执行,一个事务的执行必须等其他事务结束


🥸 面试官:讲一下数据库的四种隔离级别,以及具体的实现

😎 小牛肉:数据库的四种隔离级别主要是用来解决四种并发一致性问题的,隔离级别越高,能够处理的并发一致性问题越多,相应的数据库付出的性能代价也就越高。

最低的隔离级别是读取未提交,一个事务还没提交时,它做的变更就能被别的事务看到:可以解决丢失更新问题(所谓丢失更新问题,就是指一个事务的更新操作会被另一个事务的更新操作所覆盖);

然后是读取已提交,一个事务提交之后,它做的变更才会被其他事务看到:可以解决丢失更新和脏读问题(所谓脏读,就是一个事务读到了另外一个事务未提交的数据);

然后是 InnoDB 默认的隔离级别可重复读,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的:可以解决丢失更新、脏读和不可重复读问题(所谓不可重复读,就是指第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据是不一样的)。另外,InnoDB 的这个默认隔离级别,会通过 Next-Lock key 来解决幻读问题,所以其实是可以达到 SQL 标准的可串行化隔离级别的;

最后是可串行化,强制事务串行执行,对于同一行记录,“写” 会加 “写锁”,“读” 会加 “读锁”,当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。这样可以避免并发一致性问题,解决丢失更新、脏读、不可重复读和幻读问题(所谓幻读,和不可重复读差不多,不过幻读侧重于记录数量的增减,不可重复读侧重于记录的修改)

对于读取已提交和可重复读这两个隔离级别来说,其底层实现就是多版本并发控制 MVCC。

具体来说,对于这两个隔离级别,数据库会为每个事务创建一个视图 (ReadView),访问的时候以视图的逻辑结果为准。通过 undo log 版本链使得事务可以回滚到视图记录的状态。

而这两个隔离级别的区别就在于,它们生成 ReadView 的时机是不同的:

  • 在 “读取已提交” 隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的
  • 在 “可重复读” 隔离级别下,这个视图是在事务启动时就创建的,整个事务存在期间都用这个视图