探索不同引擎Innodb和Myisam的索引优化方案
数据库可能存在千万级的数据,必须将这些行数据以一定的结构组织起来做到高效的增删改查。
我们将分别探索innodb和myisam两种引擎的索引方案。
一、InnoDB的索引
1、假设表初始没有记录,只有一个空页,所有记录按照主键顺序放到页中。随着记录的增长,一个页放不下所有记录,因此会分裂成多个页,每个页用双向指针链接,页与页之间形成一条双向链表。
这些页我们称为用户记录页,页内记录的字段是用户定义的字段内容。如下图所示,这是一个以 (id, key1, key2) 组成的联合索引,页10的第1条记录表示(id=1, key1=4, key2='u')。
如果Innodb表数据是按这种结构存储,那么我们只能做到“页间遍历,页内二分搜索的方式查找”。也就是说当我要查找一条id=30的数据,需要先对页10进行二分查找,没找到再从页28进行二分查找,没找到再从页9进行二分查找直到找到,效率很低。为了提高查找效率,可以为这些 用户记录页 建立 目录页。
2、目录页在页结构上 和 用户记录页 一样,目录页的行记录也是以堆的形式存储,我们称目录页的行记录为目录项,每条记录只有两个列(没有其他的隐藏列),分别是下层页中最小记录的主键 和 用户记录页的页号。根据主键值(或者索引值)查找数据时,会从最顶层的目录页开始检索,找到目录页中符合的记录后,再根据记录中的最小记录的主键 和 用户记录页的页号这两个信息,找到其对应的用户页的最小记录在磁盘中的位置。
记录头信息的record_type = 0 表示该记录是用户记录,record_type = 1 是目录项记录。
需要注意:
目录页的页内查找也是通过上节中介绍的页目录(Page Directory)进行二分查找进行检索的。
目录页内的行记录过多,一个页放不下的时候,目录页也会发生页分裂,而且目录页之间也有双向链接。
如下图所示
3、如果目录页也有多个,那么查目录也要遵循目录页间遍历查找,目录页内二分查找,为了避免目录页间的横向遍历,可以建立多层目录,顶层目录只有一个目录页。所有目录页和用户记录页形成一棵B+树:
这么一来,就可以把 页间查找 的次数压缩为树的层数,这意味着磁盘IO的次数被压缩到树的层数。一般而言,MySQL中一棵B+树不会超过4层。
Innodb规定,无论是目录页还是用户记录页,一个页至少容纳2条记录,这样做是为了控制树的高度,不让B+树的性能优势丧失。
主键和唯一索引的区别
其实,两者肯定不同: 主键是一种约束,唯一索引是一种索引,两者在本质上就是不同的。
1、一张表里只能有一个主键约束,可以有多个唯一约束
2、主键不能为空,而唯一可以为空
3、主键列,默认是聚集索引,聚集索引 查询效率最高
主键索引(聚簇索引)和普通索引(辅助索引)的区别
什么是聚集索引(主键索引)?
首先 innodb 引擎默认在主键上建立聚集索引,通常说的主键索引就是聚集索引,聚集索引会保存行上的所有数据,因此不需要额外的 IO
什么是辅助索引(普通索引)?
辅助索引 (Secondary Index) , 叶子节点只保存了行的键值和指向对应行的 "书签" , 一般指向的是聚集索引,此外 innodb 实现了覆盖索引 (Covering index) , 即叶子节点除了保存该行的键值还保存了对应索引列的值,如果不需要额外数据的话则不需要另外对聚集索引中的数据进行 IO
注: SQL Server的主键索引和普通索引的区别仅仅是唯一非空,而mysql innodb下不是, 另外严格来说主键是约束
为什么性别列和其他低选择性的列不适合加索引?
因为你访问索引需要付出额外的 IO 开销,你从索引中拿到的只是地址,要想真正访问到数据还是要对表进行一次 IO。假如你要从表的 100 万行数据中取几个数据,那么利用索引迅速定位,访问索引的这 IO 开销就非常值了。但如果你是从 100 万行数据中取 50 万行数据,就比如性别字段,那你相对需要访问 50 万次索引,再访问 50 万次表,加起来的开销并不会比直接对表进行一次完整扫描小。
当然凡事不是绝对,如果把性别字段设为表的聚集索引,那么就肯定能加快大约一半该字段的查询速度了。聚集索引指的是表本身中数据按哪个字段的值来进行排序。因此,聚集索引只能有一个,而且使用聚集索引不会付出额外 IO 开销。当然你得能舍得把聚集索引这么宝贵资源用到性别字段上。
优化器为什么会选择覆盖索引用于 count () 等统计问题?
因为覆盖索引远小于聚集索引,可以减少磁盘 IO 操作
对于主键索引B+树而言,页内记录是按照主键大小排序的,同一层的双向链表目录页也是按照主键索引排序的。
对于二级索引B+树而言,假设该B+树是以c2字段为索引。那么
二级索引的叶子节点页的记录只有两个列:c2列 和 主键值,不含其它列和隐藏列。(重点来了)而二级索引的非叶子节点页内的记录有3个列:c2列 、 下层页号 和 主键值。
页内记录是按照 c2列和主键值 (先按c2列排序后按主键列排序)的大小排序的,页目录(Page Directory)每个槽(Slot)也是分组中拥有最大c2列值的记录的地址,目录页之间也是按照c2列和主键大小排序的。
为什么二级索引的目录页和叶子页的记录需要保存主键索引并且在二级索引值相同的情况下按主键索引排序呢?考虑一个问题,如下图所示:c1是主键,c2是普通索引。
此时 c2 字段索引的B+树如下:
此时,如果我想插入一个 c1:9、c2:1、c3:'c' 的记录,在目录页3没有存储主键值的情况下,由于目录页3的c2全都是1,所以页3无法决定该记录应该插在页4还是页5。
但如果目录页加上主键字段就可以解决这个问题:
所以,为 c2 列建立普通索引 相当于为 c2 和 主键列建 立了一个联合索引。
对于唯一二级索引来说,也可能会出现 多条记录键值相同的情况(例如NULL,以及MVCC),所以唯一二级索引的目录项记录也会包含记录的主键值。
我们结合下面这个例子,看看innodb是如何在使用索引找到对应记录的,其中key1是普通索引。
SELECT * FROM single_table WHERE key1 = ' a ' AND id > 9000 ;
这个sql的行为如下:
1、在从key1字段的B+树最顶层目录页的记录查找,找到符合 key1 = 'a' 的记录后,根据下层页号找到下一层目录页,依此类推,最终找到符合 key1 = 'a'的叶子节点的第一条记录;
2、沿着叶子节点页的向后指针找到所有满足 key1 = 'a' 的记录,在这个过程中同时一条条的比对 id > 9000 这个条件,得到满足条件的叶子节点记录。
也就是说,id > 9000 的比较是在二级索引发生的,而不是在主键索引发生的,这样就可以减少多次回表带来的磁盘IO。
3、根据满足条件的叶子节点记录中的主键值,到主键索引B+树中查找,查找方式和前两个步骤一致。最终找到主键B+树叶子节点对应的目标记录。
主键索引和二级索引不同的一点在于,主键索引的叶子页的记录包含用户定义的所有字段,而二级索引的叶子页的记录只包含二级索引值和主键值。
联合索引的页
对于联合索引B+树(假设联合索引的字段是 c3,c4),叶子节点页的记录会包含 c3、c4 和 主键 这3个列。
二、MyISAM的索引方案
在介绍MyISAM的索引结构之前,需要先介绍MyISAM的行格式,分为3种:定长记录(static)、变长记录(Dynamic)和压缩格式(compressed)。
MyISAM的索引和表记录是分成两个文件存储的(MYD 和 MYI)。InnoDB 和 MyISAM 都是用B+树作为索引结构。不同点在于:
1、InnoDB的表记录(包含用户定义的所有字段)是保存在主键的叶子页并通过一系列页内的数据结构维护(例如以数组为结构的页目录)。
而MyISAM的表记录是按照记录的插入顺序(而不是主键顺序)紧密的存储在MYD文件中,并没有将它们分成多个页。
如下图所示
图中是定长格式(static)的记录在 MYD 的存储方式,每一行拥有一个虚拟的逻辑行号。
2、MyISAM的B+树的叶子节点只存储索引字段的值 和 行号,InnoDB的B+树叶子节点存的是行的所有字段值。
MyISAM下,若要根据一个索引值查找一条或多条记录,需要先在MYI文件的B+数中,根据索引值找到行号,再根据行号到MYD文件找记录。
所以MyISAM的主键索引比InnoDB多了一次回表。
MyISAM的主键索引是一个二级索引而不是聚集索引。MyISAM的二级索引比InnoDB的二级索引快,因为前者直接根据行的地址到MYD文件回表,后者是根据主键值回表,这意味着要根据主键值逐层查到用户记录页的地址和行的页内地址。
在聚集索引页不在缓冲区的情况下InnoDB比MyISAM多了2~3次磁盘IO才能获得行内容。
另外,如果MyISAM使用变长格式的记录,那么MyISAM的叶子节点存储 索引字段值 和 行所在MYD的地址。
三、索引的代价
空间上,每建立一个索引就会创建一个B+树,一个索引页就是16K;
时间上,增删改需要对所有B+树操作,由于新数据添加时发生在叶子节点,因此要从根节点找到叶子节点,会涉及多次磁盘IO,B+树越多,IO次数越多。
此外DML操作可能引起页分裂、页回收,InnoDB需要额外的时间完成这些操作以维护节点和记录的排序。
最后,如果一条查询语句涉及多个索引,生成执行计划需要计算使用不同索引执行查询的成本,并选择成本最低的哪个索引执行(因为一般情况下一个查询语句最多使用一个二级索引)。建立太多索引会导致成本分析时间过长。
四、索引条件下推
在 mysql 5.6以后,索引条件下推功能默认是开启的。
所谓的索引条件下推是指在二级索引中就尽可能根据sql条件减少在二级索引匹配到的记录数,这是一种用二级索引进行范围搜索的优化,可以有效减少回表的次数。
举个例子:
有一个联合索引 key (a, b, c),有一个sql语句:
SELECT * from t WHERE a = ' a' AND c = 'c';
假设 满足 a = 'a' 的记录有 10 条,满足 a = 'a' and c = 'c' 的记录有5条,聚集索引 M 和联合二级索引 N 都只有3层,不考虑页缓存,请问回表次数和磁盘IO次数是多少?
查找过程如下:
1、从索引N的根节点(根目录页)开始,找到 a < 'a' 且离 a = 'a' 最近的目录项,进入下一层目录页;
2、同上操作,进入到第三层页,即叶子节点页。
3、在第三层叶子节点页页内找到 a < 'a' 且离 a = 'a'最近的记录,顺着指针在页内往下遍历;在页外顺着页间指针遍历;(如果 所有a='a' 的记录都在二级索引N的一个用户记录页,则第3步需要进行1次IO,如果是分布在2个用户记录页,则第3步需要2次IO,不会分布超过2个用户记录页的,因为满足 a='a' 的记录只有10条)。
第3步得到了 满足 a="a" 的10个主键id;
4、对着10个主键id进行回表,需要回表10次;
5、每次回表查到了完整的表记录,查看 c字段是否满足 c = 'c';
总回表次数为10,总IO次数为(10*3 + 3~4 = 33~34)。
如果使用索引条件下推,那么在二级索引的叶子节点比对的时候,还会检查 c = 'c' 的条件,因为 N 的页的记录也包含c字段的值。所以检索完毕后可以在N的叶子节点层得到 5 个满足条件的主键,所以只会回表5次,发生 15 + 3~4 = 18~19次磁盘IO。
再例如这2个例子:
SELECT * from t WHERE a > ' a' AND a like '%mbc';
SELECT * FROM t WHERE a = ' a ' AND id > 9000 ;
也同样会用到索引条件下推优化。
五、索引的功能
索引主要具有3个功能:高效查找、排序和分组;
在没有用到索引的情况下,只能将数据加载到内存,并在内存中排序和分组(如果中间结果太多还需要将中间结果存到磁盘中),这种情况叫做文件排序(filesort)。使用到文件排序会降低查找性能。
而使用了索引,由于记录插入B+树时本来就是按索引列的顺序插入,因此索引内的数据是有序的,查询时无需再在内存中排序或分组。
几种无法使用索引排序的情况:
1、ASC和DESC混用,即对某个字段升序排序,对另一个字段降序排序;
不过,Mysql 8.0 给出了 Descending Index 的新功能,允许联合索引的建立可以按某个字段升序,某个字段降序的方式插入。
2、order by 包含非同一个索引的列;
3、order by 包含多个是同一联合索引的列,但列的顺序不对(例如对字段A升序,对字段B降序);
4、where 条件的索引列和排序的索引列不同,因为你无法保证 where a='xxx' 的情况下的记录 其排序字段 c 是有序的;
5、order by 对列使用函数;
六、回表的代价
回表的代价就是当执行对二级索引的范围查询时,如果查到的记录数太多,每条记录都需要在主键索引进行回表,回表一次就会在主键索引发生多次随机IO。
一次sql查询中,回表的记录(或者说次数)越少,二级索引的效率越高。
例如下面的sql语句:
SELECT * from single_table WHERE key1 > ' a ' AND key1< ' c ' ;
可以选择使用 key1 索引进行查询,也可以选择不使用 key1 索引而是直接全表扫描。
这是由查询优化器决定的,而决定的标准就是 区间 ('a', 'c') 的范围有多大,也就是取决于使用二级索引会发生回表的次数所带来的磁盘IO次数 和 全表臊面发生的磁盘IO次数哪个更多。
一般情况下,对于指定了 limit 子句的查询,优化器倾向于使用二级索引 + 回表的方式;
如果一个order by 使用了二级索引作为排序列,但是记录数太多,而且没有覆盖索引,那么mysql很可能使用 全表扫描 + 文件排序的方式也不用二级索引的排序,例如:
SELECT * from single_table order by key1 ;
七、如何善用索引
1、只为用于查询、排序和分组的列创建索引;
2、为基数大(即重复值少)的列建立索引(因为重复值多意味着回表次数多);
3、索引列的类型尽量小(例如int类型,较短的varchar类型,能用int就不用bigint,能用tinyint就不用int),这决定了页内能容纳记录的个数(一次磁盘IO能将更多的目录项和记录加载到内存),也决定了树高;
这个建议尤其适用在主键上,因为所有二级索引的页都会存一份记录的主键值。
4、为列前缀建立索引,例如不要对一个varchar(100)的字段c1建立索引,而是对 c1的前10个字节建立索引;
原因有二:一是为了让页内能容纳更多的记录和目录项,二是字符串的比较,其复杂度会随字符串长度增加而增加;
另外需要注意:为列前缀建立索引,该索引只能用于查找,无法用于排序。例如:
ALTER TABLE single_table ADD INDEX idx_key1(key1(10));
SELECT * from single_table ORDER BY key1 limit 10;
这个排序没有用到key1的索引,因为B+树只对key1的前10个字节排序了,没有给整个key1值排序;
5、覆盖索引(即避免使用*,而是在sql中注明要查询的列):可以彻底避免回表,对于二级索引而言,覆盖索引会让优化器会选择使用二级索引而不会选择全表扫描。
6、让索引列以列名的形式在搜索条件中单独出现(别使用函数或对列计算)。
7、主键的插入尽可能按顺序插入:这是为了避免也分裂。对于二级索引,就没办法要求它的插入页按顺序了,这是业务需求决定的。
考虑一种情况:一个页按照id为 1、3、5、7、9、2、4、6、8、10的顺序插入,这会导致页分裂;
但是如果 1、2、3、4、5、6,我删除id=2的记录,再插入id=2的记录,是不会导致页分裂的,他会重新占用页内的已删除空间。
八. 如何查看索引使用的情况:
show status like 'Handler_read%';
注意:
- handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
- handler_read_rnd_next:这个值越高,说明查询比较的低效。
九:比对InnoDB和MyISAM
MyISAM 和 InnoDB 比较
1、MyISAM:是MySQL的默认数据库引擎(5.5版之前),由早期的ISAM所改良。虽然性能极佳,但却有一个缺点:不支持事务处理(transaction)。它是存储记录和文件的标准方法。不是事务安全的,而且不支持外键,如果执行大量的select,insert MyISAM比较适合
2、InnoDB:支持事物安全的引擎,支持外键,行锁,事务控制是它最大的特点,在有大量的insert,update语句时,使用InnoDB比较合适,特别是针对多个并发和QPS较高的时候。
区别
表锁差异
MyISAM:
myisam只支持表级锁,用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。也可以通过lock table命令来锁表,这样操作主要是可以模仿事务,但是消耗非常大,一般只在实验演示中使用。
InnoDB :
Innodb支持事务和行级锁,是innodb的最大特色。
Innodb的行锁模式有以下几种:共享锁,排他锁,意向共享锁(表锁),意向排他锁(表锁),间隙锁。
注意:当语句没有使用索引,innodb不能确定操作的行,这个时候就使用的意向锁,也就是表锁
数据库文件差异
MyISAM :
myisam属于堆表
myisam在磁盘存储上有三个文件,每个文件名以表名开头,扩展名指出文件类型。
.frm 用于存储表的定义
.MYD 用于存放数据
.MYI 用于存放表索引
myisam表还支持三种不同的存储格式:
静态表(默认,但是注意数据末尾不能有空格,会被去掉)
动态表
压缩表
InnoDB :
innodb属于索引组织表
innodb有两种存储方式,共享表空间存储和多表空间存储
两种存储方式的表结构和myisam一样,以表名开头,扩展名是.frm。
如果使用共享表空间,那么所有表的数据文件和索引文件都保存在一个表空间里,一个表空间可以有多个文件,通过innodb_data_file_path和innodb_data_home_dir参数设置共享表空间的位置和名字,一般共享表空间的名字叫ibdata1-n。
如果使用多表空间,那么每个表都有一个表空间文件用于存储每个表的数据和索引,文件名以表名开头,以.ibd为扩展名。
索引差异
1、关于自动增长
myisam引擎的自动增长列必须是索引,如果是组合索引,自动增长可以不是第一列,他可以根据前面几列进行排序后递增。
innodb引擎的自动增长列必须是索引,如果是组合索引也必须是组合索引的第一列。
2、关于主键
myisam允许没有任何索引和主键的表存在,
myisam的索引都是保存行的地址。
innodb引擎如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见)
innodb的数据是主索引的一部分,附加索引保存的是主索引的值。
3、关于count()函数
myisam保存有表的总行数,如果select count() from table;会直接取出出该值
innodb没有保存表的总行数,如果使用select count() from table;就会遍历整个表,消耗相当大,但是在加了wehre 条件后,myisam和innodb处理的方式都一样。
4、全文索引
myisam支持 FULLTEXT类型的全文索引
innodb不支持FULLTEXT类型的全文索引,但是innodb可以使用sphinx插件支持全文索引,并且效果更好。(sphinx 是一个开源软件,提供多种语言的API接口,可以优化mysql的各种查询)
5、delete from table
使用这条命令时,innodb不会从新建立表,而是一条一条的删除数据,在innodb上如果要清空保存有大量数据的表,最 好不要使用这个命令。(推荐使用truncate table,不过需要用户有drop此表的权限)
6、索引保存位置
myisam的索引以表名+.MYI文件分别保存。
innodb的索引和数据一起保存在表空间里。
探索不同引擎Innodb和Myisam的索引优化方案的更多相关文章
- 常用Mysql存储引擎--InnoDB和MyISAM简单总结
常用Mysql存储引擎--InnoDB和MyISAM简单总结 2013-04-19 10:21:52| 分类: CCST|举报|字号 订阅 MySQL服务器采用了模块化风格,各部分之间保持相 ...
- MySQL存储引擎InnoDB,MyISAM
MySQL存储引擎InnoDB,MyISAM1.区别:(1)InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语 ...
- [转帖]mysql常用存储引擎(InnoDB、MyISAM、MEMORY、MERGE、ARCHIVE)介绍与如何选择
mysql常用存储引擎(InnoDB.MyISAM.MEMORY.MERGE.ARCHIVE)介绍与如何选择原创web洋仔 发布于2018-06-28 15:58:34 阅读数 1063 收藏展开 h ...
- MySQL两大存储引擎InnoDB与MyISAM
1.InnoDB存储引擎 MySQL5.5中InnoDB成为默认的存储引擎.InnoDB是事务型存储引擎,被设计用来处理大量的短期事务.它的性能和自动崩溃恢复特性,使得它在非事务场景中也很流行. 所以 ...
- MySQL存储引擎Innodb和MyISAM对比总结
Innodb引擎 InnoDB是一个事务型的存储引擎,设计目标是处理大数量数据时提供高性能的服务,它在运行时会在内存中建立缓冲池,用于缓冲数据和索引. Innodb引擎优点 1.支持事务处理.ACID ...
- MySql的多存储引擎架构, 默认的引擎InnoDB与 MYISAM的区别(滴滴)
1.存储引擎是什么? MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力.通过选择不同的技术, ...
- MySQL存储引擎 InnoDB与MyISAM的区别
来源:http://www.jb51.net/article/47597.htm 基本的差别:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能,其执行 ...
- mysql数据库引擎InnoDB和MyISAM
一.Mysql锁分类 表级锁:开销小,加锁块:不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低. 行级锁:开销大,加锁慢:会出现死锁:锁定粒度最小,发生锁冲突的概率最低,并发性也最高. 页面 ...
- MySQL存储引擎InnoDB与Myisam
InnoDB与Myisam的六大区别 InnoDB与Myisam的六大区别 MyISAM InnoDB 构成上的区别: 每个MyISAM在磁盘上存储成三个文件.第一个 文件的名字以表的名字开始,扩展名 ...
- InnoDB 和 MyISAM的索引区别
MyISAM索引实现 MyISAM索引文件和数据文件是分离的,索引文件的data域保存记录所在页的地址(物理存储位置),通过这些地址来读取页,进而读取被索引的行数据. MyISAM的索引原理图如下,C ...
随机推荐
- 2024已过半,还没试过在vue3中使用ioc容器吗?
Vue3 已经非常强大和灵活了,为什么还要引入 IOC 容器呢?IOC 容器离不开 Class,那么我们就从 Class 谈起 Class的应用场景 一提起 Class,大家一定会想到这是 Vue 官 ...
- 【Playwright+Python】系列教程(五)元素定位
一.常见元素定位 定位器是 Playwright 自动等待和重试能力的核心部分.简而言之,定位器代表了一种随时在页面上查找元素的方法,以下是常用的内置定位器. 1.按角色定位 按显式和隐式可访问性属性 ...
- 流式查询1. mybatis的游标Cursor,分页大数据查询
流式查询流式查询 指的是查询成功后不是返回一个集合而是返回一个迭代器,应用可以通过迭代器每次取一条查询结果.流式查询的好处是能够降低内存使用.例如我们想要从数据库取 1000 万条记录而又没有足够的内 ...
- Linux 破解mysql密码
mysql忘记密码怎么办 [root@master ~]# mysql -uroot -pHuawei123123$ mysql: [Warning] Using a password on the ...
- 【WSDL】01 JAX-WS 入门案例
去年这个时候工作遇见时暂时总结的笔记: https://www.cnblogs.com/mindzone/p/14777493.html 当时也不是很清楚,直到最近前同事又遇上了这项技术, 除了WSD ...
- 【Java】【常用类】Calendar 日历类
Calendar 日历类,我居然念错发音,来,好好看下音标 ['kælɪndə] 卡琳达 public class DateTest { public static void main(Strin ...
- Springboot实现HTML表单from简单的接收信息
HTML< from >元素 from可向Web服务器提交请求 普遍格式: <from action="服务器地址" method="请求方式" ...
- 数值优化算法-BFGS
参考: https://www.cnblogs.com/Leo_wl/p/3367323.html 牛顿法: 使用牛顿法优化函数 f(θ) 最小值时,每次计算获得新的\(θ\)值,即\(θ_{k+1} ...
- 如何配置docker pull代理
参考: https://blog.csdn.net/vic_qxz/article/details/130061661 经过验证确实有效.
- python学习之---迭代器与生成器
什么是迭代器 可迭代对象: 可以通过for循环来实现遍历,例如list.string.dict 迭代器: 不仅可以使用for循环,还可以使用next()方法.__iter__() next():获取容 ...