mysql中的mvcc 原理详解

目录
  • 简介
  • 前言
  • 一、mysql 数据写入磁盘流程
  • 二、redo log
    • 1、redolog 的整体流程
    • 2、为什么需要 redo log
  • 三、undo log
    • 1、undo log 特点
    • 2、undo log 类型
    • 3、undo log 生成过程
    • 4、undo log 回滚过程
    • 5、undo log的删除
  • 四、mvcc
    • 1、什么是MVCC
    • 2、MVCC组成
    • 3、快照读与当前读
      • 快照读
      • 当前读
  • 五、mvcc操作演示
    • 1、READ COMMITTED 隔离级别
    • 2、REPEATABLE READ 隔离级别

简介

MVCC(Multi-Version Concurrency Control)多版本并发控制,是用来在数据库中控制并发的方法,实现对数据库的并发访问用的。在MySQL中,MVCC只在读取已提交(Read Committed)和可重复读(Repeatable Read)两个事务级别下有效。其是通过Undo日志中的版本链和ReadView一致性视图来实现的。MVCC就是在多个事务同时存在时,SELECT语句找寻到具体是版本链上的哪个版本,然后在找到的版本上返回其中所记录的数据的过程。

首先需要知道的是,在MySQL中,会默认为我们的表后面添加三个隐藏字段:

  • DB_ROW_ID:行ID,MySQL的B+树索引特性要求每个表必须要有一个主键。如果没有设置的话,会自动寻找第一个不包含NULL的唯一索引列作为主键。如果还是找不到,就会在这个DB_ROW_ID上自动生成一个唯一值,以此来当作主键(该列和MVCC的关系不大);
  • DB_TRX_ID:事务ID,记录的是当前事务在做INSERT或UPDATE语句操作时的事务ID(DELETE语句被当做是UPDATE语句的特殊情况,后面会进行说明);
  • DB_ROLL_PTR:回滚指针,通过它可以将不同的版本串联起来,形成版本链。相当于链表的next指针。

注意,添加的隐藏字段并不是很多人认为的创建时间和删除时间,同时在MySQL中MVCC的实现也不是通过什么快照来实现的。之所以有这种说法可能是源自于《高性能MySQL》一书中对MySQL中MVCC的错误结论,然后就人云亦云传开了(注意,我这里一直强调的是MySQL中MVCC的实现,是因为在不同的数据库中可能会有不同的实现)。所以说看源码和看官方文档才是最权威的解释)

前言

很多人在谈起mysql事务的时候都能很快的答出mysql的几种事务隔离级别,以及在各自隔离级别下产生的问题,但是一旦谈到为什么会产生这样的结果时会觉得难以回答,说到底,还是对底层的原理未做深入的探究,本篇将从较为底层的原理层面来聊聊关于mysql的mvcc原理,了解并掌握了mvcc原理,也就能真正回答这些问题了。

一、mysql 数据写入磁盘流程

在了解mvcc原理之前,先来看下面这种图,这是一张关于客户端发起一条update 数据的语句时,mysql 的innodb引擎所作的一些列操作过程(可按照前面的序列号);

从这张图,我们提取如下关键信息:

  • update 语句到达mysql的innodb引擎之后,并不是直接操作磁盘进行数据修改,而是先将磁盘数据load到buffer pool(如果没有的话);
  • buffer poo中update完成之后,并不是立即刷到磁盘,还需要将数据写到 undolog和redolog;
  • undolog记录了数据修改前的记录,redolog记录的是事务提交时数据页的物理修改;
  • 提交事务时,数据刷写到磁盘,同时把所有修改信息都存到该日志文件(redolog), 用于在刷新脏页到磁盘,发生错误时, 进行数据恢复使用;
  • 数据确认落盘成功后,redolog就没有作用了,innodb将会自动清理redolog;

从上面的分析中,可以看出,redolog文件在整个执行过程中起到了非常重要的作用,有必要对该文件做一些深入的了解和学习;

二、redo log

