写点什么

详解 MySQL 之事务

作者:C++后台开发
  • 2022 年 10 月 08 日
    湖南
  • 本文字数:3628 字

    阅读完需:约 12 分钟

详解MySQL之事务

事务

  事务的组成可由一条非常简单的 SQL 语句组成,也可由一组复杂的 SQL 语句组成。

特征

  事务具有以下特征:  

  (1)在数据提交时,可以确保要么所有修改都已保存,要么所有修改不保存;

  (2)事务是访问并更新数据库各种数据项的一个执行单元;

  (3)在 innodb 下,每一条语句都是事务,可以通过 set autocommit = 0 (默认值 1,不需要手动添加开始事务和提交语句),设置当前会话手动提交,一般需要执行多条语句的时候,就会显式地开始事务。

指令

        -- 显示开启事务         BEGIN        -- 提交事务,并使得已对数据库做的所有修改持久化        COMMIT        -- 回滚事务,结束用户的事务,并撤销正在进行的所有未提交的修改        ROLLBACK        -- 创建一个保存点,一个事务可以有多个保存点        SAVEPOINT identifier        -- 删除一个保存点        RELEASE SAVEPOINT identifier        -- 事务回滚到保存点        ROLLBACK TO [SAVEPOINT] identifier
复制代码

事务的 ACID 特性

  (1)原子性

    a. 事务要么都做(提交),要么都不做(回滚);

    b.事务是访问并更新数据库各种数据的执行单元,执行单元是不可分割的单位;

    c.通过 undolog 来实现回滚(语句的操作都会记录在 undolog 中),当回滚时,回放事务具体操作的逆运算

  (2)隔离性

    a.mysql 在处理每一个连接的请求是并发的,所以需要隔离性;

    b.事务的隔离性要求每个读写事务对象对其他事务的操作对象能互相分离,也就是事务提交前对其他事务不可见

    c.通过 MVCC 和锁来实现,MVCC 是多版本并发控制,主要解决一致性非锁定读,通过记录和获取行版本,而不是通过锁来限制读操作,从而实现高并发读性能;

    d.通过锁来处理数据库的 DML 操作,数据库提供了针对表(聚集索引 B+树)、页(聚集索引 B+树的叶子节点)、行(叶子节点当中某一段记录行)等三种粒度加锁

  (3)持久性

    a.事务提交后,事务 DML 操作将会持久化(写入 redolog 磁盘文件,那一页,页偏移值,具体数据);

    b.即使发生宕机等故障,数据库也能将数据恢复,redolog 记录着物理日志。

  (4)一致性

    一致性指事务将数据库从一种一致性状态转变为下一种一致性的状态,在事务执行前后,数据库完整性约束没有被破坏。

  MYSQL 的 DML 操作整个流程,首先修改内存的数据,同时写到 redolog(这里只有一次磁盘 IO),另外有一个 master 线程,异步地将内存中的脏数据(内存与磁盘不一致的数据)写到聚集索引 B+树中(.idb 文件)。

事务并发异常

  (1)脏读

    事务 A 可以读到另一个事务 B 中未提交的数据,也就是事务 A 读取到脏数据

    现象:读到未提交数据

    原因:read uncommitted 读未加锁,也没有 MVCC

    解决:添加 MVCC 持支

  (2)不可重复读

    事务 A 可以读到事务 B 中提交的数据,通常发生在一个事务中两次读到的数据是不一样的情况;

    现象:读到其他事务提交的数据,造成两次读结果不一致;

    原因:read committed 隔离级别下 MVCC,读取最新的历史版本数据;

    解决:修改 MVCC 读取定义,读取事务开始前的历史版本数据;

  (3)幻读

    事务中一次读操作不能支撑接下来的业务逻辑,通常发生在一个事务中,一次读判断接着写操作失败的情况;

    现象:一个事务中某次读操作不能作为接下来业务逻辑的数据;

    原因:repeatable read 隔离级别下,读操作使用 MVCC,读操作未加锁,从而能进行写操作;

    解决:手动给读操作加 S 锁或者 X 锁,通过 next-key lock(间隙锁)锁住读取范围,避免范围内加入数据

C/C++Linux服务器开发高级架构师/C++后台开发架构师​免费学习地址

另外还整理一些 C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以自行添加:Q群:720209036 点击加入~ 群文件共享


隔离级别 

  (1)READ UNCOMMITTED

    读:读未提交,不做任何处理

    写:自动加 X 锁

  (2)READ COMMITTED

    读:MVCC,读已提交,读最新版本的数据

    写:自动加 X 锁

  (3)REPEATABLE READ

    读:MVCC,可重复读,读事务开始前版本的行数据

    写:自动加 X 锁

  (4)SERIALIZABLE

    可串行化,该级别下给读加了共享锁,所以事务都是串行化的执行,此时隔离级别最苛刻,使用场景在分布式数据库;

    读:自动加 S 锁(next-key lock 间隙锁)

    写:自动加 X 锁

  以上隔离级别中,写操作都会自动加 X 锁,对于读操作,除 SERIALIZABLE 会自动加 S 锁外,其他的隔离级别不做任何处理;所以根据不同的业务来使用隔离级别,在正确性与性能间做个抉择。

