MySQL数据库——事务

有错误的地方,欢迎大家评论留言指正!

一、事务的定义:

  1. 数据库事务可以包含一个或多个数据库操作,但这些操作构成一个逻辑上的整体。
  2. 构成逻辑整体的这些数据库操作,要么全部执行成功,要么全部不执行。
  3. 构成事务的所有操作,要么全都对数据库产生影响,要么全都不产生影响,即不管事务是否执行成功,数据库总能保持一致性状态。
  4. 以上即使在数据库出现故障以及并发事务存在的情况下依然成立。

二、事务的特征(ACID保证最终一致性):

  1. 原子性(Atomicity):事务是最小的执行单位,不允许分割。 整个事务中的所有操作要么全部提交成功,要么全部失败回滚;例如转账的这两个关键操作(将张三的余额减少200元,将李四的余额增加200元)要么全部完成,要么全部失败。undolog 保证原子性。
  2. 一致性(Consistency): 事务执行前后,数据库的状态应保持一致性。 确保从一个正确的状态转换到另外一个正确的状态,这就是一致性。例如转账业务中,将张三的余额减少200元,中间发生断电情况,李四的余额没有增加200元,这个就是不正确的状态,违反一致性。又比如表更新事务,一部分数据更新了,但一部分数据没有更新,这也是违反一致性的;
  3. 隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。 锁和 MVCC 保证隔离性。
  4. 持久性(Durability):一个事务被提交之后,对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。 redolog 保证持久性。

三、事务之间的相互影响:

  1. 脏读(Dirty Read):
    指一个事务读取了另一个事务未提交的数据。
    当事务T1正在访问字段 A 并且对进行了修改,而这种修改还没有提交到数据库中。这时另外一个事务 T2 也访问和使用字段 A,但由于事务 T1 修改字段 A 后还没有提交 COMMIT,而那么事务 T2 读到的字段 A 是“脏数据”。
  2. 第一类丢失更新:(脏写)
    撤销一个事务的时候,把其它事务已提交的更新数据覆盖了。
    这是完全没有事务隔离级别造成的。如果事务1被提交,另一个事务被撤销,那么会连同事务1所做的更新也被撤销。
  3. 第二类丢失更新:
    当两个或多个事务查询相同的记录,然后各自基于查询的结果更新记录时会造成第二类丢失更新问题。
    每个事务不知道其它事务的存在,最后一个事务对记录所做的更改将覆盖其它事务之前对该记录所做的更改。通过读加锁(for update)或者版本号解决。
  4. 不可重复读:(MySql默认隔离级别)
    不可重复读取是指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。
    如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的。原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。
  5. 幻读:
    指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致。
    事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录,就好像发生了幻觉一样。

不可重复读重点在于update和delete,而幻读的重点在于insert。避免不可重复读需要锁行(某一行在select操作时,不允许update与delete)就行,避免幻读则需要锁表或间隙锁。

四、事务隔离级别:

在MySQL数据库中,支持下面四种隔离级别,默认的为Repeatable read (可重复读) ;而在 Oracle数据库 中,只支持Serializable (串行化) 级别和 Read committed (读已提交) 这两种级别,其中默认的为 Read committed(读已提交) 级别。
在这里插入图片描述

  • 读未提交:
    在读未提交级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读 。

  • 读已提交(原理):

    • 当前读(一致性锁定读):(就是给行记录加 X 锁)(默认)
      • 如果是当前读,会对读取的记录加共享锁或排它锁,保证事务之间的隔离性。但是会出现幻读。
      • 加锁情况:事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
    • 快照读(一致性非锁定读、MVCC):(就是单纯的 SELECT 语句)
      • 读取的是数据的可见版本;如果读取的行正在执行 DELETE 或 UPDATE 操作(即读取的记录已被其它事务加上 X 锁),这时读取操作不是去等待行上锁的释放。而是读取行的一个快照数据。
      • RC 隔离级别每次 select 查询前都生成一个Read View (m_ids 列表);所以读取的是已提交的最新版本数据;
  • 可重复读(原理):

    • 当前读(一致性锁定读):(就是给行记录加 X 锁或 S 锁)

      • 如果是当前读,会对读取的记录使用 Next-key Lock 临键锁来防止幻读。
        如果两次查询中间有其它事务插入数据,就会产生幻读。所以,会对读取的记录使用 Next-key Lock 临键锁,来防止其他事务在间隙间插入数据。
      • 加锁情况:事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加行级共享锁,直到事务结束才释放;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
    • 快照读(一致性非锁定读、MVCC):(就是单纯的 SELECT 语句)(默认)

      • 和 RC 一样,读取的是数据的可见版本;如果读取的行正在执行 DELETE 或 UPDATE 操作(即读取的记录已被其它事务加上 X 锁),这时读取操作不是去等待行上锁的释放。而是读取行的一个快照数据。
      • RR 隔离级别只会在事务开启后的第一次查询生成 Read View ,并使用至事务提交。
      • 防止了可重复读和防止快照读下的 “幻读”(部分幻读)。
    • SELECT语句时,可以指定当前读方式。(非单纯的 SELECT)例如:

      • SELECT … FOR SHARE / LOCK IN SHARE MODE:当前读方式,对记录加 S 锁,其它事务也可以加S锁,如果加 x 锁则会被阻塞。
      • SELECT … FOR UPDATE / insert、update、delete:当前读方式,对记录加 X 锁,且其它事务不能加任何锁。
  • 串行化:
    以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待

五、并发事务的控制方式有哪些?