又叫重做日志,记录的是事务提交时数据页的物理修改,用来实现事务的持久性

redo log 日志文件由两部分组成:

  • 重做日志缓冲(redo log buffer),保存在内存中,容易丢失,对应于mysql配置文件参数为:innodb_log_buffer_size,redo log buffer 大小,默认 16M ,最大值是4096M,最小值为1M,可以通过命令:show variables like '%innodb_log_buffer_size%' 进行查看;
  • 以及重做日志文件(redo logfile),保存在磁盘中,是持久的;

1、redolog 的整体流程

仍然以上面流程图中的更新一条数据的事务过程分析,来看redolog的整体流转过程

具体步骤如下:

  • 将原始数据从磁盘中load到内存,修改数据的内存拷贝(buffer pool);
  • 生成一条重做日志,并写入redo log buffer,记录的是数据被修改后的值;
  • 事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加
  • 写的方式;
  • 定期将内存中修改的数据刷新到磁盘中;

2、为什么需要 redo log

在 InnoDB引擎中的内存结构中,主要内存区域就是缓冲池, 在缓冲池中缓存了很多的数 据页(磁盘中读取mysql数据时一般以数据页为单位进行加载); 在一个事务执行中,比如执行多个增删改的操作时, InnoDB 引擎会先操作缓冲池中的数据,如果 缓冲区没有对应的数据,再通过后台线程将磁盘中数据load出来,放到缓冲区,然后修改缓冲池中 的数据,修改后的数据页我们称为脏页; 而脏页则会在一定的时机,通过后台线程刷新到磁盘中,从而保证缓冲区与磁盘的数据一致。 但是缓冲区脏页数据并不是实时刷新的,而是隔一段时间后才将缓冲区的数据刷到磁盘中。 假如刷新到磁盘的过程出错了,而提示给用户事务提交成功,而数据却 没有持久化下来,这就出现问题了,没有保证事务的持久性。 有了 redolog 之后,当对缓冲区的数据进行增删改之后,会首先将操作的数据页的变化,记录在 redo log buffer中。在事务提交时,会将 redo log buffer 中的数据刷新到 redo log 磁盘文件中。 过一段时间后,如果刷新缓冲区的脏页到磁盘时,发生错误,此时就可以借助于 redo log 进行数据 恢复,这样就保证了事务的持久性。 而如果脏页成功刷新到磁盘 或 或者涉及到的数据已经落盘,此 时redolog 就没有作用了,就可以删除了,所以存在的两个 redolog 文件是循环写的。 说到这里就有伙伴要问,为什么每一次提交事务,要刷新 redo log 到磁盘中呢,而不是直接将 buffer pool 中的脏页刷新 到磁盘呢? 因为客户端与mysql进行数据交互(IO)过程中,们操作数据一般都是随机读写磁盘的(随机读写比较慢),而不是顺序读写磁盘(顺序读写块)。 而 redo log 在 往磁盘文件中写入数据,由于是日志文件,所以都是顺序写的。顺序写的效率,要远大于随机写。 这 种先写日志的方式,也称之为 WAL ( Write-Ahead Logging )。

三、undo log

undo log 也成为回滚日志,用于记录数据被修改前的信息 , 作用包含两个 : 提供回滚 ( 保证事务的原子性 ) 和 MVCC(多版本并发控制 ) 。

举例来说,本次使用update语句修改了一条id为1的数据,如果事务提交失败,那么就需要回滚数据,mysql引擎怎么知道回滚到哪里呢?那就要借助undo log了,undolog中记录了修改之前的数据,所以就可以用于事务回滚。

1、undo log 特点

  • undo log和redo log记录物理日志不一样,它是逻辑日志;
  • 当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的 update记录;
  • 执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚;

2、undo log 类型

  • insert undo log;
  • update undo log;

3、undo log 生成过程

从文章开头的流程图中再简单抽象出下面的简化执行步骤

在开启一个事务对一条数据记录进行update的时候,对于这条数据行来说,其底层存储的结构大概长下面这样;