MVCC

  多版本并发控制,用来实现一致性的非锁定读,非锁定读是指不需要等待访问的行上 X 锁的释放;在 read committed 隔离级别下,对于快照数据总是读取最新的一份快照数据;在 repeatable read 隔离级别下,对于快照数据总是读取事务开始前的数据版本。

  思考:为什么读取快照数据不需要上锁?

    因为没有事务需要对历史的数据进行修改操作。

  锁的机制用于管理共享资源的并发访问,用来实现事务的隔离级别,也就是说事务的隔离性是通过锁实现的,在事务中锁都是在提交或者回滚后才释放。

  (1)共享锁(S)

    相当于读锁,是个行锁,事务读操作加锁,对某一行加锁

    a.在 READ UNCOMMITTED 隔离级别下,既没有加锁也没有使用 MVCC;    b.在 READ COMMITTED 隔离级别下,没必要加共享锁,采用的是 MVCC,加锁也不能解决幻读问题;    c.在 REPEATABLE READ 隔离级别下,需手动加共享锁,可解决幻读问题,因为加的实际是一个间隙锁,把某个范围锁住了;    d.在 SERIALIZABLE 隔离级别下,默认帮读操作加共享锁。

  (2)排他锁(X)

    相当于写锁,是个行锁,事务删除或更新加的锁,对某一行加锁;在四种隔离级别下,都添加排他锁,事务提交或者回滚后释放。

  (3)意向(IS)共享锁

    对一张表某几行加的共享锁。

  (4)意向(IX)排他锁

    对一张表某几行加的排他锁。

  当想为某一行添加 S 锁,先自动为所在的页和表添加意向共享锁(IS),再为该行添加 S 锁;

  当想为某一行添加 X 锁,先自动为所在的页和表添加意向排他锁(IX),再为该行添加 X 锁;

  表级锁用户是不能更改的,因为用户在为某一行添加共享锁、排他锁时,自动在页和表上添加相关的意向共享锁,或意向排他锁,再为修改行添加共享锁或排他锁,所以表级锁、意向锁不是由程序员控制的,而是自动添加的。

  行级锁是针对表的索引加锁,这里的索引包括聚集索引和辅助索引,表级锁是针对页或表进行加锁。

锁的算法

  (1)Record Lock

    记录锁,单个行记录上的锁;

  (2)Gap Lock

    间隙锁,锁一定的范围,但不包含记录本身,全开区间;REPEATABLE READ 级别及以上持支间隙锁;如果 REPEATABLE READ 修改 innodb_locks_unsafe_for_binlog = 0,那么隔离级别相当于退化为 READ COMMITED;

  (3)Next-Key-Lock

    记录锁+间隙锁,锁定一个范围,并且锁住记录本身,左开右闭

  (4)Inster Intention Lock

    插入意向锁,inster 操作的时候产生,在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。

    例如:假设有一个记录索引包含键值 4 和 7,两个不同的事务分别插入 5 和 6,每个事务都会产生一个加在 4-7 之间的插入意向锁,获取在插入行上的排他锁,但是不会被互相锁住,因为数据行并不冲突。

  (5)AUTO_INC Lock

    自增锁,是一种特殊的表级锁,发生在 AUTO_INCREMENT 约束下的插入操作;采用一种特殊的表锁机制,完成对自增长值插入的 SQL 语句后立即释放;在有大量数据插入时,会影响插入性能,因为另外一个事务中的插入会被阻塞(锁);从 MYSQL5.12 开始,提供了一种轻量级互斥量的自增长实现机制,该机制提高了自增从值得插入的性能。

redolog(重做日志)

  redo 日志用来实现事务的持久性,内存中包含 redo log buffer,磁盘中包含 redo log file;当事务提交时,必须将该事务的所有日志写入到重做日志文件进行持久化,待事务的 commit 操作完成才完成事务提交;redo log 顺序写,记录的是对每个页的修改(页、页偏移量、以及修改的内容);在数据库运行时不需要对 redo log 的文件进行读取操作,只有发生宕机的时候,才会拿 redo log 进行恢复。

undolog(回滚日志)

  undo 日志用来帮助事务回滚以及 MVCC 的功能,存储在共享表空间中;undo log 是逻辑日志,回滚时将数据库逻辑地恢复到原来的样子,根据 undo log 的记录,做之前的逆运算;比如事务中有 inster 操作,那么执行 delete 操作,对于 update 操作执行相反的 update 操作;同时 undo log 日志记录行的版本信息,用于处理 MVCC 功能。

  以上的两种日志应区别于 binlog(二进制日志),binlog 用于复制,在主从复制中,从库利用主库上的 binlog 进行重播。

原文地址:MySQL之事务 - MrJuJu - 博客园

用户头像

C/C++后台开发技术交流qun:720209036 2022.05.06 加入

还未添加个人简介

评论

发布
暂无评论
详解MySQL之事务_MySQL_C++后台开发_InfoQ写作社区