Descripción general
En esta página, se proporcionan conceptos sobre los planes de ejecución de consultas y cómo Spanner los usa para realizar consultas en un entorno distribuido. Si quieres obtener información para recuperar un plan de ejecución para una consulta específica con la consola deGoogle Cloud , consulta Cómo ejecuta Spanner las consultas. También puedes ver muestras de planes de consulta históricos y comparar el rendimiento de una consulta a lo largo del tiempo para ciertas consultas. Para obtener más información, consulta Planes de consultas muestreados.
Spanner usa instrucciones de SQL declarativas para consultar sus bases de datos. Las instrucciones de SQL definen qué quiere el usuario sin especificar cómo obtener los resultados. Un plan de ejecución de consultas es el conjunto de pasos para saber cómo obtener los resultados. Para una instrucción de SQL determinada, puede haber varias formas de obtener los resultados. El optimizador de consultas de Spanner evalúa los planes de ejecución diferentes y selecciona el que considera más eficaz. Luego, Spanner usa el plan de ejecución para recuperar los resultados.
De forma conceptual, un plan de ejecución es un árbol de operadores relacionales. Cada operador lee filas de sus entradas y produce filas de salida. El resultado del operador en la raíz de la ejecución se muestra como resultado de la consulta de SQL.
Por ejemplo:
SELECT s.SongName FROM Songs AS s;
Mediante la consulta anterior, se obtiene como resultado un plan de ejecución de consultas que se puede visualizar de la siguiente manera:
Las consultas y los planes de ejecución en esta página se basan en el siguiente esquema de base de datos:
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
BirthDate DATE
) PRIMARY KEY(SingerId);
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
MarketingBudget INT64
) PRIMARY KEY(SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);
CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);
CREATE TABLE Songs (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
TrackId INT64 NOT NULL,
SongName STRING(MAX),
Duration INT64,
SongGenre STRING(25)
) PRIMARY KEY(SingerId, AlbumId, TrackId),
INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums;
CREATE INDEX SongsBySongName ON Songs(SongName);
CREATE TABLE Concerts (
VenueId INT64 NOT NULL,
SingerId INT64 NOT NULL,
ConcertDate DATE NOT NULL,
BeginTime TIMESTAMP,
EndTime TIMESTAMP,
TicketPrices ARRAY<INT64>
) PRIMARY KEY(VenueId, SingerId, ConcertDate);
Puedes usar las siguientes declaraciones del lenguaje de manipulación de datos (DML) para agregar datos a estas tablas:
INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate)
VALUES (1, "Marc", "Richards", "1970-09-03"),
(2, "Catalina", "Smith", "1990-08-17"),
(3, "Alice", "Trentor", "1991-10-02"),
(4, "Lea", "Martin", "1991-11-09"),
(5, "David", "Lomond", "1977-01-29");
INSERT INTO Albums (SingerId, AlbumId, AlbumTitle)
VALUES (1, 1, "Total Junk"),
(1, 2, "Go, Go, Go"),
(2, 1, "Green"),
(2, 2, "Forever Hold Your Peace"),
(2, 3, "Terrified"),
(3, 1, "Nothing To Do With Me"),
(4, 1, "Play");
INSERT INTO Songs (SingerId, AlbumId, TrackId, SongName, Duration, SongGenre)
VALUES (2, 1, 1, "Let's Get Back Together", 182, "COUNTRY"),
(2, 1, 2, "Starting Again", 156, "ROCK"),
(2, 1, 3, "I Knew You Were Magic", 294, "BLUES"),
(2, 1, 4, "42", 185, "CLASSICAL"),
(2, 1, 5, "Blue", 238, "BLUES"),
(2, 1, 6, "Nothing Is The Same", 303, "BLUES"),
(2, 1, 7, "The Second Time", 255, "ROCK"),
(2, 3, 1, "Fight Story", 194, "ROCK"),
(3, 1, 1, "Not About The Guitar", 278, "BLUES");
Obtener planes de ejecución eficaces es un desafío, ya que Spanner divide los datos en divisiones. Las divisiones se pueden mover de forma independiente unas de otras y pueden asignarse a servidores distintos, que pueden estar en diferentes ubicaciones físicas. Para evaluar los planes de ejecución sobre los datos distribuidos, Spanner usa la ejecución en función de lo siguiente:
- la ejecución local de subplanes en servidores que contienen los datos
- la organización y agregación de varias ejecuciones remotas con una fuerte reducción de la distribución
Spanner usa el operador primitivo distributed union
, junto con sus variantes distributed cross apply
y distributed outer apply
, para habilitar este modelo.
Planes de consulta muestreados
Los planes de consulta muestreados de Spanner te permiten ver muestras de planes de consulta históricos y comparar el rendimiento de una consulta a lo largo del tiempo. No todas las consultas tienen planes de consulta de muestra disponibles. Solo se pueden muestrear las consultas que consumen más CPU. La retención de datos para las muestras de planes de consulta de Spanner es de 30 días. Puedes encontrar muestras de planes de consulta en la página Estadísticas de consultas de la consola de Google Cloud . Para obtener instrucciones, consulta Cómo ver planes de consulta muestreados.
La anatomía de un plan de consulta de muestra es la misma que la de un plan de ejecución de consulta normal. Para obtener más información sobre cómo comprender los planes visuales y usarlos para depurar tus consultas, consulta Un recorrido por el visualizador de planes de consultas.
Estos son algunos casos de uso comunes para los planes de consulta muestreados:
Estos son algunos casos de uso comunes de los planes de consulta muestreados:
- Observa los cambios en el plan de consulta debido a cambios en el esquema (por ejemplo, agregar o quitar un índice).
- Observa los cambios en el plan de consultas debido a una actualización de la versión del optimizador.
- Observa los cambios en el plan de consultas debido a las nuevas estadísticas del optimizador, que se recopilan automáticamente cada tres días o se realizan de forma manual con el comando
ANALYZE
.
Si el rendimiento de una consulta muestra una diferencia significativa con el tiempo o si quieres mejorarlo, consulta las prácticas recomendadas de SQL para crear instrucciones de consulta optimizadas que ayuden a Spanner a encontrar planes de ejecución eficientes.
El ciclo de una consulta
Una consulta en SQL en Spanner se compila primero en un plan de ejecución y, luego, se envía a un servidor raíz inicial para su ejecución. El servidor raíz se elige para minimizar la cantidad de saltos con el fin de llegar a los datos que se consultan. El servidor raíz realiza lo siguiente:
- inicia la ejecución remota de los subplanes (si es necesario)
- espera los resultados de las ejecuciones remotas
- maneja los pasos de ejecución local restantes, como la agregación de resultados
- muestra los resultados de la consulta
Los servidores remotos que reciben un subplan actúan como un servidor “raíz” para su subplan y siguen el mismo modelo que el servidor raíz superior. El resultado es un árbol de ejecuciones remotas. De forma conceptual, la ejecución de la consulta fluye de arriba abajo y los resultados de la consulta se muestran de abajo arriba. En el siguiente diagrama, se muestra este patrón:
En los siguientes ejemplos, se ilustra este patrón con más detalle.
Consultas agregadas
Una consulta agregada implementa consultas GROUP BY
.
Por ejemplo:
SELECT s.SingerId, COUNT(*) AS SongCount
FROM Songs AS s
WHERE s.SingerId < 100
GROUP BY s.SingerId;
Los resultados de la consulta anterior son los siguientes:
+----------+-----------+
| SingerId | SongCount |
+----------+-----------+
| 3 | 1 |
| 2 | 8 |
+----------+-----------+
De manera conceptual, este es el plan de ejecución:
Spanner envía el plan de ejecución a un servidor raíz que coordina la ejecución de la consulta y realiza la distribución remota de los subplanes.
Este plan de ejecución comienza con una unión distribuida, que distribuye los subplanes a los servidores remotos con divisiones que cumplen con SingerId < 100
. Una vez que se completa el análisis de las divisiones individuales, el operador stream aggregate agrega filas para obtener los recuentos de cada SingerId
. Luego, el operador de serialización de resultados serializa el resultado. Por último, la unión distribuida combina todos los resultados y muestra los resultados de la consulta.
Para obtener más información sobre los agregados, consulta la página sobre el operador de agregación.
Consultas de unión ubicadas en el mismo lugar
Las tablas intercaladas se almacenan de forma física con sus filas de tablas relacionadas ubicadas en el mismo lugar. Una unión ubicada en el mismo lugar es una unión entre tablas intercaladas. Las uniones ubicadas en el mismo lugar pueden ofrecer beneficios de rendimiento en comparación con las uniones que requieren índices o reversión de uniones.
Por ejemplo:
SELECT al.AlbumTitle, so.SongName
FROM Albums AS al, Songs AS so
WHERE al.SingerId = so.SingerId AND al.AlbumId = so.AlbumId;
(En esta consulta, se da por sentado que Songs
está intercalado en Albums
).
Los resultados de la consulta anterior son los siguientes:
+-----------------------+--------------------------+
| AlbumTitle | SongName |
+-----------------------+--------------------------+
| Nothing To Do With Me | Not About The Guitar |
| Green | The Second Time |
| Green | Starting Again |
| Green | Nothing Is The Same |
| Green | Let's Get Back Together |
| Green | I Knew You Were Magic |
| Green | Blue |
| Green | 42 |
| Terrified | Fight Story |
+-----------------------+--------------------------+
Este es el plan de ejecución:
Este plan de ejecución comienza con una unión distribuida, que distribuye los subplanes a los servidores remotos que tienen divisiones de la tabla Albums
.
Como Songs
es una tabla intercalada de Albums
, cada servidor remoto puede ejecutar todo el subplan en cada servidor remoto sin necesidad de unirse a un servidor diferente.
Los subplanes contienen una aplicación cruzada. Cada aplicación cruzada realiza un análisis
de la tabla en la tabla Albums
para recuperar SingerId
, AlbumId
y
AlbumTitle
. La aplicación cruzada asigna el resultado del análisis de la tabla al resultado de un análisis de índice en el índice SongsBySingerAlbumSongNameDesc
, sujeto a un filtro del SingerId
en el índice que coincide con el SingerId
del resultado del análisis de la tabla. Cada aplicación cruzada envía sus resultados a un operador de serialización de resultados que serializa los datos AlbumTitle
y SongName
, y muestra resultados a las uniones distribuidas locales. La unión distribuida agrega los resultados de las uniones distribuidas locales y los muestra como el resultado de la consulta.
Consultas de índices y de reversiones de uniones
En el ejemplo anterior, se usó una unión en dos tablas, una intercalada en la otra. Los planes de ejecución son más complejos y menos eficaces cuando dos tablas o una tabla y un índice no están intercalados.
Considera un índice creado con el siguiente comando:
CREATE INDEX SongsBySongName ON Songs(SongName)
Usa este índice en la siguiente consulta:
SELECT s.SongName, s.Duration
FROM Songs@{force_index=SongsBySongName} AS s
WHERE STARTS_WITH(s.SongName, "B");
Los resultados de la consulta anterior son los siguientes:
+----------+----------+
| SongName | Duration |
+----------+----------+
| Blue | 238 |
+----------+----------+
Este es el plan de ejecución:
El plan de ejecución resultante es complicado porque el índice SongsBySongName
no contiene la columna Duration
. Para obtener el valor Duration
, Spanner necesita revertir la unión de los resultados indexados a la tabla Songs
. Esta es una unión, pero no se ubica en un mismo lugar porque la tabla Songs
y el índice global SongsBySongName
no están intercalados. El plan de ejecución resultante es más complejo que el ejemplo de unión ubicada en el mismo lugar, porque Spanner realiza optimizaciones para acelerar la ejecución si los datos no están ubicados en el mismo lugar.
El operador superior es una aplicación cruzada distribuida. La entrada de este operador son lotes de filas del índice SongsBySongName
que cumplen con el predicado STARTS_WITH(s.SongName, "B")
. La aplicación distribuida cruzada asigna los lotes a los servidores remotos, que tienen divisiones con los datos Duration
. Los servidores remotos usan un análisis de tabla para recuperar la columna Duration
.
El análisis de tabla usa el filtro Condition:($Songs_key_TrackId' =
$batched_Songs_key_TrackId)
, que une TrackId
de la tabla Songs
a TrackId
de las filas que se agruparon por lote del índice SongsBySongName
.
Los resultados se agregan a la respuesta final de la consulta. A su vez, el lado de entrada de la aplicación distribuida cruzada contiene un par de unión distribuida y unión distribuida local para evaluar las filas del índice que cumplen con el predicado STARTS_WITH
.
Considera hacer una consulta un poco diferente, que no seleccione la columna s.Duration
:
SELECT s.SongName
FROM Songs@{force_index=SongsBySongName} AS s
WHERE STARTS_WITH(s.SongName, "B");
Esta consulta puede aprovechar al máximo el índice como se muestra en este plan de ejecución:
El plan de ejecución no necesita una reversión de la unión, porque todas las columnas que solicita la consulta están presentes en el índice.
¿Qué sigue?
Obtén más información sobre los operadores de ejecución de consultas
Obtén más información sobre el optimizador de consultas de Spanner
Obtén más información sobre cómo administrar el optimizador de consultas.