在这行记录中,对应着两个隐藏字段,事务ID和回滚指针,当执行一条insert语句时,

begin ;
insert into user (name) values ( "tom" );

对于 undolog 来说,记录的数据状态将会呈现如下效果,可以看到,在这条记录中,回滚指针指向了一条数据激励,记录了这条数据的源信息,通过一个undo no标识;

执行update的时候,数据行记录变更,同时在redo log 回滚指针链上将增加一条记录,并连接上一条记录;

继续执行一个update语句:

UPDATE user SET name ='jike'   WHERE id= 1 ;

4、undo log 回滚过程

如果事务回滚,执行rollback,对应的流程如下:

  • 通过undo no=3的日志把name='jike'的数据删除;
  • 通过undo no=2的日志把id=1的数据的deletemark还原成0;
  • 通过undo no=1的日志把id=1的数据的name还原成Tom;
  • 通过undo no=0的日志把id=1的数据删除;

5、undo log的删除

undo log的删除分成2种

  • 针对于insert undo log,因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作;
  • 针对于update undo log,该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除;

四、mvcc

1、什么是MVCC

全称:多版本并发控制,MVCC 是通过数据行的多个版本管理来实现数据库的并发控制。通过这项技术,使得在InnoDB的事务隔离级别下执行 一致性读操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的数据行,并且可以看到它们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁。

2、MVCC组成

mvcc的实现主要依赖下面的3个主要逻辑实现,分别是:

  • 隐藏字段,在上文中有所交待,每个数据行都会存在一个隐藏字段;
  • undolog版本链,上文有所交待,记录了回滚数据行的数据;
  • ReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id,可能是一个数组;

3、快照读与当前读

MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,这样即使有读写冲突时,也能做到不加锁,非阻塞并发读 ,而这个读指的就是快照读,而非当前读。当前读实际上是一种加锁的操作,是悲观锁的实现,而MVCC本质是采用乐观锁思想的一种方式。

快照读

快照读又叫一致性读,读取的是快照数据。不加锁的简单的 SELECT 都属于快照读,即不加锁的非阻塞读;比如这样:

SELECT * FROM player WHERE ...

之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于MVCC,它在很多情况下,避免了加锁操作,降低了开销。

既然是基于多版本,那么快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。

当前读

当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据),读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。加锁的 SELECT,或者对数据进行增删改都会进行当前读。比如:

SELECT * FROM student LOCK IN SHARE MODE; # 共享锁

SELECT * FROM student FOR UPDATE; # 排他锁

INSERT INTO student values ... # 排他锁

DELETE FROM student WHERE ... # 排他锁

五、mvcc操作演示

来看下面这一些列的事务操作过程,如下是一组操作同一条数据的记录的多个事务,从事务2 ~ 事务5,分别对应不同的操作何阶段;

从上文我们对undolog的了解,每次修改一条数据时,会在undolog 回滚链中增加一条记录,用于后续做数据回滚;

具体步骤如下:

比如当事务2执行第一条修改语句时,会记录一条undo log日志,记录了当前数据变更之前的样子; 然后更新记录,并且记录本次操作的事务ID,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本;

当事务 3 执行第一条修改语句时,也会记录 undo log 日志,记录数据变更之前的样子 ; 然后更新记 录,并且记录本次操作的事务 ID ,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本;

当事务 4 执行第一条修改语句时,也会记录 undo log 日志,记录数据变更之前的样子 ; 然后更新记 录,并且记录本次操作的事务 ID ,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本;

通过上面一些列的操作,最终会发现,不同事务或相同事务对同一条记录进行修改,会导致该记录的 undolog 生成一条 记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。

有了上面的redo log 的回链,最终是怎么确定某个事务读取的数据是长什么样子呢?接下来 readview就派上用场了;

ReadView (读视图)是 快照读 SQL 执行时 MVCC 提取数据的依据,记录并维护系统当前活跃的事务 (未提交的)id;

ReadView中包含了四个核心字段:


字段

含义

m_ids

当前活跃事务 ID 集合

