Pergunta

Eu tenho os 3 seguintes tabelas em um MySQL 4.x DB:

  • exércitos: (300.000 registros)
    • id (unsigned int) PRIMARY KEY
    • nome (VARCHAR 100)
  • caminhos: (6.000.000 registros)
    • id (unsigned int) PRIMARY KEY
    • nome (VARCHAR 100)
  • urls: (7.000.000 registros)
    • host (unsigned int) PRIMARY KEY <--- links para hosts.id
    • caminho (unsigned int) chave primária <--- links para paths.id

Como você pode ver, o esquema é muito simples, mas o problema é a quantidade de dados nessas tabelas.

Aqui está a consulta que estou executando:

SELECT CONCAT(H.name, P.name)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id;

Esta consulta funciona perfeitamente bem, mas leva 50 minutos para ser executado. Alguém tem alguma idéia sobre como eu poderia acelerar essa consulta?

Obrigado antecipadamente. Nicolas

Foi útil?

Solução

Para uma coisa que eu não faria o CONCAT na consulta. Fazê-lo fora.

Mas realmente você está consulta é executada lentamente, porque você está recuperando milhões de linhas.

Outras dicas

Talvez você deve incluir uma cláusula WHERE? Ou você realmente precisa de todos os dados?

Este me parece um caso onde o uso de excesso de zelo de chaves substitutas está retardando para baixo. Se as tabelas foram:

  • exércitos:

    • nome (VARCHAR 100) PRIMARY KEY
  • caminhos:

    • nome (VARCHAR 100) PRIMARY KEY
  • urls:

    • host (VARCHAR 100) CHAVE PRIMÁRIA <--- links para hosts.name
    • caminho (VARCHAR 100) CHAVE PRIMÁRIA <--- links para paths.name

Em seguida, sua consulta seria necessário nenhum junta-se em tudo:

SELECT CONCAT(U.host, U.path) FROM urls U;

É verdade que URLS tabela ocuparia mais espaço em disco - mas que isso importa

EDIT: Pensando bem, o que é o ponto de que a tabela CAMINHOS afinal? Quantas vezes diferentes hosts compartilham os mesmos caminhos?

Por que não:

  • exércitos:

    • nome (VARCHAR 100) PRIMARY KEY
  • urls:

    • host (VARCHAR 100) CHAVE PRIMÁRIA <--- links para hosts.name
    • caminho (VARCHAR 100) CHAVE PRIMÁRIA <--- nenhuma ligação para qualquer lugar

EDIT2: Ou se você realmente necessidade a chave substituta para os anfitriões:

  • exércitos:

    • id integer PRIMARY KEY
    • nome (VARCHAR 100)
  • urls:

    • hospedeiras inteiro PRIMARY KEY <--- links para hosts.name
    • caminho (VARCHAR 100) CHAVE PRIMÁRIA <--- nenhuma ligação para qualquer lugar

    SELECIONAR CONCAT (H.name, U.path) a partir de URLs U ADERIR hospedeiros H NO H.id = U.host;

No geral, o melhor conselho é para rastrear e perfil para ver o que realmente está tendo o tempo. Mas aqui estão meus pensamentos sobre coisas específicas para olhar.

(1) eu diria que você deseja garantir que os índices não são usados ??na execução desta consulta. Desde que você não tem condições de filtragem, deve ser mais eficiente para full-scan todas as mesas e depois juntá-las com uma operação de ordenação-merge ou hash.

(2) A concatenação é certamente tomar algum tempo, mas eu não entendo por que as pessoas estão recomendando para removê-lo. Você presumivelmente, em seguida, precisa fazer a concatenação em outro pedaço de código, onde ainda levaria aproximadamente a mesma quantidade de tempo (a menos que concatenação do MySQL é particularmente lento por algum motivo).

(3) A transferência de dados a partir do servidor para o cliente provavelmente está tomando um tempo significativo, muito possivelmente mais do que o tempo as necessidades do servidor para obter os dados. Se você tiver ferramentas para rastrear esse tipo de coisa, usá-los. Se você pode aumentar o tamanho de busca array em seu cliente, experiência com tamanhos diferentes (por exemplo, em JDBC uso Statement.setFetchSize ()). Isto pode ser significativo, mesmo se o cliente eo servidor estão no mesmo host.

Eu tentaria criar uma nova tabela com os dados que você quer obter. Fazer isso significa que você perde alguns dados reais, mas você ganha em rapidez. Poderia esta ideia ser semelhante ao OLAP ou algo parecido?

É claro que você tem que fazer uma atualização (diário ou qualquer outro) desta tabela.

Não sou especialista MySQL, mas parece que as chaves primárias MySQL são agrupados - você vai querer ter certeza de que é o caso com suas chaves primárias; índices de cluster vai certamente ajudar a acelerar as coisas.

Uma coisa, porém - Eu não acredito que você pode ter duas chaves "primárias" em qualquer mesa; seus urls aparência de mesa em vez suspeitos para mim por essa razão. Acima de tudo, você deve fazer absolutamente certo essas duas colunas na tabela de urls são indexados ao máximo - um único índice numérico de cada um deve estar bem - porque você está se juntando a eles, de modo que o DBMS precisa saber encontrá-los rapidamente; que poderia ser o que está acontecendo no seu caso. Se você é full-table-digitalização que muitas linhas, então sim, você pode estar sentado lá por algum tempo, enquanto as tentativas servidor para encontrar tudo o que você pediu.

Eu também sugerem remover essa função CONCAT da instrução SELECT, e ver como isso afeta seus resultados. Eu ficaria surpreso se isso não fosse um fator que contribui de alguma forma. Apenas recuperar ambas as colunas e lidar com a concatenação depois, e ver como isso vai.

