【Mysql】Mysql中表连接的原理

连接简介

在实际工作中,我们需要查询的数据很可能不是放在一张表中,而是需要同时从多张表中获取。下面我们以简单的两张表为例来进行说明。

连接的本质

为方便测试说明,,先创建两个简单的表并给它们填充一点数据:

mysql> CREATE TABLE t1 (m1 int, n1 char(1));
Query OK, 0 rows affected (0.12 sec)
mysql> CREATE TABLE t2 (m2 int, n2 char(1));
Query OK, 0 rows affected (0.13 sec)
mysql> INSERT INTO t1 VALUES(1, 'a'), (2, 'b'), (3, 'c');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> INSERT INTO t2 VALUES(2, 'b'), (3, 'c'), (4, 'd');
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0

以上是建立的两个表 t1 和 t2 ,两个表都有两个列,一个是 INT 类型的,一个是 CHAR(1) 类型的,填充的表数据如下:

mysql> SELECT * FROM t1;
+------+------+
| m1 | n1 |
+------+------+
| 1 | a |
| 2 | b |
| 3 | c |
+------+------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM t2;
+------+------+
| m2 | n2 |
+------+------+
| 2 | b |
| 3 | c |
| 4 | d |
+------+------+
3 rows in set (0.00 sec)

连接的本质就是把各个连接表中的记录都取出来并将依次匹配的组合加入结果集并返回给用户。所以我们把 t1 和t2 两个表连接起来的过程如下图所示:
在这里插入图片描述
这个过程看起来就是把 t1 表的记录和 t2 的记录连起来组成新的更大的记录,所以这个查询过程称之为连接查询。连接查询的结果集中包含一个表中的每一条记录与另一个表中的每一条记录相互匹配的组合,这样的组合集就可以称之为笛卡尔积 。因为表 t1 中有3条记录,表 t2 中也有3条记录,所以这两个表连接之后的笛卡尔积就有 3×3=9 行记录。在 MySQL 中,连接查询的语法也很随意,只要在 FROM 语句后边跟多个表名就好了,比如我们把 t1 表和 t2 表连接起来的查询语句可以写成这样:

mysql> SELECT * FROM t1, t2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    1 | a    |    2 | b    |
|    2 | b    |    2 | b    |
|    3 | c    |    2 | b    |
|    1 | a    |    3 | c    |
|    2 | b    |    3 | c    |
|    3 | c    |    3 | c    |
|    1 | a    |    4 | d    |
|    2 | b    |    4 | d    |
|    3 | c    |    4 | d    |
+------+------+------+------+
9 rows in set (0.00 sec)

连接过程简介