min_trx_id

最小活跃事务 ID

max_trx_id

预分配事务 ID ,当前最大事务 ID+1 (因为事务 ID 是自增的)

creator_trx_id

ReadView 创建者事务 ID

而在 readview 中就规定了版本链数据的访问规则,

trx_id 代表当前undolog版本链对应事务ID

完整的匹配规则如下:


条件

是否可以访问

说明

trx_id ==

creator_trx_id


可以访问该版本

成立,说明数据是当前这个事

务更改的


trx_id < min_trx_id

可以访问该版本

成立,说明数据已经提交了

trx_id > max_trx_id

不可以访问该版本

成立,说明该事务是在

ReadView 生成后才开启


min_trx_id <= trx_id

<= max_trx_id


如果 trx_id 不在 m_ids 中,

是可以访问该版本的


成立,说明数据已经提交

不同的隔离级别,生成ReadView的时机不同:

  • READ COMMITTED (读已提交):在事务中每一次执行快照读时生成ReadView;
  • REPEATABLE READ(可重复读):仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView;

也就是说,在利用mvcc的多版本并发控制时,只需要关注这两种事务隔离级别就行了,接下来,以上午的excel中展示的几个事务为例,对照这两种类型的事务隔离级别进行说明;

1、READ COMMITTED 隔离级别

以事务5为例进行说明,两次快照读读取数据时,是如何获取数据的?

在事务 5 中,查询了两次 id 为 30 的记录,由于隔离级别为 Read Committed ,所以每一次进行快照读 都会生成一个 ReadView ,那么两次生成的 ReadView 如下

那么这两次快照读在获取数据时,就需根据所生成的 ReadView 以及 ReadView 的版本链访问规则, 到undolog 版本链中匹配数据,最终决定此次快照读返回的数据; 先来看第一次快照读具体的读取过程

对应的undo log 版本链如下

在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:

1)先匹配下面这条记录,这条记录对应的trx_id为4,也就是将4带入右侧的匹配规则中。 ①不满足 ②不满足 ③不满足 ④也不满足 , 都不满足,则继续匹配undo log版本链的下一条;

2) 再匹配第二条记录, 这条 记录对应的 trx_id 为 3 ,也就是将 3 带入右侧的匹配规则中。①不满足 ②不满足 ③不满足 ④也 不满足 ,都不满足,则继续匹配 undo log 版本链的下一条;

3)再匹配第三条记录,这条记 录对应的trx_id为2,也就是将2带入右侧的匹配规则中。①不满足 ②满足 终止匹配,此次快照 读,返回的数据就是版本链中记录的这条数据;

再来看第二次快照读具体的读取过程:

对应的undolog 版本链如下:

在进行匹配时,会从 undo log 的版本链,从上到下进行挨个匹配:

1)先匹配这条记录,这条记录对应的 trx_id为4,也就是将4带入右侧的匹配规则中。 ①不满足 ②不满足 ③不满足 ④也不满足 , 都不满足,则继续匹配undo log版本链的下一条;

2)再匹配第二条, 这条 记录对应的 trx_id 为 3 ,也就是将 3 带入右侧的匹配规则中。①不满足 ②满足 。终止匹配,此次 快照读,返回的数据就是版本链中记录的这条数据;

2、REPEATABLE READ 隔离级别

在这种隔离级别下,仅在事务中第一次执行快照读时生成 ReadView ,后续继续复用该 ReadView 。 而 可重复读在一个事务中,执行两次相同的select 语句,查询到的结果是一样的; 那 MySQL 是如何做到可重复读的呢 ? 按照上面的过程做一下类似的分析

可以看到,在可重复读这种事务 隔离级别下,只在事务中第一次快照读时生成 ReadView ,后续都复用该 ReadView。既然 ReadView 都一样, ReadView 版本链匹配规则也一样, 最终快照读返 回的结果也是一样的了。 总结 MVCC实现原理是通过 InnoDB表的隐藏字段、UndoLog 版本链、ReadView来实现的;MVCC + 锁,则实现了事务的隔离性;一致性则是由redolog 与 undolog保证;