Por fim, você já descobriu onde o gargalo é? Acaba de entrar em três mesas de vários milhões de linhas não deve demorar muito tempo a todos (eu esperaria talvez um segundo ou assim, apenas eyeballing suas tabelas e consulta), desde as mesas estão devidamente indexado. Mas se você está empurrando as linhas ao longo de um NIC lento ou já indexada, para um servidor de aplicativo sedentos de memória, etc., a lentidão poderia ter nada a ver com sua consulta em tudo, mas sim com o que acontece após a consulta. Sete milhões de linhas é um pouco de dados a montagem e movendo-se, independentemente de quanto tempo a descoberta dessas linhas acontece a tomar. Tente selecionar apenas uma linha em vez disso, em vez de todos os sete milhões, e ver como que se parece ao contrário. Se isso é rápido, então o problema não é a consulta, é o conjunto de resultados.

Tal como o seu conjunto de resultados retorna todos os dados, há muito pouco de otimização que pode ser feito em tudo. Que está a digitalizar toda a tabela, em seguida, ingressar em outras tabelas que têm índices.

são os PrimaryKeys cluster? Isto assegura que os dados são armazenados no disco no fim do índice, evitando assim a saltitar diferentes partes do disco.

Além disso, você pode ter a disseminação de dados através de vários discos. Se você tiver URLs no primário e caminhos / hosts em SECUNDÁRIA então você vai ter melhor rendimento das unidades.

Você precisa olhar para a configuração do servidor. Os parâmetros de memória padrão para o MySQL irá prejudicar o desempenho em uma tabela que tamanho. Se você estiver usando os padrões, você precisa levantar pelo menos key_buffer_size e join_buffer_size por pelo menos um fator de 4, talvez muito mais. Procure na documentação; existem outros parâmetros de memória que você pode alterar.

O MySQL tem uma peculiaridade engraçado desempenho, onde se suas tabelas de passar por cima de um certo tamanho com consultas que irá retornar a maioria dos dados, o desempenho vai para o banheiro. Infelizmente, ele não tem nenhuma maneira de dizer-lhe quando esse limite é atingido. Parece-me que você tem, no entanto.

Tente otimizar suas tabelas antes de executar a consulta:

optimize table hosts, paths, urls;

Pode poupar algum tempo, especialmente se linhas foram excluídas das tabelas. (Veja aqui para mais informações sobre OPTIMIZE)

Você já declarou algumas índices nas juntam-atributos?

PS: Veja aqui [link quebrado] para índices em MySQL 4.x

O concat é, definitivamente, retardando para baixo. podemos ver os resultados de um MySQL explicar sobre isso? Documentação Fazer a ligação

A maior coisa a fazer é tentar puxar apenas os dados que você precisa embora. Se você pode puxar menos registros que irá acelerar-lo tanto quanto qualquer coisa. Mas um MySQL explicar deve nos ajudar a ver se os índices ajudaria.

Eu entendo que você quer uma lista completa de URLs - que é de 7 milhões de discos. Talvez como sugerido por Mitch você deve considerar usando a cláusula WHERE para filtrar os resultados. Talvez o tempo está relacionada principalmente ao atraso na exibição de registros

check para esta consulta

select count(*)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id

Se isso ainda é lento Eu iria e verificar tempo para selecionar count (*) de urls

então

select count(*) 
from urls u 
inner join hosts h on u.host = h.id

então

select count(*) 
from urls u 
inner join hosts h on u.host = h.id
inner join paths p on u.path = p.id

apenas para localizar a origem do abrandamento

Além disso, por vezes, reordenando sua consulta pode ajudar

SELECT CONCAT(u.host, u.path)
from urls u 
inner join hosts h on u.host = h.id
inner join paths p on u.path = p.id

Eu não posso dizer com certeza sobre o MySQL, mas eu sei que no SQL Server que chaves primárias criar um índice automaticamente, mas chaves estrangeiras não. Certifique-se de verificar que há um índice em seus campos de chave estrangeira.

Desde que eu não sou um grande fã de MySQL, gostaria de perguntar se você já tentou PostgreSQL. Nesse DB, você iria querer ter certeza de que sua configuração work_mem foi bastante elevada, mas você pode configurá-lo por conexão DB com SET work_mem = 64MB, por exemplo.

Outra sugestão é olhar para usar entradas de caminho duplicados. Existem são muitas URLs que compartilham caminhos.

Outra coisa que pode ou não ajuda está usando campos de texto de comprimento fixo em vez de varchars. É usado para fazer a diferença de velocidade, mas não tenho certeza sobre os motores DB atuais.

Se você fizer uso PostgreSQL ele vai deixar você usar Cadastre usando, mas mesmo em MySQL eu gosto mais: o nome do seu campo id a mesma em cada mesa. Em vez de id em hospedeiros e anfitrião em urls, nomeá-lo HOST_ID ambos os lugares.

Agora, alguns mais comentários. :) Este layout de dados que você tem aqui é muito útil quando você está selecionando um pequeno conjunto de linhas, talvez a cada URL do mesmo domínio. Ele também pode ajudar a muito se suas consultas frequentemente precisa fazer varreduras seqüenciais da tabela de urls para outros dados armazenados lá, porque a digitalização pode pular sobre os grandes campos de texto (A menos que isso não importa, porque seu DB lojas de texto via ponteiros para uma tabela ligada de qualquer maneira).

No entanto, se você quase sempre selecionar todos os dados de domínio e caminho, então faz mais sentido para armazená-lo em uma tabela.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top