1. MySQL 中 varchar 与 char 的区别?varchar(50) 中的 50 代表的涵义?
- varchar 与 char 的区别,char 是一种固定长度的类型,varchar 则是一种可变长度的类型。
- varchar(50) 中 50 的涵义最多存放 50 个字符。varchar(50) 和 (200) 存储 hello 所占空间一样,但后者在执行
order by
语法时消耗更多内存, 因为order by按照采用最大的字节数(fixed_length)计算.
所以,实际场景下,选择合适的 varchar 长度还是有必要的。
2. int(11) 中的 11 代表什么涵义?
- int(n) 中的 n ,不影响字段存储的范围, 无论n取何值, 都是4字节存储
- n值只表示显示宽度, 规定用于显示宽度小于指定的列宽度的值时从左侧填满0。eg: int(6)的字段显示数字11->"000011"
3. 一张表,里面有 ID 自增主键,当 insert 了 17 条记录之后,删除了第 15,16,17 条记录,再把 MySQL 重启,再 insert 一条记录,这条记录的 ID 是 18 还是 15?
- 一般情况下,我们创建的表的类型是 InnoDB ,如果新增一条记录(不重启 MySQL 的情况下),这条记录的 ID 是18 ;但是如果重启 MySQL 的话,这条记录的 ID 是 15 。因为 InnoDB 表只把自增主键的最大 ID 记录到内存中,所以重启数据库或者对表 OPTIMIZE 操作,都会使最大 ID 丢失。
- 但是,如果我们使用表的类型是 MyISAM ,那么这条记录的 ID 就是 18 。因为 MyISAM 表会把自增主键的最大 ID 记录到数据文件里面,重启 MYSQL 后,自增主键的最大 ID 也不会丢失。
最后,还可以跟面试官装个 x ,生产数据,不建议进行物理删除记录。
4. 为什么mysql的默认引擎是innodb
主要原因如下:
- 【最重要】支持事务。
- 支持锁行和锁表,能支持更多的并发量。
- 查询不加锁,完全不影响查询。
- 支持崩溃后恢复。
5. 为什么mysql的count()使用innodb时比使用MyISAM时慢
对于 SELECT COUNT(*) FROM table 语句,在没有 WHERE 条件的情况下,InnoDB 比 MyISAM 可能会慢很多,尤其在大表的情况下。因为,InnoDB 是去实时统计结果,会全表扫描;而 MyISAM 内部维持了一个计数器,预存了结果,所以直接返回即可。
https://blog.csdn.net/qq_15037231/article/details/81179383
6. 【重点】什么是索引?
mysql的索引, 记录了数据所在文件的行数, 根据这个行数可以直接取到数据, 不用扫表
-
索引有什么好处?
- 降低IO成本: 因为索引可以直接定位到行, 避免了大块的数据移动从磁盘到内存
- 降低数据排序的成本: 索引的建立过程中会对数据排序(B+树的叶子结点形成成了一个链表,从左到右值依次增大); 若该字段正好需要排序,则正好降低了排序的成本。
-
索引有什么坏处?
- 索引使得update操作变慢, 因为更新数据时也要更新数据的索引表
-
索引的使用场景?
- 对非常小的表,大部分情况下全表扫描效率更高。
- 对中大型表,索引非常有效。
- 特大型的表,建立和使用索引的代价不断增长; 此时可以选择"分库分表"
-
索引的类型?
- 普通索引:最基本的索引,没有任何约束。
- 唯一索引:与普通索引类似,但具有唯一性约束。
- 主键索引:特殊的唯一索引,不允许有空值。
- 复合索引:将多个列组合在一起创建索引,可以覆盖多个列。
- 外键索引:只有InnoDB类型的表才可以使用外键索引,保证数据的一致性、完整性和实现级联操作。
- 全文索引:为了模糊查询而设计的整个字段的索引; MySQL 自带的全文索引只能用于 InnoDB、MyISAM ,并且只能对英文进行全文检索,一般使用全文索引引擎。
在一般情况下,模糊查询都是通过 like 的方式进行查询。但是,对于海量数据,这并不是一个好办法,在 like “value%” 可以使用索引,但是对于 like “%value%” 这样的方式,执行全表查询,这在数据量小的表,不存在性能问题,但是对于海量数据,全表扫描是非常可怕的事情,所以 like 进行模糊匹配性能很差。常用的全文索引引擎的解决方案有 Elasticsearch、Solr 等等。最为常用的是 Elasticsearch 。
-
索引的“创建”原则有哪些
- 最适合索引的列是出现在 WHERE 子句中的列,或连接子句中的列,而不是出现在 SELECT 关键字后的列。
- 索引列的基数越大(索引字段取不同值的个数越大),索引效果越好。
- 根据情况创建复合索引,复合索引可以提高查询效率。
因为复合索引的基数会更大。
- 避免创建过多的索引,索引会额外占用磁盘空间,降低写操作效率。
- 主键尽可能选择较短的数据类型,可以有效减少索引的磁盘占用提高查询效率。
- 对字符串进行索引,应该定制一个前缀长度,可以节省大量的索引空间。
-
MySQL 索引的“使用”注意事项?
- 应尽量避免在
WHERE
子句中使用!= 或 <>
操作符,否则将引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。
注意,column IS NULL 也是不可以使用索引的。
- 应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,
如:SELECT id FROM t WHERE num = 10 OR num = 20 。
- 应尽量避免在 WHERE 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
select * from news where id / 100 = 1 为了使用索引,防止执行全表扫描,可以进行改造。 select * from news where id = 1 * 100
- 应尽量避免在 WHERE 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。
select * from news where year(publish_time) < 2017 为了使用索引,防止执行全表扫描,可以进行改造。 select * from news where publish_time < '2017-01-01'
- 多个单列索引并不是最佳选择, 应该使用复合索引, 复合索引遵循前缀原则。
MySQL 只能使用一个索引,会从多个索引中选择一个限制最为严格的索引,因此,为多个列创建单列索引,并不能提高 MySQL 的查询性能。 假设,有两个单列索引,分别为 news_year_idx(news_year) 和 news_month_idx(news_month)。现在,有一个场景需要针对资讯的年份和月份进行查询,那么,SQL 语句可以写成: select * from news where news_year = 2017 and news_month = 1 事实上,MySQL 只能使用一个单列索引。为了提高性能,可以使用复合索引 news_year_month_idx(news_year, news_month) 保证 news_year 和 news_month 两个列都被索引覆盖。
- 复合索引的最左前缀原则(只有在where条件中使用了复合索引的第一个字段,索引才会被使用)
复合索引遵守“最左前缀”原则,即在查询条件中使用了复合索引的第一个字段,索引才会被使用。因此,在复合索引中索引列的顺序至关重要。如果不是按照索引的最左列开始查找,则无法使用索引。 假设,有一个场景只需要针对资讯的月份进行查询,那么,SQL 语句可以写成: select * from news where news_month = 1 此时,无法使用 news_year_month_idx(news_year, news_month) 索引,因为遵守“最左前缀”原则,在查询条件中没有使用复合索引的第一个字段,索引是不会被使用的。
- 如果 MySQL 评估使用索引比全表扫描更慢,会放弃使用索引。如果此时想要索引,可以在语句中添加强制索引。
- 列类型是字符串类型,查询时一定要给值加引号,否则索引失效。
- LIKE 查询可能会引起索引失效:
like 的方式进行查询,在 like “value%” 可以使用索引,但是对于 like “%value%” 这样的方式,执行全表查询。如果需要模糊匹配,可以使用全文索引。
- 应尽量避免在
以下三条 SQL 如何建索引,只建一条怎么建?
WHERE a = 1 AND b = 1
WHERE b = 1
WHERE b = 1 ORDER BY time DESC
按照复合索引的最左前缀原则, 以顺序 b , a, time 建立复合索引,CREATE INDEX table1_b_a_time ON index_test01(b, a, time)。
对于第一条 SQL ,因为最新 MySQL 版本会优化 WHERE 子句后面的列顺序,以匹配复合索引顺序。
- 一个查询用到了哪个索引,如何查看?
EXPLAIN
显示了 MYSQL 如何使用索引来处理SELECT
语句以及连接表,可以帮助选择更好的索引和写出更优化的查询语句。
使用方法,在 SELECT 语句前加上 EXPLAIN 就可以了。
7. 【重点】MySQL 索引的原理?
-
innoDB使用B+树作为索引存储的数据结构
- innodb中, 有
页
的概念, 默认页的大小设置为16kb, 表示磁盘和内存IO时一次的交换大小. 由于B+树数据结构中, 处于外存的每个文件就是B+树的一个节点, 因此, Innodb一个索引节点最大有16kb,按照INT栈4字节, 指针类型占4字节计算, 可以存放(16kb/4B+4B)=1000个索引. 因此如果把inodb的索引看成1000阶的B+树, 则3层的B+树就可对条记录做索引 - innoDB索引的
根节点会常驻内存
, 只有在切换节点或修改节点时才发生磁盘IO, 且由于innodb索引是高阶B+树, 因此, IO次数一般3次左右 - 对于一般的B+树, 只有叶子结点才存储真正的索引, 其它节点只是存储的key序列, innodB的索引文件也一样, 只有叶子结点才记录着被索引的row在磁盘什么位置. 但与一般B+树不同的是, 一般B+树只会把叶子结点从左到右串成一个链表来应付顺序查找, 而innodb将所有节点串成环形链表, 且维护2个指针:一个指向根节点,一个指向key最小的叶子结点; 以此来应付对于主键的
范围查找和分页查找
- innodb中, 有
-
有哪些索引类型: 聚簇索引和非聚簇索引
索引分为聚簇索引主键索引和非主键索引, 聚簇索引的叶子结点是<主键值, row的数据>; 非聚簇索引的叶子结点 是<非主键列的值, 主键值>; 非聚簇索引是聚簇索引的二级索引; - 聚簇索引和非聚簇索引
- 在《数据库原理》一书中说, 聚簇索引的叶子结点就是数据节点; 非聚簇索引的叶子结点仍然是索引节点, 只不过有能指向数据的指针.
- innoDB中, 用聚簇索引对主键做索引, 用非聚簇索引索引做非主键列的索引;
非主键列的索引其叶子结点存储着其对应行的主键值. 在非主键列上查找时, 先通过非聚簇索引找到属于自己的主键值, 然后用这个主键值在聚簇索引中查找row的数据. 因此, 可以说非聚簇索引是聚簇索引的二级索引. 之所以非聚簇索引存储的是主键值而不是指向数据行的行指针, 是为了避免当数据移动时维护B+树的结构需要同时修改锁哥索引文件, 有了这种设计, 值修改主键索引(聚簇索引)的row index即可
- 没设置主键的情况下, innoDB如何安排聚簇索引
如果没设置主键, innoDB会隐式的将唯一且非空的列作为主键, 如果没有这样的列, 生成一个隐藏列作为主键
- 聚簇索引和非聚簇索引
-
聚簇索引有什么应该注意的地方
聚簇索引最大限度的提高了IO密集型应用的性能, 但也有如下几个限制- 聚簇索引的插入速度严重依赖于插入顺序
顺序插入是最快的插入方式, 因为创建B+树的节点时. 顺序插入的索引都会处于一个页中, 非顺序插入可能会插入到不同页, 增大了IO次数. 这也是为什么会定义一个与业务无关的自增主键作为聚簇索引的原因 - 主键一般要定义为不可更新
因为更新主键, 可能会导致其所在行移动, 行指针变化, 所以我们一般定义主键为不可更新。 - 二级索引(非聚簇索引)访问需要两次索引查找,第一次找到主键值,第二次根据主键值(聚簇索引)找到行数据。
- 主键 ID 建议使用整型。
因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。
- 聚簇索引的插入速度严重依赖于插入顺序
-
什么是最左匹配原则
使用复合索引时, B+树中的key也是符合项, 比如索引(name, age, sex)
的时候,B+Tree 是按照从左到右的顺序来建立搜索树的。先用待查记录的name比较, 找到name相同的索引后再比较age, 最后比较sex. 如果查询语句中的条件只有(age,sex), 则因为缺少最左面的name条件, 导致索引全部不可用. 这就是最左匹配原则- 比如当 (张三, 20, F) 这样的数据来检索的时候,B+Tree 会优先比较 name 来确定下一步的所搜方向,如果 name 相同再依次比较 age 和 sex ,最后得到检索的数据。
- 但当 (20, F) 这样的没有 name 的数据来的时候,B+Tree 就不知道下一步该查哪个节点,因为建立搜索树的时候 name 就是第一个比较因子,必须要先根据 name 来搜索才能知道下一步去哪里查询。
- 比如当 (张三, F) 这样的数据来检索时,B+Tree 可以用 name 来指定搜索方向,但下一个字段 age 的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是 F 的数据了。
-
(附加)优化你的索引-运用Hash以及BitMap? BitMap与BloomFIlter的区别?
Innodb中还支持一种索引构建方式: hash索引, 拥有比B+树索引更高效的查找时间复杂度; innodb没有把它作为默认索引是因为:- hash索引处理场景有限
Hash 索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询。 - hash索引不附带排序效果
B+树索引在索引构建完毕后, 数据就自然排序了 - hash索引的构建方式对符合索引不利
B+树索引在构建复合索引时, 非叶子节点的查找可以先根据符合索引的key1进行比较, 在对key2进行比较. 即若对符合索引构建索引后, 相当于 也已经是索引; 而hash索引只能利用整个符合索引的列计算hash值, 对条件 的查询无法利用已存在的符合索引 - Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。
- hash索引处理场景有限
8. 【重点】请说说 MySQL 的四种事务隔离级别?
-
本地事务的4个级别是什么
- 原子性 Atomicity :一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
- 一致性 Consistency :在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器)、级联回滚等。
- 隔离性 Isolation :数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括:
- 读未提交(Read uncommitted)、
- 读提交(read committed)、
- 可重复读(repeatable read)、
- 串行化(Serializable)。
- 持久性 Durability :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
-
并发事务下的3种错误读
实际场景下,事务并不是串行的,所以会带来如下三个问题:- 另一个事务回滚导致的脏读 : 事务 A 读取了事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据。
- 另一个事务并发修改导致的不可重复读 : 事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果不一致。
- 另一个事务增加数据导致的幻读 : 系统管理员 A 将数据库中所有学生的成绩从具体分数改为 ABCDE 等级,但是系统管理员 B 就在这个时候插入了一条具体分数的记录,当系统管理员 A 改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。 解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
Myql事务隔离级别
9. 【重点】Mysql的锁机制
-
从读写锁的角度看: 共享锁和排它锁
当多个查询同一时刻进行数据修改时,就会产生并发控制的问题。MySQL 的共享锁和排他锁,就是读锁和写锁。- 共享锁:不堵塞,多个用户可以同时读一个资源,互不干扰。
- 排他锁:一个写锁会阻塞其他的读锁和写锁,这样可以只允许一个用户进行写入,防止其他用户读取正在写入的资源。
-
从锁粒度的角度看: 锁行和锁表
- 表锁:系统开销最小,会锁定整张表,MyIsam 使用表锁。
- 行锁:最大程度的支持并发处理,但是也带来了最大的锁开销,InnoDB 使用行锁。
-
悲观锁和乐观锁
- 悲观锁 : 数据库的内置锁都是悲观锁(共享锁,排它锁)
在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。 - 乐观锁 : 为数据库表增加一个int类型的“version”字段, 实现并发修改下的CAS操作
- 读数据时, 将版本号一起读出
- 修改数据时, 同时将新的字段值和新的版本号更新, 且只有表中的版本号小于这个版本号时才能更新成功,否则认为是过期数据。
update t_goods set status=2,version=${newVersion} where id=#{id} and version<${newVersion};
- 悲观锁 : 数据库的内置锁都是悲观锁(共享锁,排它锁)
-
什么是死锁
多数情况下,可以认为如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。简单的说,进程 A 等待进程 B 释放他的资源,B 又等待 A 释放他的资源,这样就互相等待就形成死锁。死锁的发生必须具备以下四个必要条件:- 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
- 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
- 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
- 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合 {P0,P1,P2,•••,Pn} 中的 P0 正在等待一个 P1 占用的资源;P1 正在等待 P2 占用的资源,……,Pn 正在等待已被 P0 占用的资源。
下列方法有助于最大限度地降低死锁:
- 设置获得锁的超时时间。
通过超时,至少保证最差最差最差情况下,可以有退出的口子。
- 按同一顺序访问对象。
这个是最重要的方式。
- 避免事务中的用户交互。
- 保持事务简短并在一个批处理中。
- 使用低隔离级别。
- 使用绑定连接。
10. Innodb 的事务与日志的实现方式
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-transaction-model.html
11. 【重要】MySQL 查询执行顺序?
- RBO谓词下推, 限制性where
- join
- group by
- having
- order by
- limit
12. 【重要】聊聊 MySQL SQL 优化?
-
叙述几种优化手段?
- 如果使用了索引, 索引字段的where条件要满足MySQL 索引的“使用”注意事项?
- 用exist代替in, 或用join代替in
- 尽量使用数字类型的字段
若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
-
MySQL 数据库 CPU 飙升到 500% 的话,怎么处理?
- 当 CPU 飙升到 500% 时,先用操作系统命令 top 命令观察是不是 mysqld 占用导致的,如果不是,找出占用高的进程,并进行相关处理。
如果此时是 IO 压力比较大,可以使用 iostat 命令,定位是哪个进程占用了磁盘 IO 。
- 如果是 mysqld 造成的,使用 show processlist 命令,看看里面跑的 Session 情况,是不是有消耗资源的 SQL 在运行。找出消耗高的 SQL ,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。一般来说,肯定要 kill 掉这些线程(同时观察 CPU 使用率是否下降),等进行相应的调整(比如说加索引、改 SQL 、改内存参数)之后,再重新跑这些 SQL。
也可以查看 MySQL 慢查询日志,看是否有慢 SQL 。
- 也有可能是每个 SQL 消耗资源并不多,但是突然之间,有大量的 Session 连进来导致 CPU 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等。
-
在 MySQL 服务器运行缓慢的情况下输入什么命令能缓解服务器压力?
这个回答,和上面的回答思路是差不多的,优秀在更有层次感。- 检查系统的状态
通过操作系统的一些工具检查系统的状态,比如 CPU、内存、交换、磁盘的利用率,根据经验或与系统正常时的状态相比对,有时系统表面上看起来看空闲,这也可能不是一个正常的状态,因为 CPU 可能正等待IO的完成。除此之外,还应观注那些占用系统资源(CPU、内存)的进程。- 使用 sar 来检查操作系统是否存在 IO 问题。
- 使用 vmstat 监控内存 CPU 资源。
- 磁盘 IO 问题,处理方式:做 raid10 提高性能 。
- 网络问题,telnet 一下 MySQL 对外开放的端口。如果不通的话,看看防火墙是否正确设置了。另外,看看 MySQ L是不是开启了 skip-networking 的选项,如果开启请关闭。
- 检查 MySQL 参数
max_connect_errors
connect_timeout
skip-name-resolve
slave-net-timeout=seconds
master-connect-retry - 检查 MySQL 相关状态值
关注连接数
关注下系统锁情况
关注慢查询(slow query)日志
- 检查系统的状态
13.【加分】什么是 MVCC ?
14. MySQL binlog 的几种日志录入格式以及区别
- Mysql一共有几种日志 ?
- 错误日志(error log) : 关于mysqld的启动, 运行, 停止过程中出错的日志
- 查询日志(general query log) : 接收到的客户端statement
- 二进制日志(binary log) : 客户端发来的会改变数据的statement(用于备份)
- 转发日志(relay log) : 从master发送来的数据更改消息
- 慢查询日志(slow query log) : 记录查询时间超过
long_query
秒数的query - DDL日志(meta log) : 记录DDL操作
除了error log自动开启,其他日志都要先配置才能开启(DDL日志不提供配置, 会在需要的时候自动记录) 查看这些日志是否开启可使用如下语法, 比如看binlog是否开启
mysql> show variables like '%bin%';
+--------------------------------------------+----------------------+
| Variable_name | Value |
+--------------------------------------------+----------------------+
| bind_address | 127.0.0.1 |
| binlog_cache_size | 32768 |
| binlog_checksum | CRC32 |
| binlog_direct_non_transactional_updates | OFF |
| binlog_error_action | ABORT_SERVER |
.... ... ...
+--------------------------------------------+----------------------+
- bin log功能
记录会修改数据库的"事件", 比如建表, 修改表记录等. bin log有2个目的:- 备份: master上的bin log提供数据修改的记录, 然后将这个bin log反送给slave. slave执行记录的这些"事件"
- 数据恢复: 当数据库进行备份后, 执行binary log会让数据库恢复至备份点
开启bin log可能会影响一些性能
- bin log的格式
/etc/my.cnf
https://dev.mysql.com/doc/refman/5.7/en/binary-log.html
15. MySQL 主从复制的流程是怎么样的?
16. 聊聊 MySQL 备份方式?备份策略是怎么样的?
17. Mysql Join实现
join有三种算法,分别是Nested Loop Join,Hash join,Sort Merge Join。MySQL官方文档中提到,MySQL只支持Nested Loop Join这一种join algorithm
https://blog.csdn.net/orangleliu/article/details/72850659
文中不仅讲解了最简单的SNLJ, 还讲解了join key是索引时, 使用INLJ, 非索引时使用缓存的BNLJ. 此外对比了Hash join, 这种BNLJ的进化版(虽然mysql不支持), 从双循环的SNLJ, 进化到BNLJ, 缓存外侧join的记录, 到hash join的进化, 都是在尽量减少内侧循环(内表)的全表扫描次数. 这也解释了sort merge join内表外表都只扫描一次的大数据解决方案