(MySQL 的隔离级别是基于锁实现的吗?)

MySQL 中并发事务的控制方式无非就两种:锁 和 MVCC。锁是悲观控制的模式,多版本并发控制是乐观控制的模式。

  • 锁:
    • 串行化隔离级别是通过锁来实现的,读已提交和可重复读隔离级别是基于 MVCC 实现的。不过,串行化之外的其他隔离级别可能也需要用到锁机制,就比如可重复读在当前读情况下需要使用加锁读来保证不会出现幻读。
  • MVCC:
    • MVCC 是多版本并发控制方法,通过创建数据的多个版本和使用快照读取来实现并发控制。读操作使用旧版本数据的快照,写操作创建新版本,并确保原始版本仍然可用。 这样,不同的事务可以在一定程度上并发执行,而不会相互干扰,从而提高了数据库的并发性能和数据一致性。

六、事务处理

  • 不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。
  • MySQL 默认是隐式提交:
    • 每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。
    • 通过 set autocommit=0 可以取消自动提交,直到 set autocommit=1 才会提交;autocommit 标记是针对每个连接而不是针对服务器的。
  • 事务指令:
    • START TRANSACTION - 指令用于标记事务的起始点。
    • SAVEPOINT - 指令用于创建保留点。
    • ROLLBACK TO - 指令用于回滚到指定的保留点;如果没有设置保留点,则回退到 START TRANSACTION 语句处。
    • COMMIT - 提交事务。

参考:
原文连接:https://blog.csdn.net/qq_41116027/article/details/124135831
原文连接:https://blog.csdn.net/listeningsea/article/details/118407983
原文连接:https://javaguide.cn/database/mysql/mysql-questions-01.html
原文连接:https://javaguide.cn/database/sql/sql-syntax-summary.html

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 确实,MySQL的varchar类型在InnoDB存储引擎中的存储结构比较复杂。在InnoDB中,每个记录都被存储为一个B+树节点,每个节点都有一个固定大小的页,通常为16KB。 当一个varchar类型的列被插入到InnoDB表中时,它会被拆分成两个部分:一个是实际的数据,另一个是长度信息。长度信息会被存储在记录头中,而实际的数据会被存储在记录的数据页中。 在InnoDB中,如果一个varchar类型的列的长度小于等于768个字节,那么它会被存储在记录的数据页中。如果一个varchar类型的列的长度超过了768个字节,那么它会被存储在单独的页中,并且在记录中只存储一个指向这个页的指针。 此外,由于InnoDB使用了行级锁定,每个记录都需要存储一个事务ID,用于实现MVCC(多版本并发控制)。因此,在InnoDB中,每个记录头还需要存储一个6字节的事务ID和一个2字节的回滚指针。 综上所述,当使用varchar类型时,需要注意其实际数据的长度和存储引擎的存储结构,以便更好地设计表结构和查询语句。 ### 回答2: MySQL的varchar存储结构确实是相当深奥的。在InnoDB存储引擎中,varchar类型的数据存储在表的记录中,其存储结构会影响数据写入、存储空间占用和查询性能。 首先,varchar类型的数据在记录中是以变长字符串的形式进行存储的。这意味着,varchar字段占用的存储空间与其实际存储的数据长度相关,而不是固定的。相比之下,固定长度的数据类型(如char)在存储时会占用固定的存储空间,无论实际数据的长度是多少。 其次,varchar类型的数据在记录中的存储格式是由一个表示长度的字节和真实字符串数据构成的。这个长度字段用于指示存储的实际数据的长度,使得数据库可以根据需要动态地分配存储空间,从而节省了存储空间。 此外,在InnoDB存储引擎中,varchar字段的数据存储在页内部的某个位置,而不是直接存储在页上。这是由于InnoDB采用了B+树的数据结构来组织数据,为了节省存储空间和提高数据访问效率,varchar字段的数据会被存储在叶子节点中。这样一来,在查询时可以更快地遍历和定位数据,提高查询性能。 综上所述,MySQL的varchar存储结构的深度体现在其变长存储方式、长度字段和数据存储位置等方面。了解和理解这些存储结构对于正确使用varchar类型的字段、优化存储空间和提高查询性能都是非常重要的。 ### 回答3: MySQL的varchar存储结构在InnoDB引擎中确实是一个很深入的话题。InnoDB引擎是MySQL的默认引擎,它采用了B+树索引来存储数据。在InnoDB的记录存储结构中,varchar类型字段经过了一系列处理。 首先,InnoDB将每个记录分为固定长度部分和变长长度部分。varchar字段属于变长长度部分。对于varchar字段,MySQL会额外存储一个指针,指向数据存储区域。 其次,在实际存储varchar字段值时,InnoDB会使用两种方式。对于较短的varchar字段值,会直接将其存储在记录的数据域中。这样做的好处是可以减少额外的存储开销。 而对于较长的varchar字段值,InnoDB会将其存储在一个称为“Overflow Page”的额外存储空间中。Overflow Page的指针存储在记录的数据域中。Overflow Page与主记录有一个单独的物理连接。 另外,需要注意的是,在InnoDB中,varchar字段的长度是可变的,存储的最大长度由定义时的最大长度决定。这与char字段是不同的,char字段的长度是固定的。 总之,MySQL的varchar存储结构在InnoDB引擎中是相对复杂的。它采用了不同的存储方式来处理不同长度的字段值,既保证了数据的存储效率,又满足了灵活性的要求。对于开发人员来说,了解varchar存储结构对于正确使用和优化数据库非常重要。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值