理论上我们可以连接任意数量张表,但是如果没有任何限制条件的话,这些表连接起来产生的 笛卡尔积可能是非常巨大的。比方说3个100行记录的表连接起来产生的 笛卡尔积就有 100×100×100=1000000 行数据!所以在连接的时候过滤掉特定记录组合是有必要的,在连接查询中的过滤条件可以分成两种:

  • 涉及单表的条件
    这种只设计单表的过滤条件我们之前都提到过一万遍了,我们之前也一直称为搜索条件 ,比如 t1.m1 > 1是只针对 t1 表的过滤条件, t2.n2 < ‘d’ 是只针对 t2 表的过滤条件。
  • 涉及两表的条件
    比如 t1.m1 = t2.m2 、 t1.n1 < t2.n2 等,这些条件中涉及到了两个表,我们稍后会分析这种过滤条件是如何使用的.
    下边我们就要看一下携带过滤条件的连接查询的大致执行过程了,比方说下边这个查询语句:
    SELECT * FROM t1, t2 WHERE t1.m1 > 1 AND t1.m1 = t2.m2 AND t2.n2 < ‘d’;
    在这个查询中我们指明了这三个过滤条件:
  • t1.m1 > 1
  • t1.m1 = t2.m2
  • t2.n2 < ‘d’
    这个连接查询的大致执行过程如下:
    1 . 首先确定第一个需要查询的表,这个表称之为驱动表 。单表中执行查询语句按照前一章节的方式进行,只需要选取代价最小的那种访问方法去执行单表查询语句就好了(依次是const、ref、ref_or_null、range、index、all这些执行方法中选取代价最小的去执行查询)。此处假设使用 t1 作为驱动表,那么就需要到 t1 表中找满足 t1.m1 > 1 的记录,因为表中的数据太少,我们也没在表上建立二级索引,所以此处查询 t1 表的访问方法应该为all,也就是采用全表扫描的方式执行单表查询。关于如何提升连接查询的性能我们之后再说,现在先把基本概念搞清楚。所以查询过程就如下图所示:
    在这里插入图片描述
    我们可以看到, t1 表中符合 t1.m1 > 1 的记录有两条。
    2 . 针对上一步骤中从驱动表产生的结果集中的每一条记录,分别需要到 t2 表中查找匹配的记录,所谓匹配的记录 ,指的是符合过滤条件的记录。因为是根据 t1 表中的记录去找 t2 表中的记录,所以 t2 表也可以被称之为被驱动表 。上一步骤从驱动表中得到了2条记录,所以需要查询2次 t2 表。此时涉及两个表的列的过滤条件 t1.m1 = t2.m2 就派上用场了:
  • 当 t1.m1 = 2 时,过滤条件 t1.m1 = t2.m2 就相当于 t2.m2 = 2 ,所以此时 t2 表相当于有了 t2.m2 =2 、 t2.n2 < ‘d’ 这两个过滤条件,然后到 t2 表中执行单表查询。
  • 当 t1.m1 = 3 时,过滤条件 t1.m1 = t2.m2 就相当于 t2.m2 = 3 ,所以此时 t2 表相当于有了 t2.m2 =3 、 t2.n2 < ‘d’ 这两个过滤条件,然后到 t2 表中执行单表查询。
    所以整个连接查询的执行过程就如下图所示:
    在这里插入图片描述
    也就是说整个连接查询最后的结果只有两条符合过滤条件的记录:
m1n1m2n2
2b2b
3c3c
从上边两个步骤可以看出来,我们上边唠叨的这个两表连接查询共需要查询1次 t1 表,2次 t2 表。当然这是在特定的过滤条件下的结果,如果我们把 t1.m1 > 1 这个条件去掉,那么从 t1 表中查出的记录就有3条,就需要查询3次 t2 表了。也就是说在两表连接查询中,驱动表只需要访问一次,被驱动表可能被访问多次。

内连接和外连接

为方便大家理解,我们先创建两个比较实用的表,

CREATE TABLE student (
number INT NOT NULL AUTO_INCREMENT COMMENT '学号',
name VARCHAR(5) COMMENT '姓名',
major VARCHAR(30) COMMENT '专业',
PRIMARY KEY (number)
) Engine=InnoDB CHARSET=utf8 COMMENT '学生信息表';
Query OK, 0 rows affected (0.12 sec)

CREATE TABLE score (
number INT COMMENT '学号',
subject VARCHAR(30) COMMENT '科目',
score TINYINT COMMENT '成绩',
PRIMARY KEY (number, score)
) Engine=InnoDB CHARSET=utf8 COMMENT '学生成绩表';
Query OK, 0 rows affected (0.18 sec)

我们新建了一个学生信息表和一个学生成绩表,然后我们向上述两个表中插入一些数据,插入后两表中的数据如下:

mysql> insert into student values(20230101,'张三','石油工程'),(20230102,'李四','测控技术'),(20230103,'王五','通信工程');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> insert into score values(20230101,'高等数学','76'),(20230102,'模拟电路','92'),(20230102,'模拟电路','86'),(20230102,'高等数学','95');
Query OK, 4 rows affected (0.06 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from student;
+----------+--------+--------------+
| number   | name   | major        |
+----------+--------+--------------+
| 20230101 | 张三   | 石油工程     |
| 20230102 | 李四   | 测控技术     |
| 20230103 | 王五   | 通信工程     |
+----------+--------+--------------+
3 rows in set (0.00 sec)

mysql>  select * from score;
+----------+--------------+-------+
| number   | subject      | score |
+----------+--------------+-------+
| 20230101 | 高等数学     |    76 |
| 20230102 | 模拟电路     |    86 |
| 20230102 | 模拟电路     |    92 |
| 20230102 | 高等数学     |    95 |
+----------+--------------+-------+
4 rows in set (0.00 sec)

现在我们想把每个学生的考试成绩都查询出来就需要进行两表连接了(因为 score 中没有姓名信息,所以不能单纯只查询 score 表)。连接过程就是从 student 表中取出记录,在 score 表中查找 number 相同的成绩记录,所以过滤条件就是 student.number = socre.number ,整个查询语句就是这样:

mysql> SELECT * FROM student, score WHERE student.number = score.number;
+----------+--------+--------------+----------+--------------+-------+
| number   | name   | major        | number   | subject      | score |
+----------+--------+--------------+----------+--------------+-------+
| 20230101 | 张三   | 石油工程     | 20230101 | 高等数学     |    76 |
| 20230102 | 李四   | 测控技术     | 20230102 | 模拟电路     |    86 |
| 20230102 | 李四   | 测控技术     | 20230102 | 模拟电路     |    92 |
| 20230102 | 李四   | 测控技术     | 20230102 | 高等数学     |    95 |
+----------+--------+--------------+----------+--------------+-------+
4 rows in set (0.00 sec)

字段有点重复,我们精简一下:

mysql> SELECT s1.number, s1.name, s1.major , s2.subject, s2.score FROM student AS s1, score AS s2 where  s1.number = s2.number;
+----------+--------+--------------+--------------+-------+
| number   | name   | major        | subject      | score |
+----------+--------+--------------+--------------+-------+
| 20230101 | 张三   | 石油工程     | 高等数学     |    76 |
| 20230102 | 李四   | 测控技术     | 模拟电路     |    86 |
| 20230102 | 李四   | 测控技术     | 模拟电路     |    92 |
| 20230102 | 李四   | 测控技术     | 高等数学     |    95 |
+----------+--------+--------------+--------------+-------+
4 rows in set (0.00 sec)

从上述查询结果中我们可以看到,各个同学对应的各科成绩就都被查出来了,可是有个问题, 王五同学,也就是学号为 20230103的同学因为某些原因没有参加考试,所以在 score 表中没有对应的成绩记录。那如果老师想查看所有同学的考试成绩,即使是缺考的同学也应该展示出来,但是到目前为止我们介绍的连接查询是无法完成这样的需求的。我们稍微思考一下这个需求,其本质是想:驱动表中的记录即使在被驱动表中没有匹配的记录,也仍然需要加入到结果集。为了解决这个问题,就有了内连接和外连接的概念:

  • 对于内连接的两个表,驱动表中的记录在被驱动表中找不到匹配的记录,该记录不会加入到最后的结果集,我们上边提到的连接都是所谓的内连接 。(交集?)
  • 对于外连接的两个表,驱动表中的记录即使在被驱动表中没有匹配的记录,也仍然需要加入到结果集。
    在 MySQL 中,根据选取驱动表的不同,外连接仍然可以细分为2种:
    • 左外连接
      选取左侧的表为驱动表。
    • 右外连接
      选取右侧的表为驱动表。
      可是这样仍然存在问题,即使对于外连接来说,有时候我们也并不想把驱动表的全部记录都加入到最后的结果集。这就犯难了,有时候匹配失败要加入结果集,有时候又不要加入结果集,怎么解决呢,其实把过滤条件分为两种这个问题就解决了,放在不同地方的过滤条件是有不同语义的:
  • WHERE 子句中的过滤条件
    WHERE 子句中的过滤条件就是我们平时见的那种,不论是内连接还是外连接,凡是不符合 WHERE 子句中的过滤条件的记录都不会被加入最后的结果集。
  • ON 子句中的过滤条件
    对于外连接的驱动表的记录来说,如果无法在被驱动表中找到匹配 ON 子句中的过滤条件的记录,那么该记录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用 NULL 值填充。
    需要注意的是,这个ON子句是专门为外连接驱动表中的记录在被驱动表找不到匹配记录时应不应该把该记录加入结果集这个场景下提出的,所以如果把 ON 子句放到内连接中, MySQL 会把它和 WHERE 子句一样对待,也就是说:内连接中的WHERE子句和ON子句是等价的。

一般情况下,我们都把只涉及单表的过滤条件放到 WHERE 子句中,把涉及两表的过滤条件都放到 ON 子句中,我们也一般把放到 ON 子句中的过滤条件也称之为连接条件 。
注意:左外连接和右外连接简称左连接和右连接。

左(外)连接的语法

左(外)连接的语法还是挺简单的,比如我们要把 t1 表和 t2 表进行左外连接查询可以这么写:
SELECT * FROM t1 LEFT [OUTER] JOIN t2 ON 连接条件 [WHERE 普通过滤条件];
其中中括号里的 OUTER 单词是可以省略的。对于 LEFT JOIN 类型的连接来说,我们把放在左边的表称之为外表或者驱动表,右边的表称之为内表或者被驱动表。所以上述例子中 t1 就是外表或者驱动表, t2 就是内表或者被驱动表。需要注意的是,对于左(外)连接和右(外)连接来说,必须使用 ON 子句来指出连接条件。了解了左(外)连接的基本语法之后,再次回到我们上边那个现实问题中来,看看怎样写查询语句才能把所有的学生的成绩信息都查询出来,即使是缺考的考生也应该被放到结果集中:

mysql> SELECT s1.number, s1.name, s2.subject, s2.score FROM student AS s1 LEFT JOIN score AS s2 ON s1.number = s2.number;
+----------+--------+--------------+-------+
| number   | name   | subject      | score |
+----------+--------+--------------+-------+
| 20230101 | 张三   | 高等数学     |    76 |
| 20230102 | 李四   | 模拟电路     |    86 |
| 20230102 | 李四   | 模拟电路     |    92 |
| 20230102 | 李四   | 高等数学     |    95 |
| 20230103 | 王五   | NULL         |  NULL |
+----------+--------+--------------+-------+
5 rows in set (0.00 sec)

从结果集中可以看出来,虽然 王五并没有对应的成绩记录,但是由于采用的是连接类型为左(外)连接,所以仍然把她放到了结果集中,只不过在对应的成绩记录的各列使用 NULL 值填充而已。

右(外)连接的语法

右(外)连接和左(外)连接的原理是一样一样的,语法也只是把 LEFT 换成 RIGHT 而已:
SELECT * FROM t1 RIGHT [OUTER] JOIN t2 ON 连接条件 [WHERE 普通过滤条件];
只不过驱动表是右边的表,被驱动表是左边的表。

mysql> SELECT s1.number, s1.name, s2.subject, s2.score FROM score AS s2 right  JOIN student  AS s1 ON s1.number = s2.number;

内连接的语法

内连接和外连接的根本区别就是在驱动表中的记录不符合 ON 子句中的连接条件时不会把该记录加入到最后的结果集,我们最开始说的那些连接查询的类型都是内连接。不过之前仅仅提到了一种最简单的内连接语法,就是直接把需要连接的多个表都放到 FROM 子句后边。其实针对内连接,MySQL提供了好多不同的语法,我们以 t1和 t2 表为例:
SELECT * FROM t1 [INNER | CROSS] JOIN t2 [ON 连接条件] [WHERE 普通过滤条件];
也就是说在 MySQL 中,下边这几种内连接的写法都是等价的:

  • SELECT * FROM t1,t2;
  • SELECT * FROM t1 JOIN t2;
  • SELECT * FROM t1 INNER JOIN t2;
  • SELECT * FROM t1 CROSS JOIN t2;

现在我们虽然介绍了很多种内连接 的书写方式,不过熟悉一种就好了,我们推荐 INNER JOIN 的形式书写内连接(因为 INNER JOIN 语义很明确,可以和 LEFT JOIN 和 RIGHT JOIN 很轻松的区分开)。这里需要注意的是,由于在内连接中ON子句和WHERE子句是等价的,所以内连接中不要求强制写明ON子句。

我们前边说过,连接的本质就是把各个连接表中的记录都取出来依次匹配的组合加入结果集并返回给用户。不论哪个表作为驱动表,两表连接产生的笛卡尔积肯定是一样的。而对于内连接来说,由于凡是不符合 ON 子句或WHERE 子句中的条件的记录都会被过滤掉,其实也就相当于从两表连接的笛卡尔积中把不符合过滤条件的记录给踢出去,所以对于内连接来说,驱动表和被驱动表是可以互换的,并不会影响最后的查询结果。但是对于外连接来说,由于驱动表中的记录即使在被驱动表中找不到符合 ON 子句连接条件的记录,所以此时驱动表和被驱动表
的关系就很重要了,也就是说左外连接和右外连接的驱动表和被驱动表不能轻易互换。

连接的原理

嵌套循环连接(Nested-Loop Join)

我们前边说过,对于两表连接来说,驱动表只会被访问一遍,但被驱动表却要被访问到好遍,具体访问几遍取决于对驱动表执行单表查询后的结果集中的记录条数。对于内连接来说,选取哪个表为驱动表都没关系,而外连接的驱动表是固定的,也就是说左(外)连接的驱动表就是左边的那个表,右(外)连接的驱动表就是右边的那个表。我们上边已经大致介绍过 t1 表和 t2 表执行内连接查询的大致过程,大致过程如下:

  • 步骤1:选取驱动表,使用与驱动表相关的过滤条件,选取代价最低的单表访问方法来执行对驱动表的单表查询。
  • 步骤2:对上一步骤中查询驱动表得到的结果集中每一条记录,都分别到被驱动表中查找匹配的记录。

通用的两表连接过程如下图所示:
在这里插入图片描述
如果有3个表进行连接的话,那么 步骤2 中得到的结果集就像是新的驱动表,然后第三个表就成为了被驱动表,
重复上边过程,也就是 步骤2 中得到的结果集中的每一条记录都需要到 t3 表中找一找有没有匹配的记录,用伪
代码表示一下这个过程就是这样:
for each row in t1 { #此处表示遍历满足对t1单表查询结果集中的每一条记录
for each row in t2 { #此处表示对于某条t1表的记录来说,遍历满足对t2单表查询结果集中的
每一条记录
for each row in t3 { #此处表示对于某条t1和t2表的记录组合来说,对t3表进行单表查询
if row satisfies join conditions, send to client
}
}
}
这个过程就像是一个嵌套的循环,所以这种驱动表只访问一次,但被驱动表却可能被多次访问,访问次数取决于对驱动表执行单表查询后的结果集中的记录条数的连接执行方式称之为 嵌套循环连接 ( Nested-Loop Join ),这是最简单,也是最笨拙的一种连接查询算法。

使用索引加快连接速度

我们知道在嵌套循环连接的步骤2 中可能需要访问多次被驱动表,如果访问被驱动表的方式都是全表扫描的话,那得要扫描很多次了。 但是别忘了,查询 t2 表其实就相当于一次单表扫描,我们可以利用索引来加快查询速度。回顾一下最开始介绍的 t1 表和 t2 表进行内连接的例子:
SELECT * FROM t1, t2 WHERE t1.m1 > 1 AND t1.m1 = t2.m2 AND t2.n2 < ‘d’;
我们使用的其实是 嵌套循环连接 算法执行的连接查询,再把上边那个查询执行过程表拉下来给大家看一下:
在这里插入图片描述
查询驱动表 t1 后的结果集中有两条记录, 嵌套循环连接 算法需要对被驱动表查询2次:

  • 当 t1.m1 = 2 时,去查询一遍 t2 表,对 t2 表的查询语句相当于:
    SELECT * FROM t2 WHERE t2.m2 = 2 AND t2.n2 < ‘d’;
  • 当 t1.m1 = 3 时,再去查询一遍 t2 表,此时对 t2 表的查询语句相当于:
    SELECT * FROM t2 WHERE t2.m2 = 3 AND t2.n2 < ‘d’;

可以看到,原来的 t1.m1 = t2.m2 这个涉及两个表的过滤条件在针对 t2 表做查询时关于 t1 表的条件就已经确定了,所以我们只需要单单优化对 t2 表的查询了,上述两个对 t2 表的查询语句中利用到的列是 m2 和 n2 列,我们可以:

  • 在 m2 列上建立索引,因为对 m2 列的条件是等值查找,比如 t2.m2 = 2 、 t2.m2 = 3 等,所以可能使用到ref (refs复数,多个)的访问方法,假设使用 ref 的访问方法去执行对 t2 表的查询的话,需要回表之后再判断 t2.n2 < d 这个条件是否成立。
    这里有一个比较特殊的情况,就是假设 m2 列是 t2 表的主键或者唯一二级索引列,那么使用 t2.m2 = 常数值这样的条件从 t2 表中查找记录的过程的代价就是常数级别的。我们知道在单表中使用主键值或者唯一二级索引列的值进行等值查找的方式称之为 const ,而设计 MySQL 的人把在连接查询中对被驱动表使用主键值或者唯一二级索引列的值进行等值查找的查询执行方式称之为: eq_ref 。
  • 在 n2 列上建立索引,涉及到的条件是 t2.n2 < ‘d’ ,可能用到 range 的访问方法,假设使用 range 的访问
    方法对 t2 表的查询的话,需要回表之后再判断在 m2 列上的条件是否成立。

假设 m2 和 n2 列上都存在索引的话,那么就需要从这两个里边儿挑一个代价更低的去执行对 t2 表的查询。当然,建立了索引不一定使用索引,只有在 二级索引 + 回表 的代价比全表扫描的代价更低时才会使用索引。

另外,有时候连接查询的查询列表和过滤条件中可能只涉及被驱动表的部分列,而这些列都是某个索引的一部分,这种情况下即使不能使用 eq_ref 、 ref 、 ref_or_null 或者 range 这些访问方法执行对被驱动表的查询的话,也可以使用索引扫描,也就是 index 的访问方法来查询被驱动表。所以我们建议在真实工作中最好不要使用 * 作为查询列表,最好把实际需要用到的列作为查询列表。

基于块的嵌套循环连接(Block Nested-Loop Join)

扫描一个表的过程其实是先把这个表从磁盘上加载到内存中,然后从内存中比较匹配条件是否满足。现实生活中的表可不像 t1 、t2 这种只有3条记录,成千上万条记录都是少的,几百万、几千万甚至几亿条记录的表到处都是。内存里可能并不能完全存放的下表中所有的记录,所以在扫描表前边记录的时候后边的记录可能还在磁盘上,等扫描到后边记录的时候可能内存不足,所以需要把前边的记录从内存中释放掉。我们前边又说过,采用嵌套循环连接算法的两表连接过程中,被驱动表可是要被访问好多次的,如果这个被驱动表中的数据特别多而且不能使用索引进行访问,那就相当于要从磁盘上读好几次这个表,这个 I/O 代价就非常大了,所以我们得想办法:尽量减少访问被驱动表的次数。

当被驱动表中的数据非常多时,每次访问被驱动表,被驱动表的记录会被加载到内存中,在内存中的每一条记录只会和驱动表结果集的一条记录做匹配,之后就会被从内存中清除掉。然后再从驱动表结果集中拿出另一条记录,再一次把被驱动表的记录加载到内存中一遍,周而复始,驱动表结果集中有多少条记录,就得把被驱动表从磁盘上加载到内存中多少次。所以我们可不可以在把被驱动表的记录加载到内存的时候,一次性和多条驱动表中的记录做匹配,这样就可以大大减少重复从磁盘上加载被驱动表的代价了。所以设计 MySQL 的人提出了一个
join buffer 的概念, join buffer 就是执行连接查询前申请的一块固定大小的内存,先把若干条驱动表结果集中的记录装在这个 join buffer 中,然后开始扫描被驱动表,每一条被驱动表的记录一次性和 join buffer 中的多条驱动表记录做匹配,因为匹配的过程都是在内存中完成的,所以这样可以显著减少被驱动表的 I/O 代价。使用 join buffer 的过程如下图所示:
在这里插入图片描述
最好的情况是 join buffer 足够大,能容纳驱动表结果集中的所有记录,这样只需要访问一次被驱动表就可以完成连接操作了。设计 MySQL 的人把这种加入了 join buffer 的嵌套循环连接算
法称之为 基于块的嵌套连接(Block Nested-Loop Join)算法。

这个 join buffer 的大小是可以通过启动参数或者系统变量 join_buffer_size 进行配置,默认大小为 262144字节 (也就是 256KB ),最小可以设置为128字节 。当然,对于优化被驱动表的查询来说,最好是为被驱动表加上效率高的索引,如果实在不能使用索引,并且自己的机器的内存也比较大可以尝试调大 join_buffer_size 的值来对连接查询进行优化。

另外需要注意的是,驱动表的记录并不是所有列都会被放到 join buffer 中,只有满足查询列表中的列和过滤条件中的列才会被放到 join buffer 中,最后强调说明,最好不要把 * 作为查询列表,只需要把我们实际需要使用到的列放到查询列表就好了,这样就可以在 join buffer 中一次性放置更多的记录。

更多关于mysql的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/115541.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

保障效率与可用,分析Kafka的消费者组与Rebalance机制

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 防止消息丢失与消息重复——Kafka可…

react+canvas实现横跨整个页面的动态的波浪线(贝塞尔曲线)

本来写这个特效 我打算用css实现的&#xff0c;结果是一波三折&#xff0c;我太难了&#xff0c;最终没能用css实现&#xff0c;转战了canvas来实现。来吧先看效果图 当然这个图的波浪高度、频率、位置、速度都是可调的&#xff0c;请根据自己的需求调整&#xff0c;如果你讲波…

windows10系统-17-文献管理软件

参考诸多文献管理软件的优劣比较如何&#xff1f;你有哪些使用心得&#xff1f; 参考我愿称之为目前最好用的文献管理和阅读软件&#xff01;readpaper 1 文献总结 文献总结是非常重要的一项技能&#xff0c;不知道大家看完文献后有没有总结文献的习惯&#xff0c;有的话那挺…

数据仓库-拉链表

在数据仓库中制作拉链表&#xff0c;可以按照以下步骤进行&#xff1a; 确定需求&#xff1a;首先明确需要使用拉链表的场景和需求。例如&#xff0c;可能需要记录历史数据的变化&#xff0c;以便进行时间序列分析等。设计表结构&#xff1a;在数据仓库中&#xff0c;拉链表通…

Web自动化测试 —— PageObject设计模式!

一、page object 模式简介 1.1、传统 UI 自动化的问题 无法适应 UI 频繁变化无法清晰表达业务用例场景大量的样板代码 driver/find/click 二、page object 设计原则 2.1、POM 模式的优势 降低 UI 变化导致的测试用例脆弱性问题让用例清晰明朗&#xff0c;与具体实现无关 2.…

JVM常用命令

jps —查看pid jstat -gcutil 4364 1000 2000 —查看堆内存占用百分比&#xff0c;每秒打印1次&#xff0c;总共打印2000次 S0&#xff1a;幸存1区当前使用比例 S1&#xff1a;幸存2区当前使用比例 E&#xff1a;伊甸园区使用比例 O&#xff1a;老年代使用比例 M&#xff1a;元…

服务Service

一、服务概述 Service(服务)是Android四大组件之一&#xff0c;是能够在后台长时间执行操作并且不是供用户界面的应用程序组件。Senice可以与其他组件进行交互&#xff0c;一般由Activity启动&#xff0c;但是并不依赖于Activity。当Activity的生命周期结束时&#xff0c;Serv…

野火霸天虎 STM32F407 学习笔记_1 stm32介绍;调试方法介绍

STM32入门——基于野火 F407 霸天虎课程学习 前言 博主开始探索嵌入式以来&#xff0c;其实很早就开始玩 stm32 了。但是学了一段时间之后总是感觉还是很没有头绪&#xff0c;不知道在学什么。前前后后分别尝试了江协科技、正点原子、野火霸天虎三次 stm32 的课程学习。江协科…

学Python,一个月从小白到大神?看你怎么学!

Python是一门超强大而且超受欢迎的编程语言。它被用在各种领域&#xff0c;比如网站开发、数据分析、人工智能和机器学习。学会Python会给你创造很多职业机会&#xff0c;所以绝对是值得一试的。 但你有没有过这样的梦想&#xff1a;一个月时间&#xff0c;从Python小白变成Py…

Docker DeskTop安装与启动(Windows版本)

一、官网下载Docker安装包 Docker官网如下&#xff1a; Docker官网不同操作系统下载页面https://docs.docker.com/desktop/install/windows-install/ 二、安装Docker DeskTop 2.1 双击 Docker Installer.exe 以运行安装程序 2.2 安装操作 默认勾选&#xff0c;具体操作如下…

页面淘汰算法模拟实现与比较

1.实验目标 利用标准C 语言&#xff0c;编程设计与实现最佳淘汰算法、先进先出淘汰算法、最近最久未使用淘汰算法、简单 Clock 淘汰算法及改进型 Clock 淘汰算法&#xff0c;并随机发生页面访问序列开展有关算法的测试及性能比较。 2.算法描述 1. 最佳淘汰算法&#xff08;Op…

编写shell脚本,利用mysqldump实现mysql数据库分库分表备份

摘要&#xff1a;本文介绍了如何使用 Shell 脚本和 mysqldump 工具实现 MySQL 数据库的分库分表备份。通过编写脚本&#xff0c;我们可以自动化备份多个数据库以及每个数据库中的所有表&#xff0c;并将备份文件按照数据库和表的层次结构进行存储。 一、准备工作 在开始编写 Sh…

HMDD 4.0:miRNA-疾病关系数据库

拥有多项自主专利技 术和软件著作权&#xff0c;具 有丰富的数据库平台 搭建经验。 凌恩-盈飞团队 MicroRNA&#xff08;miRNA&#xff09;是一类重要的小非编码RNA&#xff0c;在疾病诊断和治疗中发挥着重要作用。人类 MicroRNA 疾病数据库 (HMDD) 为 miRNA 相关医学提供了…

【云原生基础】了解云原生,什么是云原生?

&#x1f4d1;前言 本文主要讲了云原生的基本概念和原则的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日一句&#x…

人工智能师求职面试笔试题及答案汇总

人工智能师求职面试笔试题及答案汇总 1.如何在Python中实现一个生成器&#xff1f; 答&#xff1a;在Python中&#xff0c;生成器是一种特殊类型的迭代器。生成器允许你在需要时才生成值&#xff0c;从而节省内存。生成器函数在Python中是通过关键字yield来实现的。例如&…

leetCode 137. 只出现一次的数字 II(拓展篇) + 模5加法器 + 真值表(数字电路)

leetCode 137. 只出现一次的数字 II 有其他的题解可看我的往期文章&#xff1a; leetCode 137. 只出现一次的数字 II 位运算 模3加法器 真值表&#xff08;数字电路&#xff09; 有限状态机-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/134138112?sp…

生成带分表和水印的excel压缩文件

功能描述 将查询结果生成带分表和水印的excel压缩文件 功能点 1、将查询结果导出为excel文件 2、每个表格存放50万条数据&#xff0c;超过50万条数据&#xff0c;生成新的分表 3、生成的表格需要添加水印 4、将生成的全部分表&#xff0c;打包成zip压缩文件 引入依赖 <…

【LeetCode】每日一题 2023_11_2 环和杆(题目质量不错)

文章目录 刷题前唠嗑题目&#xff1a;环和杆题目描述代码与解题思路看看别人的题解 结语 刷题前唠嗑 今天是简单&#xff0c;我快乐了 题目&#xff1a;环和杆 题目链接&#xff1a;2103. 环和杆 题目描述 代码与解题思路 func countPoints(rings string) (ans int) {num…

强化学习的动态规划二

一、典型示例 考虑如下所示的44网格。 图1 非终端状态为S {1, 2, . . . , 14}。在每个状态下有四种可能的行为&#xff0c;A {up, down, right, left}&#xff0c;这些行为除了会将代理从网格上移走外&#xff0c;其他都会确定性地引起相应的状态转换。因此&#xff0c;例如&…

java入门,程序=数据结构+算法

一、前言 在学习java的时候&#xff0c;我印象最深的一句话是&#xff1a;程序数据结构算法&#xff0c;对于写java程序来说&#xff0c;这就是java的入门。 二、java基本数据结构与算法 1、数据类型 java中的数据类型8种基本数据类型&#xff1a; 整型 byte 、short 、int…
最新文章