Mysql进阶之事务原理

Mysql进阶之事务原理

前言

事务是Mysql Innodb的核心部分,特别是在可重复读这个级别中对待事务的一些机制,让我觉得一个中间件设计的如此精妙,也是我以前对于Mysql的一些积累,然后最近拜读了一些文章,自我的推理以及AI等相关的论证,此篇文章仅记录我对Mysql事务的个人理解。

四大特性

年数越久越会忘记一些上古时期的知识点。。。。

  • 原子性
    在一个事务中,所有操作要么一起成功要么一起失败回滚。
  • 持久性
    事务一旦提交数据将永久保存,数据不能因为机器故障等情况收到变更。
  • 一致性
    一致性与原子性息息相关,当数据的状态改变成另一种状态时的数据完整性要求,比如A账号有金额100,A要转帐给B50,B的账号原本50,转账成功后A只有50了,转账失败则A还是100。这系列操作是原子性操作,也遵循了数据一致性要求。
  • 隔离性
    事务与事务之间应当相互独立互不受干扰。

MVCC

一致性视图是在执行select时就会生成,这个一致性视图包含了活跃事务id数组、最小活跃事务、当前事务、下一个事务ID(类似于预分配下一个事务的ID,例如当前事务的ID为1,那么下一个事务的ID就为2)。

版本链每一条数据都会有两个隐藏的字段,一个是事务id,另一个是数据指针,这个指针指向undo log该数据上一个版本的数据。

MVCC本质是有一个活跃的事务列表(一致性视图)以及版本链,在事务中的第一个查询语句执行时生成一致性视图,查询到的数据会与一致性视图进行比对,如果数据对应的版本ID小于一致性视图中最小事务ID(注意:数据版本ID和事务ID是同一个东西)那么就会返回这条数据,如果在活跃列表里面说明你读到了一条其他事务未提交的数据,那么就会顺着版本链去读undolog数据。

什么是当前读什么是快照读

当前读是一种突破隔离性以及一致性的一种机制,它不再去对比一致性视图,而是直接对比Mysql的全局活跃事务表,在某种情况下不管是快照读或者是当前读,都会读到最新的事务提交数据,比如我A事务在查询id等于1的数据,然后我阻塞,然后去读id为2的数据,但是在执行查询id为1的数据的时候其实id为2的数据是不存在的,它是在阻塞时期产生的,这个id为2的数据已经提交了,在第二次查询的时候,首先会去判断缓存池中是否存在数据,若存在则直接返回,但是第一个查询时所生成的一致性视图压根没有这个事务id,也就是说对于一致性视图而言id为2的数据是来自未来的数据,那么在MVCC机制中会按照版本链来查询,实际id为2没有历史数据那就是返回空,但绝对不会返回这个来自未来的数据,这就是快照读,它是读取历史数据,历史的界定是在第一次查询时生成的一致性视图,但是在很多场景下要求强一致性的需求,那就需要去突破这个隔离性,那就需要进行当前读,在select的语句后增加 for update,这是一个当前读的操作且是会进行加锁的。

锁分为全局锁、表锁、行锁,全局锁不在本文介绍,表锁可分为意向排它、意向共享、排它、共享锁,行锁分为记录锁、间隙锁、临键锁。

意向锁的本质是意向锁是表级锁,作为明示表中有没有数据行加排它锁或者共享锁的存在。

共享锁与排它锁的本质是共享锁允许所有读的操作,但是其他事务不能写,然而排它锁是其他事务不能读也不能写。直白来说不同事务共享锁与排它锁还互斥的,但是要注意的是同一个事务中加了共享锁然后去修改数据,innodb会再加一把排它锁,但是同一个事务的锁是不会互斥的。

行锁

正常的如下sql就是使用的记录锁,记录锁是在索引字段且索引值存在的时候才会为记录锁

sql 复制代码
-- 当前数据为
user_id  name   age
1        李四    18
sql 复制代码
UPDATE t_user 
SET name = '张三' 
WHERE user_id = '1';

