【MySQL事务】深刻理解事务隔离以及MVCC

06-29 1347阅读

文章目录

  • 什么叫事务
  • 事务的提交方式
  • 常见的事务操作方式
    • 事务的开始与回滚
    • 总结
    • 事务的隔离
      • 设置隔离级别
      • 解释脏读
      • 解释幻读
      • 解释不可重复读
      • 为什么可重复读不能解决幻读问题?
      • 总结
      • 数据库并发的场景
        • MVCC
          • 隐藏列字段
          • undo日志
          • Read view
          • RR和RC的本质区别
            • 总结

              什么叫事务

              在MySQL中,事务(Transaction)是一组为达成某种目的的sql操作。事务可以是单条sql语句,也可以是多条语句的集合。例如银行的一次转账,主要包含两个sql操作:一是某某用户的资产减少,二是另一个用户的资产增加。将这两条sql语句打包起来就可以称之为一个简单的转账事务。对于用户而言,他并不需要关注事务的过程,他只关心这一件事务有没有完成。即使在底层事务有执行过程,但是在概念上我们认为事务就是原子性的,即忽略中间的执行过程。

              事务除了是sql操作的集合之外,还满足以下四个属性:

              • 原子性(Atomicity):一个事务中的所有操作必须要么全部完成,要么全部不完成,不会结束在中间的某个环节。即使在事务的执行过程中发生了某种错误,mysql也会回滚到事务开始前的状态。
              • 一致性(Consistency):一致性确保事务将数据库从一个一致状态转变为另一个一致状态。在事务开始和完成时,数据库必须遵守所有定义的规则,包括数据的完整性约束。
              • 隔离性( I solation):数据库允许多个并发事务同时对同一数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于sql交叉执行而导致数据不一致。具体来说,事务隔离又分为不同级别,包括:读未提交(Read uncommitted)、读提交( read committed )、可重复读( repeatable read )和串行化( Serializable )。不同的隔离级别会影响并发控制的严格程度,隔离级别越低越危险,效率越高,隔离级别越高越安全,效率越低。不同的场景需要选择合适的隔离级别。mysql默认的隔离级别是可重复读。
              • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

                上面四个属性可以简称为AICD

                值得注意的是,在MySQL中并不是所有存储引擎都支持事务,比如MyISAM就不支持,而Innodb是支持的。 这一点可以在mysql中使用sqlshow engines;查看

                【MySQL事务】深刻理解事务隔离以及MVCC

                事务的提交方式

                事务的提交方式常见的有两种:

                • 自动提交
                • 手动提交

                  可以使用sql查看事务提交方式如:

                  【MySQL事务】深刻理解事务隔离以及MVCC

                  ON表示自动提交,OFF表示关闭自动提交,默认是ON

                  可以使用set来改变mysql的自动提交模式,0表示关闭,1表示打开

                  【MySQL事务】深刻理解事务隔离以及MVCC

                  常见的事务操作方式

                  下面用具体的sql样例来展示常见的事务操作,为了方便观察,以下事务操作是在读未提交的隔离级别下进行的,关于修改数据库的隔离级别操作后面会讲,目前不用在意。

                  现创建测试表

                  create table if not exists account(
                     id int primary key, 
                     name varchar(50) not null default '', 
                     blance decimal(10,2) not null default 0.0
                  )ENGINE=InnoDB DEFAULT CHARSET=UTF8;
                  

                  上述sql初建了一个简单的银行用户表

                  事务的开始与回滚

                  • begin表示开启一个事务
                  • commit表示提交事务
                  • savepoint 设置保存点,将来可以回滚到保存点之前
                  • rollback 回滚操作,可以回滚到某一个保存点,如果一个事务被提交了则不能回退

                    【MySQL事务】深刻理解事务隔离以及MVCC

                    【MySQL事务】深刻理解事务隔离以及MVCC

                    我们可以看到在一个事务中,使用rollback可以将当前状态回滚到某一个保存点之前

                    如果直接在事务中使用rollback则表示回滚到事务的最开始,例如:【MySQL事务】深刻理解事务隔离以及MVCC

                    • 值得注意的是,如果我们开启一个事物但是没有commit提交,发生异常中断后,MySQL会自动回滚

                      【MySQL事务】深刻理解事务隔离以及MVCC

                      一旦我们conmmit提交了事务,之后发生中断mysql崩溃,数据也不会受到影响,此时数据已经持久化保存在数据库表文件中,这体现了事务的数据持久性。

                    • begin操作会自动更改提交方式,不会受MySQL是否自动提交影响

                      一旦开启事务,就必须要使用commit提交,即使我们设置了自动提交,在遇到异常时,一样会回滚。这一点在上面的实验中已经可以证明。

                    • 自动提交只会影响单sql事务

                      实验一:终端A关闭自动提交,执行一条单sql事务后发生中断,观察现象

                      【MySQL事务】深刻理解事务隔离以及MVCC

                      实验二:终端A打开自动提交,执行一条单sql事务后发生中断,观察其现象

                      【MySQL事务】深刻理解事务隔离以及MVCC

                      总结

                      • 只要使用begin或者start transaction开启事务,就必须要通过commit提交,否则即使设置了autocommit也不会自动提交。
                      • 事务可以手动回滚,同时,当操作异常时,MySQL会自动回滚
                      • 对于InnoDB每一条SQL语句都默认封装成一个事务,自动提交。(除select外)

                        事务的隔离

                        既然MySQL支持并发访问,那多个事务一起访问同一个数据库中同一张表的场景就会经常出现。不同事物之间可能会有sql交叉执行的情况。 例如一个事物执行updata另一个事务执行select,应该先执行谁呢? 为了避免多个事务相互影响,MySQL提供了一系列方案来维持事物并发时数据的一致性,这就叫做事务的隔离。当然,事务的隔离并不是完全将两个事务隔离开互不干扰,根据不同的场景,我们需要选择不同的隔离程度。隔离程度的不同也决定了隔离级别的不同。下面我们将谈谈事物的隔离级别有哪些:

                        1. 读未提交(Read Uncommitted):该隔离级别下的事务可以看到其他事务没有提交的执行结果,相当于没有隔离,会产生许多并发问题,如脏读,幻读,不可重复读等(后面会讲解什么是脏读等)。
                        2. 读提交(Read Committed):该隔离级别下,一个正在执行的事务能看到另一个已经提交的事务的执行结果,这种隔离级别不会导致脏读,但是不可重复读以及幻读。
                        3. 可重复读(Repeatable Read):MySQL默认的隔离级别。该隔离级别的事务看到的数据是一致的,不会受到其他事务的影响,即使其他事物提交了。InnoDB存储引擎默认使用这个隔离级别。注意该隔离级别依旧存在幻读问题。
                        4. 串行化(Serializable):事物的最高隔离级别。通过强制对事物进行排序,使得每一个事物的开始必然是在另一个事物结束之后,也就不会有冲突问题,从而解决了幻读问题。它在每行数据上面加上共享锁。可能这种隔离级别的效率太低了。

                        下面解释什么是脏读、幻读、不可重复读。在此之前先介绍如何在MySQL中设置隔离级别。

                        设置隔离级别

                        • 查看全局的隔离级别

                          可以使用sqlselect transaction_isolation;(mysql8.0以前版本是tx_isolation)

                          【MySQL事务】深刻理解事务隔离以及MVCC

                        • 查看当前会话的隔离级

                          使用sql select @@session.transaction_isolation;

                          【MySQL事务】深刻理解事务隔离以及MVCC

                          全局隔离级别是对整个MySQL服务器实例的默认设置,影响所有新创建的会话(连接)。当你设置全局隔离级别时,任何新的连接都会使用这个隔离级别,除非在会话中另行指定。当前会话隔离级别只影响当前连接的事务隔离级别,当你设置会话隔离级别时,该设置仅对当前连接有效,不会影响其他会话。会话隔离级别的优先级要高于全局隔离级别。

                          • 设置全局隔离级别

                            以设置为RR级别(重复读)为例,使用sql set global transaction isolation level repeatable read;

                            【MySQL事务】深刻理解事务隔离以及MVCC

                            值得注意的是,如果我们只修改全局隔离级别,那么需要重启更新会话隔离级别

                            • 设置会话隔离级别

                              set session transaction isolation level repeatable read;

                              【MySQL事务】深刻理解事务隔离以及MVCC

                              解释脏读

                              脏读是指一个事务能读取到另一个事务尚未提交的修改数据

                              例如:

                              事务A开始并更新了一行数据,但是未提交

                              事务B读取了事务A未提交的这行数据

                              事务A回滚了此次修改数据

                              此时事务B读到的就是无效数据

                              【MySQL事务】深刻理解事务隔离以及MVCC

                              解决方法:将隔离级别设置为RC(读提交),或者更高。

                              解释幻读

                              幻读是指在一个事务中,两次查询到的数据结果集不一致,因为其他事务在两次查询之间插入或者删除了数据。注意幻读与脏读的区别,脏读主要是指读取到未提交的事务,且被回滚导致另一事务读取到的数据无效,它的问题在于数据不可靠。而幻读的问题在于查询结果不一致,结果集中出现新的行或消失的行,导致数据一致性问题。主要在RR及以下的隔离级别发生。

                              解释不可重复读

                              不可重复读是指在一个事务中,连续读取同一行数据时,因为其他事务的提交而导致结果不一致。

                              例子:

                              事务A读取了一行数据。

                              事务B更新了这行数据并提交。

                              事务A再次读取同一行数据,发现数据已经发生了变化。

                              为什么可重复读不能解决幻读问题?

                              隔离性的实现是通过对数据加锁来实现的,而insert待插入的数据本来是不存在的,那么一般加锁无法屏蔽这类问题。MySQL在RR级别解决了幻读问题(其它数据库不一定),解决的方式是通过用next-key间隙锁(GAP+行锁)。

                              间隙锁(next-key locking):间隙锁是一种锁定策略,不仅锁定表中的行,也锁定行之间的间隙,防止其他事务在间隙中插入新的行,从而避免幻读。

                              间隙(GAP): 当我们使用范围查询时,InnoDB首先会给复合条件的行加锁,但是对于不在查询范围的行,我们就称为间隙。

                              例如:

                              假设有一张employees,其结构如下:

                              CREATE TABLE employees (
                                  id INT PRIMARY KEY,
                                  name VARCHAR(100)
                              );
                              

                              初始表数据如下:

                              idname
                              1Alice
                              3Bob
                              5Charlie

                              事务A执行以下查询:

                              START TRANSACTION;
                              SELECT * FROM employees WHERE id >= 3 AND id 
                                // 省略...
                               private:
                                /** 高水位,大于等于这个ID的事务均不可见*/
                                trx_id_t m_low_limit_id;
                                /** 低水位:小于这个ID的事务均可见 */
                                trx_id_t m_up_limit_id;
                                
                                /** 创建该 Read View 的事务ID*/
                                trx_id_t m_creator_trx_id;
                                
                                /** 创建视图时的活跃事务id列表*/
                                ids_t m_ids;
                                
                                /** 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOG,
                                 * 如果其他视图也不需要,则可以删除小于m_low_limit_no的UNDO LOG*/
                                trx_id_t m_low_limit_no;
                                
                                /** 标记视图是否被关闭*/
                                bool m_closed;
                                // 省略...
                              };
                              
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]