Adicione um hint MAPJOIN a uma instrução SELECT para forçar a execução do join na fase de map, ignorando as fases de shuffle e reduce. Essa abordagem reduz a sobrecarga de transmissão de dados e melhora o desempenho da consulta ao unir uma tabela grande com uma ou mais tabelas pequenas. Utilize o MAPJOIN quando o otimizador não aplicar automaticamente o join na fase de map à sua consulta.
Como funciona
Um JOIN padrão no MaxCompute passa por três fases: map, shuffle e reduce. A lógica real do join é executada na fase de reduce, o que exige o shuffle de dados entre os nós.
O MAPJOIN altera esse fluxo: ele carrega todo o conteúdo da tabela pequena especificada na memória durante a fase de map. Cada mapper realiza o join localmente com os dados em memória, eliminando completamente as fases de shuffle e reduce.
O hint indica a tabela pequena a ser carregada na memória. Para cada mapper que lê linhas da tabela grande, a tabela pequena é lida integralmente da memória:
SELECT /*+ mapjoin(a) */ a.shop_name, a.total_price, b.total_price
FROM sale_detail_sj a JOIN sale_detail b
ON a.total_price < b.total_price OR a.total_price + b.total_price < 500;
Neste exemplo, a (alias de sale_detail_sj) é a tabela pequena. O hint especifica a, portanto, é a que será carregado na memória de cada mapper.
Limites
Memória e quantidade de tabelas
|
Restrição |
Limite |
Observações |
|
Memória total para todas as tabelas pequenas |
512 MB |
Medido após o carregamento e descompactação dos dados na memória, não corresponde ao tamanho compactado armazenado no MaxCompute |
|
Número máximo de tabelas pequenas |
128 |
Especificar mais de 128 tabelas resulta em erro de sintaxe |
Tipos de JOIN suportados
|
Tipo de JOIN |
Suportado |
Requisito |
|
INNER JOIN |
Sim |
A tabela grande pode ser a da esquerda ou a da direita |
|
LEFT OUTER JOIN |
Sim |
A tabela da esquerda deve ser a tabela grande |
|
RIGHT OUTER JOIN |
Sim |
A tabela da direita deve ser a tabela grande |
|
FULL OUTER JOIN |
Não |
— |
Observações de uso
Insira /*+ mapjoin(<table_name>) */ imediatamente após SELECT. Considere os pontos a seguir:
Use aliases em vez dos nomes originais das tabelas. Se a tabela pequena ou subconsulta possuir um alias, utilize-o dentro do hint.
Subconsultas são aceitas como tabelas pequenas. Substitua a referência à tabela por uma subconsulta e use o alias correspondente no hint.
Separe múltiplas tabelas pequenas com vírgulas:
/*+ mapjoin(a,b,c) */Joins não equitativos e condições OR são permitidos. O SQL padrão do MaxCompute não aceita joins não equitativos nem lógica OR na condição ON, mas o MAPJOIN permite ambos.
Produtos cartesianos são suportados com
ON 1 = 1(por exemplo,SELECT /*+ mapjoin(a) */ a.id FROM shop a JOIN table_name b ON 1=1), porém isso pode aumentar significativamente o volume de dados de saída.
Tipos de subconsulta como SCALAR, IN, NOT IN, EXISTS e NOT EXISTS podem ser convertidos em operações JOIN durante a execução. Caso o resultado da subconsulta se qualifique como tabela pequena, adicione um hint MAPJOIN à instrução da subconsulta para aplicar explicitamente o algoritmo de join na fase de map.
Dados de exemplo
Os exemplos deste tópico utilizam as tabelas sale_detail e sale_detail_sj. Execute as instruções abaixo para criar as tabelas e inserir dados de amostra.
-- Create a partitioned table named sale_detail.
CREATE TABLE IF NOT EXISTS sale_detail
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
CREATE TABLE IF NOT EXISTS sale_detail_sj
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
-- Add partitions.
ALTER TABLE sale_detail ADD PARTITION (sale_date='2013', region='china');
ALTER TABLE sale_detail_sj ADD PARTITION (sale_date='2013', region='china');
-- Insert sample data.
INSERT INTO sale_detail PARTITION (sale_date='2013', region='china')
VALUES ('s1','c1',100.1),('s2','c2',100.2),('s3','c3',100.3);
INSERT INTO sale_detail_sj PARTITION (sale_date='2013', region='china')
VALUES ('s1','c1',100.1),('s2','c2',100.2),('s5','c2',100.2),('s2','c2',100.2);
Exemplo
Una sale_detail_sj (tabela pequena, com alias a) a sale_detail (tabela grande, com alias b) usando uma condição não equitativa. Retorne as linhas em que o total_price de a seja menor que o total_price de b, ou em que a soma dos dois preços seja inferior a 500.
-- Allow a full scan on the partitioned table.
SET odps.sql.allow.fullscan=true;
-- Use MAPJOIN with a non-equi join condition.
SELECT /*+ mapjoin(a) */
a.shop_name,
a.total_price,
b.total_price
FROM sale_detail_sj a JOIN sale_detail b
ON a.total_price < b.total_price OR a.total_price + b.total_price < 500;
A consulta retorna o seguinte resultado:
+-----------+-------------+--------------+
| shop_name | total_price | total_price2 |
+-----------+-------------+--------------+
| s1 | 100.1 | 100.1 |
| s2 | 100.2 | 100.1 |
| s5 | 100.2 | 100.1 |
| s2 | 100.2 | 100.1 |
| s1 | 100.1 | 100.2 |
| s2 | 100.2 | 100.2 |
| s5 | 100.2 | 100.2 |
| s2 | 100.2 | 100.2 |
| s1 | 100.1 | 100.3 |
| s2 | 100.2 | 100.3 |
| s5 | 100.2 | 100.3 |
| s2 | 100.2 | 100.3 |
+-----------+-------------+--------------+