Pergunta

Eu estou usando SQLite3 para armazenar um 5D grade regular de cerca de 1 000 000 de nós e tem alguns problemas com o desempenho da "ESCOLHA" de consulta.

Contexto

Descrição Do Banco De Dados

Cada entrada é composta por 5+25 duplos e representam um ponto da grade regular (nó):

  • 5 curiosidades sobre a dupla :coordenadas do ponto na 5D grade regular (v1,v2,...,v5)
  • 25 seguintes duplas :algumas características (p1,p2,...,p25)

Cada ponto é único (qualquer combinação dos 5 primeiros valores é único).A tabela é criada com CREATE TABLE myTable(v1 double,..., v5 double, p1 double,..., p25 double).Eu adicionei nenhuma restrição.

As entradas são ordenadas por ordem crescente, após as suas coordenadas (v1, em seguida, v2, v3,...) :

v1|v2|v3|v4|v5|p1|p2|p3|...
 0| 0| 0| 0| 0| x| x| x|...
 0| 0| 0| 0| 1| x| x| x|...
 0| 0| 0| 0| 2| x| x| x|...
...
 0| 0| 0| 1| 0| x| x| x|...
 0| 0| 0| 1| 1| x| x| x|...
 0| 0| 0| 1| 2| x| x| x|...
...

Eu ter criado um ÍNDICE na tabela, usando CREATE INDEX idx ON myTable (v1,v2,v3,v4,v5)

SELECIONE Consulta Descrição

Eu quero fazer um "cúbico" interpolação na 5D grade.Então, eu tenho que extrair 4 pontos em cada dimensão em torno do ponto que eu quero.A minha ESCOLHA de consulta deve retornar 4*4*4*4*4=1024 pontos.

Devido a propriedades simétricas, eu tenho que fazer 16 consultas em vez de 1.Cada pedido é da forma SELECT * FROM myTable WHERE (v1=X AND v2=X AND v3 BETWEEN x1 AND x2 AND v4 BETWEEN y1 AND y2 AND v5 BETWEEN z1 AND z2).De fato, v1 e v2 são os ângulos.Na minha 5D grade, eu ter valores de -165 180 com um passo de 15°.Portanto, se eu quiser interpolar um valor em -160, eu não posso fazer uma consulta do tipo v1 BETWEEN -180 AND -135 (para obter -180, -165, -150, -135) porque -180 não existe na minha mesa.Posso otimizar esta parte para fazer apenas 4 consultas, mas que não é a minha principal preocupação aqui.

Eu estou usando uma instrução preparada : sqlite3_prepare_v2(db,"SELECT * FROM myTable WHERE (v1=? AND v2=? AND v3 BETWEEN ? AND ? AND v4 BETWEEN ? AND ? AND v5 BETWEEN ? AND ?"),length,statement,NULL)

Em seguida, para cada consulta, o que eu faço :

sqlite3_bind_double(statement, int, double);
while(sqlite3_step(statement)==SQLITE_ROW) {
    // for each row (for each "node") :
    //   1) retrieve its properties
    double myvar1 = sqlite3_colum_double(statement,6)
    double myvar2 = sqlite3_colum_double(statement,7)
    ....
    double myvar25 = sqlite3_colum_double(statement,25)
    //   2) create the object and add it to a vector for the interpolation 
}
sqlite3_reset(statement);
sqlite3_clear_bindings(statement);

O banco de dados é aberto com sqlite3_open_v2(path,&db,SQLITE_OPEN_READONLY,NULL)

Problema

Ele leva cerca de 2,5 segundos para fazer 16 consultas e extrato de 1024 pontos.Sqlite3 operações de 99,8% do esforço de computação (callgrind)...

O que estou fazendo de errado ?

Eu tentei EXPLAIN QUERY PLAN SELECT * FROM myTable WHERE (v1=0 AND v2=0 AND v3 BETWEEN 0 AND 3 AND v4 BETWEEN 0 AND 3 AND v5 BETWEEN 0 AND 3.O resultado é SEARCH TABLE myTable USING INDEX idx (v1=? AND v2=? AND v3>? AND v3<?)

Será que vai ser mais rápido para criar um índice exclusivo com base em 5 coordenadas e fazer 1024 consultas ?Antes, eu usei um vetor e acesso direto por meio de índices.

Foi útil?

Solução

Índices normais funcionam melhor quando você fazer pesquisas usando comparações de igualdade (=).Como você tem visto a EXPLICAR o PLANO de CONSULTA de saída, um não-comparação de igualdade impede que qualquer outra colunas do índice a ser utilizado;o banco de dados deve verificar por meio de todos possível v4 e v5 linhas para encontrar os resultados.

  1. Você está consultando, ao invés de poucos pontos em um regular grelha, para que você saiba exatamente as coordenadas de todos os pontos que você deseja.Basta usar uma consulta que procura por um ponto, com os cinco coordenadas exatas, e executá-lo 1024 vezes.Isto irá resultar em um índice único de pesquisa, que é muito mais eficiente, mesmo se ele é executado para cada ponto.

    Para fazer diversas consultas ainda mais eficiente, envolva todos eles em uma única transação.

  2. Usar um separado R-tree índice para procurar os pontos.R-trees são otimizadas para (multi-dimensional) intervalo de consultas.Isso resultaria em uma consulta como esta:

    SELECT *
    FROM myTable
    WHERE rowid IN (SELECT id
                    FROM RtreeIndexTable
                    WHERE v1 = ?
                      AND v2 = ?
                      AND v3 BETWEEN ? AND ?
                      AND v4 BETWEEN ? AND ?
                      AND v5 BETWEEN ? AND ?)
    

    R-trees são normalmente utilizados para o irregular ou dados esparsos;se você pode sair com a consulta de pontos individuais, de uma R-tree pode ser um exagero.

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