Introdução
Nesta página, descrevemos detalhes sobre os operadores usados no Spanner Planos de execução de consulta. Para saber como recuperar um plano de execução para um específica usando o console do Google Cloud, consulte Noções básicas sobre O Spanner executa consultas.
As consultas e os planos de execução dessa página se baseiam no esquema de banco de dados abaixo:
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);
É possível usar as seguintes instruções da linguagem de manipulação de dados (DML) para adicionar dados a estas tabelas:
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");
Operadores folha
Um operador folha é um operador que não tem filhos. Os tipos de operadores folha são:
Array unnest
Usando um operador array unnest é possível nivelar uma matriz de entrada em linhas de elementos. Em cada linha resultante há até duas colunas: o valor real da matriz e, opcionalmente, a posição baseada em zero na matriz.
Por exemplo, para esta consulta:
SELECT a, b FROM UNNEST([1,2,3]) a WITH OFFSET b;
A consulta nivela a matriz [1,2,3]
na coluna a
e mostra a posição da matriz na coluna b
.
Estes são os resultados:
a | b |
---|---|
1 | 0 |
2 | 1 |
3 | 2 |
Este é o plano de execução:
Generate relation
Um operador generate relation retorna zero ou mais linhas.
Unit relation
Unit relation retorna uma linha. É um caso especial do operador generate relation.
Por exemplo, para esta consulta:
SELECT 1 + 2 AS Result;
O resultado é:
Result |
---|
3 |
Este é o plano de execução:
Empty relation
Empty relation não retorna nenhuma linha. É um caso especial do operador generate relation.
Por exemplo, para esta consulta:
SELECT * FROM Albums LIMIT 0
O resultado é:
No results
Este é o plano de execução:
Scan
O operador scan retorna linhas por meio da verificação de uma fonte de linhas. Estes são os tipos de operadores scan:
- Table scan: a verificação ocorre em uma tabela.
- Index scan: a verificação ocorre em um índice.
- Batch scan: a verificação ocorre em tabelas intermediárias criadas por outros operadores relacionais. Por exemplo, uma tabela criada por um operador distributed cross apply.
Sempre que possível, o Spanner aplica predicados simples nas chaves como parte
uma verificação. As verificações são executadas de modo mais eficiente quando os predicados são aplicados porque não é necessário que a tabela ou índice inteiros sejam lidos pela verificação. Os predicados aparecem no plano de execução na forma KeyPredicate: column=value
.
No pior dos casos, uma consulta pode precisar pesquisar todas as linhas de uma tabela. Essa situação leva a uma verificação completa e aparece no plano de execução como full scan: true
.
Por exemplo, para esta consulta:
SELECT s.LastName
FROM singers@{FORCE_INDEX=SingersByFirstLastName} AS s
WHERE s.FirstName = 'Catalina';
Estes são os resultados:
LastName |
---|
Smith |
Este é o plano de execução:
No plano de execução, com o operador distributed union de nível superior, são enviados subplanos a servidores remotos. Em cada subplano há um operador serialize result e um operador index scan. O predicado Key Predicate: FirstName = 'Catalina'
restringe a verificação às linhas no índice SingersByFirstLastname
que têm FirstName
igual a Catalina
.
A saída de index scan é retornada para o operador serialize result.
Operadores unary
Um operador unary é um operador que tem um único filho relacional.
Os seguintes operadores são unários:
- Aggregate
- Apply mutations
- Create batch
- Compute
- Compute struct
- Filtrar
- Filter scan
- Limite
- Atribuir ID aleatório
- Serialize result
- Ordenar
- TVF
- Union input
Aggregate
Um operador aggregate implementa GROUP BY
instruções SQL e funções agregadas (como COUNT
). A entrada de um operador de agregação é particionada logicamente em grupos organizados em colunas de chave (ou em um único grupo se GROUP BY
não estiver presente). Zero ou mais agregados são computados para cada grupo.
Por exemplo, para esta consulta:
SELECT s.SingerId, AVG(s.duration) AS average, COUNT(*) AS count
FROM Songs AS s
GROUP BY SingerId;
A consulta é agrupada por SingerId
e executa uma agregação AVG
e uma agregação COUNT
.
Estes são os resultados:
SingerId | average | count |
---|---|---|
3 | 278 | 1 |
2 | 225.875 | 8 |
Este é o plano de execução:
Os operadores aggregate podem ser baseados em stream ou baseados em hash. O plano de execução acima mostra um agregado baseado em stream. Os agregados baseados em stream leem a entrada já pré-classificada (se GROUP BY
estiver presente) e computam os grupos sem bloqueio. Os agregados baseados em hash criam tabelas de hash para manter agregados incrementais de várias linhas de entrada simultaneamente. Os agregados baseados em stream são mais rápidos e usam menos memória do que os agregados baseados em hash, mas exigem que a entrada seja classificada (por colunas de chave ou índices secundários).
Para cenários distribuídos, um operador aggregate pode ser separado em um par local/global. A agregação local é executada individualmente pelos servidores remotos nas respectivas linhas de entrada e, em seguida, os resultados são retornados ao servidor raiz. No servidor raiz é executada a agregação global.
Apply mutations
Um operador apply mutations aplica as mutações de uma linguagem de manipulação de dados (DML, na sigla em inglês) à tabela. Ele é o operador principal em um plano de consulta para uma instrução DML.
Por exemplo, para esta consulta:
DELETE FROM Singers
WHERE FirstName = 'Alice';
Estes são os resultados:
4 rows deleted
This statement deleted 4 rows and did not return any rows.
Este é o plano de execução:
Create batch
Um operador create batch reúne as linhas de entrada em sequência. Uma operação create batch geralmente ocorre como parte de uma operação distributed cross apply. É possível reordenar as linhas de entrada durante o processamento de lotes. O número de linhas de entrada que são agrupadas em cada execução do operador de lote é variável.
Consulte o operador distributed cross apply para ver um exemplo de operador create batch em um plano de execução.
Compute
A saída de um operador compute é produzida com a leitura das linhas de entrada e a adição de uma ou mais colunas extras que são computadas por meio de expressões escalares. Consulte o operador union all para ver um exemplo do operador compute em um plano de execução.
Compute struct
Com um operador compute struct é possível criar uma variável para uma estrutura contendo campos para cada uma das colunas de entrada.
Por exemplo, para esta consulta:
SELECT FirstName,
ARRAY(SELECT AS STRUCT song.SongName, song.SongGenre
FROM Songs AS song
WHERE song.SingerId = singer.SingerId)
FROM singers AS singer
WHERE singer.SingerId = 3;
Estes são os resultados:
FirstName | Unspecified |
---|---|
Alice | [["Not About The Guitar","BLUES"]] |
Este é o plano de execução:
No plano de execução, o operador de subconsulta de matriz recebe a entrada de um operador de união distribuída, que recebe entrada de um operador comput struct. O operador comput struct cria uma estrutura das colunas SongName
e SongGenre
na tabela Songs
.
Filter
Com um operador filter, todas as linhas da entrada são lidas, um predicado escalar é aplicado a cada linha e apenas as linhas que satisfazem ao predicado são retornadas.
Por exemplo, para esta consulta:
SELECT s.LastName FROM (SELECT s.LastName
FROM Singers AS s LIMIT 3) s
WHERE s.LastName LIKE 'Rich%';
Estes são os resultados:
LastName |
---|
Richards |
Este é o plano de execução:
O predicado para cantores com sobrenome começado por Rich
é implementado como um filtro. A entrada do filtro é a saída de um index scan e a saída do filtro são linhas em que LastName
começa com Rich
.
Para fins de desempenho, sempre que um filtro é posicionado diretamente acima de uma operação scan, o filtro afeta o modo como os dados são lidos. Por exemplo, considere uma tabela com a chave k
.
Um filtro com predicado k = 5
diretamente na parte superior de uma verificação da tabela procurará linhas que correspondam k = 5
, sem ler toda a entrada. Assim, a execução da consulta fica mais eficiente. No exemplo acima, o operador de filtro lê somente as linhas que satisfazem o predicado WHERE s.LastName LIKE 'Rich%'
.
Filter scan
Um operador filter scan está sempre acima de um table ou index scan. Ele opera com a verificação para reduzir o número de linhas lidas no banco de dados e a verificação resultante é normalmente mais rápida do que com um filter. O Spanner aplica a verificação de filtro em determinadas condições:
- Condição pesquisável: a condição pesquisável se aplica se o Spanner puder
determinar uma linha específica para acessar na tabela. Em geral, isso acontece quando o filtro está em um prefixo da chave primária. Por exemplo, se a chave primária consistir em
Col1
eCol2
, uma cláusulaWHERE
que inclua valores explícitos paraCol1
ouCol1
eCol2
é pesquisável. Nesse caso, O Spanner lê dados apenas dentro do intervalo de chaves. - Condição residual: qualquer outra condição em que o Spanner possa avaliar a verificação para limitar a quantidade de dados lidos.
Por exemplo, para esta consulta:
SELECT LastName
FROM Singers
WHERE SingerId = 1
Estes são os resultados:
LastName |
---|
Richards |
Este é o plano de execução:
Limit
Um operador limit restringe o número de linhas retornadas. Um parâmetro OFFSET
opcional especifica a linha inicial a ser retornada. Em cenários distribuídos, é possível separar um operador "limit" em um par local/global. Em cada servidor remoto é aplicado o limite local às linhas de saída e os resultados são retornados ao servidor raiz. No servidor raiz as linhas enviadas pelos servidores remotos são agregadas e o limite global é aplicado.
Por exemplo, para esta consulta:
SELECT s.SongName
FROM Songs AS s
LIMIT 3;
Estes são os resultados:
SongName |
---|
Not About The Guitar |
The Second Time |
Starting Again |
Este é o plano de execução:
O limite local é aquele de cada servidor remoto. As linhas dos servidores remotos são agregadas pelo servidor raiz e, em seguida, o limite global é aplicado.
ID aleatório atribuído
Um operador aleatório de atribuição de id produz saída lendo suas linhas de entrada e
adicionando um número aleatório a cada linha. Ele funciona com um operador Filter
ou Sort
para conseguir métodos de amostragem. Os métodos compatíveis de amostragem são Bernoulli e Reservoir.
Por exemplo, a consulta a seguir usa a amostragem Bernoulli com uma taxa de amostragem de 10%.
SELECT s.SongName
FROM Songs AS s TABLESAMPLE BERNOULLI (10 PERCENT);
Estes são os resultados:
SongName |
---|
Starting Again |
Nothing Is The Same |
Observe que, por ser uma amostra, o resultado pode variar toda vez que a consulta for executada, embora a consulta seja a mesma.
Este é o plano de execução:
Neste plano de execução, o operador Random Id Assign
recebe sua entrada de
um operador de união distribuída, que recebe sua entrada
a partir de um index scan. O operador retorna as linhas com códigos aleatórios, e o operador Filter
aplica um predicado escalar aos códigos aleatórios e retorna aproximadamente 10% das linhas.
O exemplo a seguir usa a amostragem de reservatório com uma taxa de amostragem de 2 linhas.
SELECT s.SongName
FROM Songs AS s TABLESAMPLE RESERVOIR (2 ROWS);
Estes são os resultados:
SongName |
---|
I Knew You Were Magic |
The Second Time |
Observe que, por ser uma amostra, o resultado pode variar toda vez que a consulta for executada, embora a consulta seja a mesma.
Este é o plano de execução:
Neste plano de execução, o operador Random Id Assign
recebe sua entrada de
um operador de união distribuída, que recebe sua entrada
a partir de um index scan. O operador retorna as linhas com códigos aleatórios, e o operador Sort
aplica a ordem de classificação nos IDs aleatórios e aplica LIMIT
com duas linhas.
Serialize result
Um operador serialize result é um caso especial do operador "compute struct" que serializa cada linha do resultado final da consulta para retorno ao cliente.
Por exemplo, para esta consulta:
SELECT ARRAY(SELECT AS STRUCT so.SongName, so.SongGenre
FROM Songs AS so
WHERE so.SingerId = s.SingerId)
FROM Singers AS s;
A consulta pede uma matriz de SongName
e SongGenre
com base em SingerId
.
Estes são os resultados:
Unspecified |
---|
[] |
[[Let's Get Back Together, COUNTRY], [Starting Again, ROCK]] |
[[Not About The Guitar, BLUES]] |
[] |
[] |
Este é o plano de execução:
O operador serialize result cria um resultado que contém, para cada linha da tabela Singers
, uma matriz de pares SongName
e SongGenre
para as músicas de um cantor.
Sort
Um operador sort lê as linhas de entrada, as ordena por colunas e, em seguida, retorna os resultados classificados.
Por exemplo, para esta consulta:
SELECT s.SongGenre
FROM Songs AS s
ORDER By SongGenre;
Estes são os resultados:
SongGenre |
---|
BLUES |
BLUES |
BLUES |
BLUES |
CLASSICAL |
COUNTRY |
ROCK |
ROCK |
ROCK |
Este é o plano de execução:
Neste plano de execução, as linhas de entrada de um operador "sort" são recebidas de um distributed union e classificadas. Em seguida, elas são retornadas a um operador serialize result.
Para restringir o número de linhas retornadas, um operador "sort" pode opcionalmente ter os parâmetros LIMIT
e OFFSET
. Em cenários distribuídos, um operador "sort" com um operador LIMIT
e/ou OFFSET
é separado em um par local/global. Cada servidor remoto aplica a ordem de classificação e o limite/deslocamento local para as linhas de entrada e, em seguida, retorna os resultados ao servidor raiz. O servidor raiz agrega as linhas enviadas pelos servidores remotos, as classifica e, em seguida, aplica o limite/deslocamento global.
Por exemplo, para esta consulta:
SELECT s.SongGenre
FROM Songs AS s
ORDER By SongGenre
LIMIT 3;
Estes são os resultados:
SongGenre |
---|
BLUES |
BLUES |
BLUES |
Este é o plano de execução:
O plano de execução mostra o limite local dos servidores remotos e o limite global do servidor raiz.
TVF
Um operador de função com valor de tabela produz saídas lendo as linhas de entrada e aplicando a função especificada. A função pode implementar o mapeamento e retornar o mesmo número de linhas que a entrada. Também pode ser um gerador que retorna mais linhas ou um filtro que retorne menos linhas.
Por exemplo, para esta consulta:
SELECT Genre, SongName
FROM ML.PREDICT(MODEL GenreClassifier, Table Songs)
Estes são os resultados:
Gênero | SongName |
---|---|
País | Not About The Guitar |
Pedra | The Second Time |
Pop | Starting Again |
Pop | Nothing Is The Same |
País | Let's Get Back Together |
Pop | I Knew You Were Magic |
Eletrônica | Azul |
Pedra | 42 |
Pedra | Fight Story |
Este é o plano de execução:
Union input
Um operador union input retorna resultados para o operador union all. Consulte o operador union all para ver um exemplo do operador "union input" em um plano de execução.
Operadores binários
Um operador binário é um operador que tem dois filhos relacionais. Os seguintes operadores são binários:
Cross apply
Um operador cross apply executa uma consulta de tabela em cada linha recuperada por uma consulta de outra tabela e retorna a união de todas as execuções da consulta de tabela. Os operadores "cross apply" e outer apply executam o processamento orientado por linha, em vez do processamento baseado em conjunto, como hash join, que é executado por outros operadores. O operador cross apply tem duas entradas, input e map. O operador cross apply aplica cada linha do lado "input" ao lado "map". O resultado de "cross apply" tem colunas de ambos os lados: "input" e "map".
Por exemplo, para esta consulta:
SELECT si.FirstName,
(SELECT so.SongName
FROM Songs AS so
WHERE so.SingerId=si.SingerId
LIMIT 1)
FROM Singers AS si;
A consulta pede o nome de cada cantor, juntamente com o nome de apenas uma das canções do cantor.
Estes são os resultados:
FirstName | Unspecified |
---|---|
Alice | Not About The Guitar |
Catalina | Let's Get Back Together |
David | NULL |
Lea | NULL |
Marc | NULL |
A primeira coluna é preenchida a partir da tabela Singers
e a segunda coluna é preenchida a partir da tabela Songs
. Caso haja SingerId
na tabela Singers
, mas não exista SingerId
correspondente na tabela Songs
, a segunda coluna conterá NULL
.
Este é o plano de execução:
O nó de nível superior é um operador distributed union. O operador "distributed union" distribui subplanos a servidores remotos. O subplano contém um operador serialize result, que computa o nome do cantor e o nome de uma das músicas dele e serializa cada linha da saída.
O operador "serialize result" recebe a entrada de um operador "cross apply".
O lado "input" do operador "cross apply" é uma operação scan na tabela Singers
.
O lado "map" da operação "cross apply" contém os seguintes itens (de cima para baixo):
- Um operador aggregate que retorna
Songs.SongName
. - Um operador limit que limita o número de canções retornadas a uma por cantor.
- Um operador index scan no índice
SongsBySingerAlbumSongNameDesc
.
O operador "cross apply" associa cada linha do lado "input" a uma linha do lado "map" que tenha o mesmo SingerId
. A saída do operador "cross apply" é o valor FirstName
da linha de "input" e o valor SongName
da linha de "map".
(O valor SongName
será NULL
se não houver linha de mapa que corresponda a SingerId
.) O operador de união distribuída na parte superior do plano de execução combina todas as linhas de saída dos servidores remotos e as retorna como os resultados da consulta.
Hash join
Um operador hash join é uma implementação baseada em hash de junções SQL. Operações "hash join" executam processamento baseado em conjunto. O operador "hash join" lê as linhas da entrada marcadas como build e as insere em uma tabela de hash com base em uma condição de junção. Em seguida, o operador "hash join" lê as linhas da entrada marcadas como probe. Para cada linha que lê a partir da entrada "probe", o operador "hash join" procura as linhas correspondentes na tabela de hash. O operador "hash join" retorna as linhas correspondentes como resultado.
Por exemplo, para esta consulta:
SELECT a.AlbumTitle, s.SongName
FROM Albums AS a JOIN@{join_method=hash_join} Songs AS s
ON a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId;
Estes são os resultados:
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 é o plano de execução:
No plano de execução, build é um distributed union que distribui scans na tabela Albums
. Probe é um operador "distributed union" que distribui "scans" no índice SongsBySingerAlbumSongNameDesc
.
O operador "hash join" lê todas as linhas do lado "build". Cada linha "build" é colocada em uma tabela de hash com base nas colunas da condição a.SingerId =
s.SingerId AND a.AlbumId = s.AlbumId
. Em seguida, o operador "hash join" lê todas as linhas do lado "probe". Para cada linha "probe", o operador "hash join" procura correspondências na tabela de hash. As correspondências resultantes são retornadas pelo operador "hash join".
As correspondências resultantes na tabela de hash também podem ser filtradas por uma condição residual antes de serem retornadas. Um exemplo de onde as condições residuais aparecem é em junções de não igualdade. Os planos de execução de "hash join" podem ser complexos devido ao gerenciamento de memória e às variantes de junção. O principal algoritmo de "hash join" é adaptado para lidar com as variantes "inner", "semi", "anti" e "outer join".
Mesclar junção
Um operador merge join é uma implementação baseada em mesclagem de mesclagem SQL. Os dois lados da junção produzem linhas ordenadas pelas colunas usadas na condição de junção. A mesclagem de mesclar consome os streams de entrada simultaneamente e gera linhas quando a condição da junção é atendida. Se as entradas não forem classificadas originalmente conforme necessário, o otimizador adicionará operadores Sort
explícitos ao plano.
Merge join não é selecionado automaticamente pelo otimizador. Para usar esse
operador, defina o método de junção como MERGE_JOIN
na dica de consulta, conforme mostrado
no exemplo a seguir:
SELECT a.AlbumTitle, s.SongName
FROM Albums AS a JOIN@{join_method=merge_join} Songs AS s
ON a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId;
Estes são os resultados:
AlbumTitle | SongName |
---|---|
Verde | 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 |
Nothing To Do With Me | Not About The Guitar |
Este é o plano de execução:
Neste plano de execução, a mesclagem é distribuída para que seja executada onde os dados estão localizados. Isso também permite que a mesclagem seja executada neste exemplo sem a introdução de outros operadores de classificação, já que as duas verificações de tabela já estão classificadas por SingerId
, AlbumId
, que é a união. condition. Neste plano, a verificação do lado esquerdoAlbums
a tabela avança
sempre queSingerId
,AlbumId
é relativamente menor que o lado direitoSongsBySingerAlbumSongNameDesc
verificação de índiceSingerId_1
,AlbumId_1
.
Da mesma forma, o lado direito avança sempre que é menor que o esquerdo. Esse avanço da integração continua procurando equivalências, de modo que as correspondências resultantes possam ser retornadas.
Considere outro exemplo de mesclagem usando a seguinte consulta:
SELECT a.AlbumTitle, s.SongName
FROM Albums AS a JOIN@{join_method=merge_join} Songs AS s
ON a.AlbumId = s.AlbumId;
Ela produz os seguintes resultados:
AlbumTitle | SongName |
---|---|
Total Junk | The Second Time |
Total Junk | Starting Again |
Total Junk | Nothing Is The Same |
Total Junk | Let's Get Back Together |
Total Junk | I Knew You Were Magic |
Total Junk | Azul |
Total Junk | 42 |
Total Junk | Not About The Guitar |
Green | The Second Time |
Green | Starting Again |
Green | Nothing Is The Same |
Verde | Let's Get Back Together |
Verde | I Knew You Were Magic |
Green | Blue |
Green | 42 |
Verde | Not About The Guitar |
Nothing To Do With Me | The Second Time |
Nothing To Do With Me | Starting Again |
Nothing To Do With Me | Nothing Is The Same |
Nothing To Do With Me | Let's Get Back Together |
Nothing To Do With Me | I Knew You Were Magic |
Nothing To Do With Me | Azul |
Nothing To Do With Me | 42 |
Nothing To Do With Me | Not About The Guitar |
Play | The Second Time |
Play | Starting Again |
Play | Nothing Is The Same |
Play | Let's Get Back Together |
Play | I Knew You Were Magic |
Play | Azul |
Play | 42 |
Play | Not About The Guitar |
Terrified | Fight Story |
Este é o plano de execução:
No plano de execução anterior, outros operadores Sort
foram introduzidos pelo otimizador de consulta para alcançar as propriedades necessárias para que a mesclagem seja executada. A condição JOIN
neste exemplo de consulta está somente em AlbumId
, que não é como os dados são armazenados. Portanto, uma classificação precisa ser adicionada. O mecanismo de consulta é compatível com um algoritmo de Distribuição distribuída, permitindo que a classificação ocorra localmente em vez de globalmente, o que distribui e carrega em paralelo o custo da CPU.
As correspondências resultantes também podem ser filtradas por uma condição residual antes de serem retornadas. Um exemplo de onde as condições residuais aparecem é em junções de não igualdade. Os planos de execução da mesclagem podem ser complexos devido a outros requisitos de classificação. O principal algoritmo de "hash join" é adaptado para lidar com as variantes "inner", "semi", "anti" e "outer join".
Junção do hash de transmissão push
Um operador envio de hash de transmissão push é uma implementação distribuída de junções de SQL baseadas em hash. O operador de hash de transmissão push lê linhas do lado da entrada para construir um lote de dados. Em seguida, esse lote é transmitido para todos os servidores que contêm dados de mapeamento. Nos servidores de destino em que o lote de dados é recebido, uma junção de hash é criada usando o lote como os dados do lado da versão, e os dados locais são verificados como o lado da sondagem da junção de hash.
Push broadcast hash join não é selecionado automaticamente pelo otimizador. Para
usar esse operador, defina o método de junção como PUSH_BROADCAST_HASH_JOIN
na
dica de consulta, conforme mostrado no exemplo a seguir:
SELECT a.AlbumTitle, s.SongName
FROM Albums AS a JOIN@{join_method=push_broadcast_hash_join} Songs AS s
ON a.SingerId = s.SingerId AND a.AlbumId = s.AlbumId;
Estes são os resultados:
AlbumTitle | SongName |
---|---|
Verde | The Second Time |
Green | Starting Again |
Green | Nothing Is The Same |
Verde | Let's Get Back Together |
Verde | I Knew You Were Magic |
Green | Blue |
Green | 42 |
Terrified | Fight Story |
Nothing To Do With Me | Not About The Guitar |
Este é o plano de execução:
A entrada da junção de hash de transmissão push é o índice AlbumsByAlbumTitle
.
Essa entrada é serializada em um lote de dados. Esse lote é enviado para todas as divisões locais do índice SongsBySingerAlbumSongNameDesc
, em que o lote é desserializado e criado em uma tabela de hash. Em seguida, a tabela de hash usa
os dados do índice local como uma sondagem que retorna correspondências resultantes.
As correspondências resultantes também podem ser filtradas por uma condição residual antes de serem retornadas. Um exemplo de onde as condições residuais aparecem é em junções de não igualdade.
Outer apply
Um operador outer apply é semelhante ao cross apply, mas em um operador "outer apply" pelo menos uma linha é retornada após a execução no lado "map" e, se necessário, uma linha preenchida com NULL é criada. Em outras palavras, fornece semântica de "left outer join".
Operadores N-ary
Um operador N-ary é um operador que tem mais de dois filhos relacionais. Os seguintes operadores são N-ary:
Union all
Um operador union all combina todos os conjuntos de linhas dos filhos sem remover duplicatas. Os operadores "union all" recebem a entrada de operadores union input distribuídos em vários servidores. O operador "union all" exige que as entradas tenham o mesmo esquema, ou seja, o mesmo conjunto de tipos de dados para cada coluna.
Por exemplo, para esta consulta:
SELECT 1 a, 2 b
UNION ALL
SELECT 3 a, 4 b
UNION ALL
SELECT 5 a, 6 b;
O tipo de linha dos filhos consiste em dois inteiros.
Estes são os resultados:
a | b |
---|---|
1 | 2 |
3 | 4 |
5 | 6 |
Este é o plano de execução:
O operador "union all" combina as linhas de entrada e, neste exemplo, envia os resultados a um operador serialize result.
Uma consulta como a seguinte seria bem-sucedida porque o mesmo conjunto de tipos de dados é usado para cada coluna, mesmo que os filhos usem variáveis diferentes para os nomes das colunas:
SELECT 1 a, 2 b
UNION ALL
SELECT 3 c, 4 e;
Uma consulta como a seguinte não seria bem-sucedida porque os filhos usam tipos de dados diferentes para as colunas:
SELECT 1 a, 2 b
UNION ALL
SELECT 3 a, 'This is a string' b;
Subconsultas escalares
Uma subconsulta escalar é uma subexpressão SQL que faz parte de uma expressão escalar. O Spanner tenta remover subconsultas escalares sempre que possível. Em determinadas situações, no entanto, os planos podem conter explicitamente subconsultas escalares.
Por exemplo, para esta consulta:
SELECT FirstName,
IF(FirstName='Alice',
(SELECT COUNT(*)
FROM Songs
WHERE Duration > 300),
0)
FROM Singers;
Esta é a subexpressão SQL:
SELECT COUNT(*)
FROM Songs
WHERE Duration > 300;
Os resultados da consulta completa são os seguintes:
FirstName | |
---|---|
Alice | 1 |
Catalina | 0 |
David | 0 |
Lea | 0 |
Marc | 0 |
Este é o plano de execução:
O plano de execução contém uma subconsulta escalar, mostrada como Scalar Subquery, acima de um operador aggregate.
O Spanner às vezes converte subconsultas escalares em outro operador, como "join" ou "cross apply", para talvez melhorar o desempenho.
Por exemplo, para esta consulta:
SELECT *
FROM Songs
WHERE Duration = (SELECT MAX(Duration) FROM Songs);
Esta é a subexpressão SQL:
SELECT MAX(Duration) FROM Songs;
Os resultados da consulta completa são os seguintes:
SingerId | AlbumId | TrackId | SongName | Duration | SongGenre |
---|---|---|---|---|---|
2 | 1 | 6 | Nothing Is The Same | 303 | BLUES |
Este é o plano de execução:
O plano de execução não contém uma subconsulta escalar porque o Spanner converteu a subconsulta escalar em "cross apply".
Subconsultas de matriz
Uma subconsulta de matriz é semelhante a uma subconsulta escalar, exceto que a subconsulta pode consumir mais de uma linha de entrada. As linhas consumidas são convertidas em uma única matriz de saída escalar que contém um elemento por linha de entrada consumida.
Por exemplo, para esta consulta:
SELECT a.AlbumId,
ARRAY(SELECT ConcertDate
FROM Concerts
WHERE Concerts.SingerId = a.SingerId)
FROM Albums AS a;
Esta é a subconsulta:
SELECT ConcertDate
FROM Concerts
WHERE Concerts.SingerId = a.SingerId;
Os resultados da subconsulta de cada AlbumId
são convertidos em uma matriz de ConcertDate
linhas em relação a AlbumId
. O plano de execução contém uma subconsulta de matriz, mostrada como Array Subquery, acima de um operador "distributed union":
Operadores distribuídos
Os operadores descritos anteriormente nesta página são executados dentro dos limites de uma única máquina. Operadores distribuídos são executados em vários servidores.
Os operadores a seguir são distribuídos:
- Distributed union
- União de mesclagem distribuída
- Distributed cross apply
- Distributed outer apply
- Apply mutations
O operador "distributed union" é o operador primitivo do qual se derivam o "distributed cross apply" e o "distributed outer apply".
Os operadores distribuídos aparecem em planos de execução com uma variante de distributed union acima de uma ou mais variantes de local distributed union. Uma variante de "distributed union" executa a distribuição remota de subplanos. Uma variante de "local distributed union" está acima de cada uma das verificações executadas para a consulta, conforme mostrado neste plano de execução:
As variantes de "local distributed union" asseguram a execução estável da consulta quando ocorrem reinicializações para alterar dinamicamente os limites de divisão.
Sempre que possível, uma variante de "distributed union" tem um predicado de divisão que resulta em um corte da divisão, o que significa que os servidores remotos só executam subplanos nas divisões que satisfazem ao predicado. Isso melhora a latência e o desempenho geral da consulta.
Distributed union
O operador distributed union permite separar conceitualmente uma ou mais tabelas em várias divisões, avaliar remotamente uma subconsulta de modo independente em cada divisão e, em seguida, unir todos os resultados.
Por exemplo, para esta consulta:
SELECT s.SongName, s.SongGenre
FROM Songs AS s
WHERE s.SingerId = 2 AND s.SongGenre = 'ROCK';
Estes são os resultados:
SongName | SongGenre |
---|---|
Starting Again | ROCK |
The Second Time | ROCK |
Fight Story | ROCK |
Este é o plano de execução:
O operador "distributed union" permite enviar subplanos aos servidores remotos que executam uma operação table scan nas divisões que atendem ao predicado WHERE
s.SingerId = 2 AND s.SongGenre = 'ROCK'
. Um operador serialize result calcula os valores SongName
e SongGenre
das linhas retornadas pelas varreduras de tabela. Em seguida, o operador "distributed union" retorna os resultados combinados dos servidores remotos como resultados da consulta SQL.
União de mesclagem distribuída
O operador distribution merge union distribui uma consulta em vários servidores remotos. Em seguida, ele combina os resultados da consulta para produzir um resultado classificado, conhecido como classificação de mesclagem distribuída.
Uma união de mesclagem distribuída executa as seguintes etapas:
O servidor raiz envia uma subconsulta para cada servidor remoto que hospeda uma split dos dados consultados. A subconsulta inclui instruções que ordenam os resultados em uma ordem específica.
Cada servidor remoto executa a subconsulta na divisão e envia os resultados de volta na ordem solicitada.
O servidor raiz mescla a subconsulta classificada para produzir uma consulta completa resultado.
A união de mesclagem distribuída é ativada por padrão para a versão 3 do Spanner e versões mais recentes.
Distributed cross apply
O operador distributed cross apply (DCA) estende o operador cross apply por meio da execução em vários servidores. O lado de entrada do DCA agrupa lotes de linhas, ao contrário de um operador de aplicação cruzada regular, que atua em apenas uma linha de entrada por vez. O lado "map" do DCA é um conjunto de operadores "cross apply" que é executado em servidores remotos.
Por exemplo, para esta consulta:
SELECT AlbumTitle FROM Songs
JOIN Albums ON Albums.AlbumId=Songs.AlbumId;
Os resultados vêm no formato:
AlbumTitle |
---|
Green |
Nothing To Do With Me |
Play |
Total Junk |
Green |
Este é o plano de execução:
A entrada de DCA contém uma verificação de índice SongsBySingerAlbumSongNameDesc
que agrupa linhas de AlbumId
.
O lado "map" desse operador "cross apply" é uma operação "index scan" no índice AlbumsByAlbumTitle
, sujeito ao predicado de AlbumId
na linha "input" que corresponde à chave AlbumId
no índice AlbumsByAlbumTitle
. O mapeamento retorna SongName
para os valores SingerId
nas linhas de entrada em lote.
Para resumir o processo de DCA para este exemplo, a entrada do DCA é as linhas em lote da tabela Albums
, e a saída do DCA é o aplicativo dessas linhas no mapa da varredura de índice.
Distributed outer apply
O operador distributed outer apply estende o operador outer apply por meio da execução em vários servidores, semelhante ao modo em que um operador "distributed cross apply" estende um operador "cross apply".
Por exemplo, para esta consulta:
SELECT LastName, ConcertDate FROM Singers
LEFT OUTER JOIN@{JOIN_TYPE=APPLY_JOIN} Concerts
ON Singers.SingerId=Concerts.SingerId;
Os resultados vêm no formato:
LastName | ConcertDate |
---|---|
Trentor | 2014-02-18 |
Smith | 2011-09-03 |
Smith | 2010-06-06 |
Lomond | 2005-04-30 |
Martin | 2015-11-04 |
Richards |
Este é o plano de execução:
Apply mutations
Um operador apply mutations aplica as mutações de uma linguagem de manipulação de dados (DML, na sigla em inglês) à tabela. Ele é o operador principal em um plano de consulta para uma instrução DML.
Por exemplo, para esta consulta:
DELETE FROM Singers
WHERE FirstName = 'Alice';
Estes são os resultados:
4 rows deleted
This statement deleted 4 rows and did not return any rows.
Este é o plano de execução:
Mais informações
Esta seção contém os itens que não são operadores autônomos, mas que executam tarefas para aceitar um ou mais dos operadores listados acima. Os itens descritos aqui são tecnicamente operadores, mas não são operadores separados no plano de consulta.
Construtor de estrutura
Um construtor de estrutura cria uma estrutura ou uma coleção de campos. Geralmente, cria uma estrutura para linhas resultantes de uma operação de computação. Um construtor de estrutura não é um operador autônomo. Em vez disso, ele é exibido em operadores compute struct ou serialize result.
Para uma operação "compute struct", o construtor de estrutura cria uma estrutura para que as colunas das linhas computadas possam usar uma única referência de variável para a estrutura.
Para uma operação "serialize result", o construtor de estrutura cria uma estrutura para serializar os resultados.
Por exemplo, para esta consulta:
SELECT IF(TRUE, struct(1 AS A, 1 AS B), struct(2 AS A , 2 AS B)).A;
Estes são os resultados:
A |
---|
1 |
Este é o plano de execução:
No plano de execução, os construtores de estrutura aparecem dentro de um operador "serialize result".