当索引字段对应的值不存在时则是采用间隙锁,间隙锁锁住的是一个间隙,那么间隙肯定是有上有下或者有左有右,中间才是间隙,如下数据Id为1~5那么就可以理解为上下界就是1和5,间隙锁的意义在于我锁住这个间隙,不允许你们做插入,也就是说如果我锁住1到5之间的间隙之后,是不允许插入id为2、3、4的数据,这就是间隙锁,那间隙锁怎么触发的呢,如下sql条件为4,明显这个数据不存在,Mysql在这个过程中它会进行加锁,如果修改时数据不存在,不应该修改新增的数据。

sql 复制代码
-- 当前数据为
user_id  name   age
1        李四    18
5        王五    18
sql 复制代码
UPDATE t_user 
SET name = '张三' 
WHERE user_id = '4';

那什么是临键锁呢,其实有点类似记录锁+间隙锁,具体数据如下,已知user_id肯定是有主键索引的,但是age其实并没有添加唯一索引,在非唯一与非主键索引的索引字段做等值、范围的条件 就会加临键锁,如下第一条sql加锁的范围就是(18,28] 左开右闭,什么是左开有闭呢,其他事务执行修改时18这个数据本身是可以修改的,但是28是不能修改的,其中19、20、21、22~~~也是不能插入的,28不能修改也就是体现为什么说临键锁类似记录锁+间隙锁,那是因为28这个界是不能修改。

sql 复制代码
-- 当前数据为
user_id  name   age
1        李四    18
5        王五    28
sql 复制代码
UPDATE t_user 
SET name = '张三' 
WHERE age= '19';

注意:

1、innodb加锁是针对索引的,如果没有那就是表级锁排它,所以一定要通过唯一或主键索引来进行数据修改等操作。

2、间隙锁与临键锁,下界上见是有♾️的概念的 如果数据是4、5,如果条件为3,上界就是无穷下界是4。

贯通与结语

那讲了MVCC与锁,我们了解了这些概念都是一个怎样的效果,那么再去了解事务的是怎么回事,或者说事务是怎样的一个内在,事务的内在就是四大特性,那么是谁在为这四大特性赋能呢。

我们在一个事务中执行查询语句的时候会生成一个一致性视图与版本链,查询数据的时候首先会查询缓存池,如果缓存池不存在数据则去查询磁盘,查到数据后会将数据存储到缓存池中,事务系统会根据当前事务上下文中获取这个事务的一致性视图进行比较,如果是已提交的数据且是小于最小事务Id的数据就会返回,如果不是则会查询undolog进行返回,这是一个正常查询的流程,保证了在查询时的数据隔离性以及同一个事务查询同样的数据的一致性。

Mysql中增删改查都是直接操作缓存池的数据,并不是直接操作磁盘,就算缓存池没有数据也会从磁盘加载到缓存池里然后进行操作,操作前会把历史数据存入到undo里,然后进行数据操作,此刻事务是待提交状态,会生成一个redolog对象,状态也是待提交状态,事务提交之后,会把这部分数据存储到binlog日志里,然后把redolog的数据变更为已提交状态,当然在这个过程中是有加锁的逻辑的,上述说到会把数据加载到缓存池中,新增也好修改也好,都会去判断是否能竞争到锁,我认为的是如果缓存没有这个数据,那么修改这个场景下肯定是无锁状态的(Mysql是非公平锁,他是依据此次操作的复杂度来定的),从三大行锁的角度来讲被锁的记录肯定是在缓存中的,新增就不一定了,undolog与redolog保证了数据持久性,而事务本身开启提交、回滚以及行锁保证了操作的原子性。

至此以上就是我个人的一些理解,可能我有些地方讲诉的不是那么准确,也希望各位道友能指出我的不足。

相关推荐
IvorySQL20 分钟前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·29 分钟前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德32 分钟前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫1 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i1 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.1 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn1 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露2 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星2 小时前
sql语言之分组语句group by
java·数据库·sql
符哥20082 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql