¿Cuál es el significado de la consulta de unión de varias tablas de MySQL?

Hoy, hablemos de la consulta conjunta de varias mesas en WeChat. ¿Debería una mesa pequeña conducir una mesa grande o una mesa grande conducir una mesa pequeña?

1. en VS existe

Antes del análisis formal, veamos las dos palabras clave en y existe.

Supongamos que ahora tengo dos tablas: la tabla de empleados y la tabla de departamentos, cada empleado tiene un departamento, la tabla de empleados contiene la identificación del departamento y este campo es un índice; la tabla de departamentos tiene atributos como la identificación del departamento, el nombre, etc. , entre los cuales id es la clave principal y name es el índice único.

Aquí usaré directamente la tabla en vhr para hacer el experimento, en lugar de darle solo el script de la base de datos, los amigos pueden consultar el proyecto vhr ( github.com/lenve/vhr) para obtener...

Supongamos que ahora quiero consultar a todos los empleados del departamento técnico. Tengo los siguientes dos métodos de consulta:

El primer método de consulta es usar la palabra clave in para consultar:

select * from employee e where e.departmentId in(select d.id from department d where d.name='技术部') limit 10;
复制代码

Este SQL es fácil de entender y creo que todos pueden entenderlo. Al consultar, también se consulta la subconsulta interna (es decir, primero se consulta la tabla de departamentos), y luego se ejecuta la consulta externa, podemos ver su plan de ejecución:

Se puede ver que, primero consulte la tabla de departamentos, use el índice si hay un índice, escanee toda la tabla si no hay índice y luego consulte la tabla de empleados, también use el índice para consultar, la eficiencia general es relativamente elevado.

El segundo es usar la palabra clave exist para consultar:

select * from employee e where exists(select 1 from department d where d.id=e.departmentId and d.name='技术部') limit 10;
复制代码

El resultado de la consulta de este SQL es el mismo que el de la palabra clave in anterior, pero el proceso de consulta es diferente. Echemos un vistazo al plan de ejecución de este SQL:

Se puede ver que el escaneo completo de la tabla se realiza primero en la tabla de empleados y luego se utiliza el ID de departamento en la tabla de empleados para comparar los datos en la tabla de departamentos. En el SQL anterior, si la subconsulta tiene un valor de retorno, significa verdadero, y si no hay valor de retorno, significa falso. Si es verdadero, se conservará el registro del empleado. Si es falso, el registro del empleado se conservará. ser desechado. Por lo tanto, no es necesario usarlo en la subconsulta, y se SELECT *puede cambiar a SELECT 1u otros.La declaración oficial de MySQL es que la lista SELECT se ignorará durante la ejecución real, por lo que hay poca diferencia en la escritura.

Comparando el número de filas escaneadas en los dos planes de consulta, podemos ver aproximadamente la diferencia.Usar in es un poco más eficiente.

如果用 in 关键字查询的话,先部门表再员工表,一般来说部门表的数据是要小于员工表的数据的,所以这就是小表驱动大表,效率比较高。

如果用 exists 关键字查询的话,先员工表再部门表,一般来说部门表的数据是要小于员工表的数据的,所以这就是大表驱动小表,效率比较低。

总之,就是要小表驱动大表效率才高,大表驱动小表效率就会比较低。所以,假设部门表的数据量大于员工表的数据量,那么上面这两种 SQL,使用 exists 查询关键字的效率会比较高。

2. 为什么要小表驱动大表

在 MySQL 中,这种多表联合查询的原理是:以驱动表的数据为基础,通过类似于我们 Java 代码中写的嵌套循环 的方式去跟被驱动表记录进行匹配。

以第一小节的表为例,假设我们的员工表 E 表是大表,有 10000 条记录;部门表 D 表是小表,有 100 条记录。

假设 D 驱动 E,那么执行流程大概是这样:

for 100 个部门{
    匹配 10000 个员工(进行B+树查找)
}
复制代码

那么查找的总次数是 100+log10000。

假设 E 驱动 D,那么执行流程大概是这样:

for 10000 个员工{
    匹配 100 个部门(进行B+树查找)
}
复制代码

那么总的查找次数是 10000+log100。

从这两个数据对比中我们就能看出来,小表驱动大表效率要高。核心的原因在于,搜索被驱动的表的时候,一般都是有索引的,而索引的搜索就要快很多,搜索次数也少。

3. 没有索引咋办?

前面第二小节我们得出的结论有一个前提,就是驱动表和被驱动表之间关联的字段是有索引的,以我们前面的表为例,就是 E 表中保存了 departmentId 字段,该字段对应了 D 表中的 id 字段,而 id 字段在 D 表中是主键索引,如果 id 不是主键索引,就是一个普通字段,那么 D 表岂不是也要做全表扫描了?那个时候 E 驱动 D 还是 D 驱动 E 差别就不大了。

对于这种被驱动表上没有可用索引的情况,MySQL 使用了一种名为 Block Nested-Loop Join (简称 BNL)的算法,这种算法的步骤是这样:

  1. Lea los datos de la tabla E en la memoria de subprocesos join_buffer.
  2. Escanee la tabla D, extraiga cada fila de la tabla D y compárela con los datos del join_buffer. Si se cumple la condición de unión, se devolverá como parte del conjunto de resultados.

Echemos un vistazo, si elimino el índice en el campo departmentId en la tabla E y luego elimino el índice de clave principal en el campo id en la tabla D, echemos un vistazo al siguiente plan de ejecución de SQL:

Se puede ver que en este momento, tanto la tabla E como la tabla D son escaneos completos de la tabla Además, se debe tener en cuenta que estas operaciones de comparación están todas en la memoria, por lo que la eficiencia de ejecución es correcta.

Sin embargo, dado que los datos se leen en la memoria, ¿pueden colocarse en la memoria? ¿Qué debo hacer si no puedo ponerlo en la memoria? Veamos el plan de consulta anterior, en la consulta de la tabla E también aparece Extra Using join buffer (Block Nested Loop)¡Bloque significa bloque! Entonces, el significado es muy claro. Si no puede ponerlo en la memoria a la vez, entonces léalo en bloques, lea una parte en la memoria primero y luego lea la otra parte en la memoria después de la comparación.

Podemos verificar el tamaño de join_buffer con el siguiente comando:

262144/1024=256KB

El tamaño predeterminado es de 256 KB.

Ahora cambio este valor a un valor mayor y luego veo el nuevo plan de ejecución, de la siguiente manera:

Como puede ver, no hay Using join buffer (Block Nested Loop)indicaciones .

En conclusión:

  • Si el join_buffer es lo suficientemente grande como para leer todos los datos en la memoria a la vez, entonces no importa si la tabla grande controla la tabla pequeña o si la tabla pequeña controla la tabla grande.
  • Si el tamaño de join_buffer es limitado, se recomienda que una tabla pequeña controle una tabla grande, de modo que incluso si desea leer en bloques, la cantidad de lecturas es menor.

Pero para ser honesto, este tipo de consulta conjunta de múltiples tablas sin índice es relativamente ineficiente y debe evitarse tanto como sea posible.

En resumen, en la consulta conjunta de varias tablas, se recomienda que las tablas pequeñas controlen a las tablas grandes.

Supongo que te gusta

Origin juejin.im/post/7083863347304611877
Recomendado
Clasificación