到此这篇关于mysql mvcc 原理详解的文章就介绍到这了,更多相关mysql mvcc 原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MYSQL数据库Innodb 引擎mvcc锁实现原理

    目录 1 数据库设置隔离级别 2 数据库表以及案例操作 3 mvcc 实现原理 4 ACID 的实现 前言: 大家都知道在java 开发过程中,会经常用到锁,在java 代码中,我们都知道锁是加在对象头上的,在java对象布局中有锁的标志位.程序通过判断锁的标志位来获取加锁的情况.但是在mysql 中,锁的实现原理是什么呢.可能大家都听过 mvcc,但是mvcc 的实现原理是什么呢,可能就说不太清楚了,本文就以实例说明来mvcc 的实现原理. 1 数据库设置隔离级别 我们都知道数据库的隔离级别可

  • 一文解析MySQL的MVCC实现原理

    目录 1. 什么是MVCC 2. 事务的隔离级别 3. Undo Log(回滚日志) 4. MVCC的实现原理 4.1 当前读和快照读 4.2 隐藏字段 4.3 版本链 4.4 Read View(读视图) 5. 不同隔离级别下可见性分析 5.1 READ COMMITTED(读已提交) 5.2 REPEATABLE READ(可重复读) 1. 什么是MVCC MVCC全称是Multi-Version Concurrency Control(多版本并发控制),是一种并发控制的方法,通过维护一个数

  • MySQL多版本并发控制MVCC底层原理解析

    目录 1 事务并发中遇到的问题 1.1 脏读 1.2 不可重复读 1.3 幻读 2 隔离级别 3 版本链 4 ReadView 4.1 ReadView 定义 4.2 访问控制 4.3 再谈隔离 4.3.1 READ COMMITTED(读已提交) 4.3.2 REPEATABLE READ(可重读) 5 幻读 6 总结 1 事务并发中遇到的问题 1.1 脏读 当一个事务读取到了另外一个事务修改但未提交的数据,被称为脏读. 1.2 不可重复读 当事务内相同的记录被检索两次,且两次得到的结果不同时

  • Mysql MVCC机制原理详解

    什么是MVCC MVCC,全称Multi-Version Concurrency Control,即多版本并发控制.MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存. 我们知道,一般情况下我们使用mysql数据库的时候使用的是Innodb存储引擎,Innodb存储引擎是支持事务的,那么当多线程同时执行事务的时候,可能会出现并发问题.这个时候需要一个能够控制并发的方法,MVCC就起到了这个作用. Mysql的锁和事务隔离级别 在理解MVCC机制

  • mysql中的mvcc 原理详解

    目录 简介 前言 一.mysql 数据写入磁盘流程 二.redo log 1.redolog 的整体流程 2.为什么需要 redo log 三.undo log 1.undo log 特点 2.undo log 类型 3.undo log 生成过程 4.undo log 回滚过程 5.undo log的删除 四.mvcc 1.什么是MVCC 2.MVCC组成 3.快照读与当前读 快照读 当前读 五.mvcc操作演示 1.READ COMMITTED 隔离级别 2.REPEATABLE READ 

  • Mysql数据库group by原理详解

    目录 引言 1. 使用group by的简单例子 2. group by 原理分析 2.1 explain 分析 2.2 group by 的简单执行流程 3. where 和 having的区别 3.1 group by + where 的执行流程 3.2 group by + having 的执行 3.3 同时有where.group by .having的执行顺序 3.4 where + having 区别总结 4. 使用 group by 注意的问题 4.1 group by一定要配合聚

  • Java中synchronized实现原理详解

    记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字"同步",也成为了我们解决多线程情况的百试不爽的良药.但是,随着我们学习的进行我们知道synchronized是一个重量级锁,相对于Lock,它会显得那么笨重,以至于我们认为它不是那么的高效而慢慢摒弃它. 诚然,随着Javs SE 1.6对synchronized进行的各种优化后,synchronized并不会显得那么

  • MYSQL中Truncate的用法详解

    本文导读:删除表中的数据的方法有delete,truncate, 其中TRUNCATE TABLE用于删除表中的所有行,而不记录单个行删除操作.TRUNCATE TABLE 与没有 WHERE 子句的 DELETE 语句类似:但是,TRUNCATE TABLE 速度更快,使用的系统资源和事务日志资源更少.下面介绍SQL中Truncate的用法 当你不再需要该表时, 用 drop:当你仍要保留该表,但要删除所有记录时, 用 truncate:当你要删除部分记录时(always with a WHE

  • mysql中整数数据类型tinyint详解

    目录 1.1 tinyint类型说明 1.2 实践环境说明 1.3 加unsigned属性 1.3.1 SQL模式开启严格模式 1.3.2 SQL模式未开启严格模式 1.4 加zerofill属性 1.4.1 SQL模式开启严格模式 1.4.2 SQL模式未开启严格模式 1.5 不加unsigned和zerofill属性 1.5.1 SQL模式开启严格模式 1.5.2 SQL模式未开启严格模式 1.1 tinyint类型说明 数据类型 显示长度 占用字节 有符号 无符号 tinyint 加上un

  • Android中Lifecycle的原理详解

    目录 一.基本使用 二.LifecycleObserver接口和LifecycleOwner接口 三.getLifecycle() 四.绑定生命周期 总结 Lifecycle是Android Architecture Components的成员,是一个生命周期感知组件,能够感知Activity.Fragment等组件的生命周期变化,并将变化通知到已注册的观察者.正确的使用有助于更好地组织代码,减少内存泄漏,增强稳定.下面分析他的实现原理,看看到底只怎么感知生命周期的. 一.基本使用 1.引入依赖

  • MySQL中数据视图操作详解

    目录 1.视图概述 1.1创建视图 1.2视图的查询 2.操作视图 2.1通过视图操作数据 2.2修改视图定义 2.3删除视图 1.视图概述 视图是从一个或多个表(或视图)导出的表.视图与表(有时为与视图区别,也称表为基本表)不同,视图是一个虚表,即视图所对应的数据不进行实际存储,数据库中只存储视图的定义,对视图的数据进行操作时,系统根据视图的定义去操作与视图相关联的基本表. 视图一经定义,就可以像表一样被查询.修改.删除和更新.使用视图有下列优点: 1.为用户集中数据,简化用户的数据查询和处理

  • Template ref在Vue3中的实现原理详解

    目录 背景 模板的编译 setup 函数返回值的处理 组件的渲染 Template Ref 的注册 总结 背景 最近我的 Vue3 音乐课程后台问答区频繁出现一个关于 Template ref 在 Composition API 中使用的问题,于是我就想写一篇文章详细解答这个问题. 先来看一个简单的例子: <template> <div ref="root">This is a root element</div> </template>

  • MySQL复制优点、原理详解

    复制是将主数据库的DDL和DML操作通过二进制日志传到从库上,然后再从库重做,从而使得从库和主库保持数据的同步.MySQL可以从一台主库同时向多台从库进行复制,从库同时也可以作为其他从库的主库,实现链式复制. MySQL复制的优点: 主库故障,可以快速切换至从库提供服务: 在从库执行查询操作,降低主库的访问压力: 在从库执行备份,避免备份期间对主库影响: MySQL复制原理 1.MySQL主库在事务提交时会把数据变更作为事件Events记录在Binlog中,主库上的sync_binlog参数控制

  • C#中foreach实现原理详解

    本文主要记录我在学习C#中foreach遍历原理的心得体会. 对集合中的要素进行遍历是所有编码中经常涉及到的操作,因此大部分编程语言都把此过程写进了语法中,比如C#中的foreach.经常会看到下面的遍历代码: var lstStr = new List<string> { "a", "b" }; foreach (var str in lstStr) { Console.WriteLine(str); } 实际此代码的执行过程: var lstStr

随机推荐