AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 200100
Accepted
SHR
SHR
Asked: 2018-03-14 06:01:01 +0800 CST2018-03-14 06:01:01 +0800 CST 2018-03-14 06:01:01 +0800 CST

Slow Select com índices. Como pode ser melhorado?

  • 772

Eu corro select em uma única tabela (Engine=InnoDB), não muito complexa, mas com muitas linhas.

A primeira seleção para um id é mais lenta, demora alguns segundos para 9M de linhas, as próximas seleções são muito mais rápidas, mesmo quando mudo a consulta.

Eu tentei mysql no Windows e mariadb no Linux.

Eu corro o comando select assim:

select `id`,count(*), sum(`counts`) from reference
    where `id`=848
      and `started`<= '2000-01-04 00:00:00'
      and `ended`  >= '2000-01-03 00:00:00';

ou assim:

 select min(`counts`),max(`counts`) from reference where `id`=848 ;

não importa qual foi a primeira consulta, a primeira é mais lenta.

quando eu corro no mariadb no linux, o id consequente às vezes era rápido, mas no mysql no windows também era lento na primeira vez. isso me fez pensar que talvez eu estivesse perdendo alguma coisa.

testes, resultado e medida de tempo e podem ser encontrados abaixo.

Obrigado por qualquer ajuda!

Este é o meu db:

create database my_test_db default char set utf8 ;
use my_test_db;
create table items (
    `id` int(11) not null auto_increment, 
    `name` varchar(50), 
    `description` varchar(250) default '', 
    primary key (`id`), 
    unique key item_name_unique(`name`)
);
create table reference (
    `id` int(11) not null,
    `started` datetime not null,
    `ended` datetime not null,
    `counts` int(11) not null,
    key fk_item_id_idx (`id`),
    key idx_started (`started`),
    key idx_ended (`ended`),
    constraint fk_item_id foreign key (`id`) references items(`id`)
              on delete no action on update no action
);

Uma exibição gráfica:

MariaDB [my_test_db]> describe items;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| name        | varchar(50)  | YES  | UNI | NULL    |                |
| description | varchar(250) | YES  |     |         |                |
+-------------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

MariaDB [my_test_db]> describe reference;
+---------+----------+------+-----+---------+-------+
| Field   | Type     | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+-------+
| id      | int(11)  | NO   | MUL | NULL    |       |
| started | datetime | NO   | MUL | NULL    |       |
| ended   | datetime | NO   | MUL | NULL    |       |
| counts  | int(11)  | NO   |     | NULL    |       |
+---------+----------+------+-----+---------+-------+
4 rows in set (0.00 sec)

Eu usei o seguinte programa para criar 2 infiles:

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <sstream>
#include <cstring>
#include <random>
#define TM_BUF_SIZE 32

#ifndef WIN32
#define localtime_s(PTM,PTIME_T) localtime_r(PTIME_T,PTM)
#endif
int main(int argc, char** argv)
{
    int id_max, count_iterations, time_frame;
    if(argc!=4)
    {
        std::cerr<<"Missing Arguments!!"<<std::endl;
        std::cerr<<"Usage: DataGen item_count time_iteration time_frame"<<std::endl;
        return -1;
    }
    id_max  = (int)strtol(argv[1],nullptr,0);
    count_iterations = (int)strtol(argv[2],nullptr,0);
    time_frame = (int)strtol(argv[3],nullptr,0);

    std::random_device r;
    std::default_random_engine re(r());
    std::uniform_int_distribution<int> uni_dist(0, 15);
    std::tm temp, tmStart,tmEnd;
    char bufStart[TM_BUF_SIZE], bufEnd[TM_BUF_SIZE];
    std::memset(&temp, 0, sizeof(tm));

    std::ofstream fitems("items.dat");
    for (int id = 1; id <= id_max; id++)
    {
        fitems << id << "\tid-" << id << "\titem.number." << id << std::endl;
    }
    temp.tm_year = 100;
    temp.tm_mday = 1;
    time_t ts_start = mktime(&temp);
    time_t ts_end;
    int iteration_left = count_iterations;
    std::ofstream frefs("references.dat");
    while(iteration_left--)
    {
        ts_end = ts_start + time_frame;
        localtime_s(&tmStart, &ts_start);
        localtime_s(&tmEnd, &ts_end);
        std::strftime(bufStart, TM_BUF_SIZE, "%Y-%m-%d %H:%M:%S.0", &tmStart);
        std::strftime(bufEnd, TM_BUF_SIZE, "%Y-%m-%d %H:%M:%S.0", &tmEnd);
        for (int id = 1; id <= id_max; id++)
        {
            int count = uni_dist(re);
            frefs << id << "\t" << bufStart << "\t" << bufEnd << "\t"<<count<< std::endl;
        }
        ts_start = ts_end;
        if(iteration_left && 0 == iteration_left % 100)
        {
            std::cout<<iteration_left<<" iterations left"<<std::endl;
        }
    }
    std::cout<<"Done!"<<std::endl;
        return 0;
}

copiou no linux usando:

g++ -std=c++0x dataGen.cpp -o DataGen

execute o programa DataGen assim:

DataGen 3000 3000 60

O programa cria 2 arquivos: "items.dat" e "references.dat"

carregue os dados no banco de dados:

use my_test_db;
load data infile '/root/items.dat' into table items;
load data infile '/root/references.dat' into table reference;

Então eu preenchi as tabelas com muitas linhas: itens com 3K de linhas e referência com 9M de linhas.

agora estou executando selects na referencemesa:

Aqui estão os resultados:

#first time for this id: 
MariaDB [my_test_db]> select `id`,count(*), sum(`counts`) from reference where `id`=848 and `started`<= '2000-01-03 00:00:00' and `ended`>='2000-01-02 00:00:00';
+------+----------+---------------+
| id   | count(*) | sum(`counts`) |
+------+----------+---------------+
|  848 |     1442 |         10640 |
+------+----------+---------------+
1 row in set (3.31 sec)

#next query for same id change time filters:
MariaDB [my_test_db]> select `id`,count(*), sum(`counts`) from reference where `id`=848 and `started`<= '2000-01-04 00:00:00' and `ended`>='2000-01-03 00:00:00';
+------+----------+---------------+
| id   | count(*) | sum(`counts`) |
+------+----------+---------------+
|  848 |      121 |           944 |
+------+----------+---------------+
1 row in set (0.03 sec)

#next query for same id change time filters again:
MariaDB [my_test_db]> select `id`,count(*), sum(`counts`) from reference
    where `id`=848
      and `started`<= '2000-01-02 00:00:00'
      and `ended`  >= '2000-01-01 00:00:00';
+------+----------+---------------+
| id   | count(*) | sum(`counts`) |
+------+----------+---------------+
|  848 |     1441 |         10848 |
+------+----------+---------------+
1 row in set (0.06 sec)

-- altera apenas o id:

MariaDB [my_test_db]> select `id`,count(*), sum(`counts`) from reference
    where `id`=1848
      and `started`<= '2000-01-02 00:00:00'
      and `ended`  >= '2000-01-01 00:00:00';
+------+----------+---------------+
| id   | count(*) | sum(`counts`) |
+------+----------+---------------+
| 1848 |     1441 |         10576 |
+------+----------+---------------+
1 row in set (2.63 sec)

#use same id change time filters: 
MariaDB [my_test_db]> select `id`,count(*), sum(`counts`) from reference
    where `id`=1848
      and `started`<= '2000-01-02 12:00:00'
      and `ended`  >= '2000-01-01 12:00:00';
+------+----------+---------------+
| id   | count(*) | sum(`counts`) |
+------+----------+---------------+
| 1848 |     1442 |         10780 |
+------+----------+---------------+
1 row in set (0.03 sec)

#use consequent id is also fast:
MariaDB [my_test_db]> select `id`,count(*), sum(`counts`) from reference
    where `id`=1849
      and `started`<= '2000-01-02 12:00:00'
      and `ended`  >= '2000-01-01 12:00:00';
+------+----------+---------------+
| id   | count(*) | sum(`counts`) |
+------+----------+---------------+
| 1849 |     1442 |         11001 |
+------+----------+---------------+
1 row in set (0.11 sec)

-- outra consulta - mesmo id - rápido

MariaDB [my_test_db]> select min(counts),max(counts) from reference where `id`=1849 ;
+-------------+-------------+
| min(counts) | max(counts) |
+-------------+-------------+
|           0 |          15 |
+-------------+-------------+
1 row in set (0.03 sec)

#again it is slow for other id
MariaDB [my_test_db]> select min(counts),max(counts) from reference where `id`=1800 ;
+-------------+-------------+
| min(counts) | max(counts) |
+-------------+-------------+
|           0 |          15 |
+-------------+-------------+
1 row in set (2.36 sec)

-- descreve a consulta:

MariaDB [my_test_db]> describe select `id`,count(*), sum(`counts`) from reference where `id`=1849 and `started`<= '2000-01-02 12:00:00' and `ended`>='2000-01-01 12:00:00';
+------+-------------+-----------+------+--------------------------------------+----------------+---------+-------+------+-------------+
| id   | select_type | table     | type | possible_keys                        | key            | key_len | ref   | rows | Extra       |
+------+-------------+-----------+------+--------------------------------------+----------------+---------+-------+------+-------------+
|    1 | SIMPLE      | reference | ref  | fk_item_id_idx,idx_started,idx_ended | fk_item_id_idx | 4       | const | 2999 | Using where |
+------+-------------+-----------+------+--------------------------------------+----------------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql performance
  • 2 2 respostas
  • 92 Views

2 respostas

  • Voted
  1. Best Answer
    Rick James
    2018-03-15T16:59:23+08:002018-03-15T16:59:23+08:00

    não importa qual foi a primeira consulta, a primeira é mais lenta.

    Isso se deve ao carregamento dos dados do disco, como explicou Gerald.

    A primeira consulta se beneficiaria de

    INDEX(id, started),
    INDEX(id, ended)
    

    para o segundo:

    INDEX(id, counts)
    

    vai fazer isso muito rápido.

    O InnoDB não gosta quando você não fornece um arquivo PRIMARY KEY. Pondere o que faz sentido para a mesa Reference.

    Observe como DESCRIBEé menos descritivo do que SHOW CREATE TABLE.

    Também parece que você tem um valor excessivamente pequeno para innodb_buffer_pool_size-- isso controla o armazenamento em cache. Se você tiver mais de 4 GB de RAM, recomendo 70% da RAM disponível para essa configuração.

    • 2
  2. Gerard H. Pille
    2018-03-14T06:12:14+08:002018-03-14T06:12:14+08:00

    Se todas as perguntas fossem tão bem documentadas. Uma pena que não há realmente um problema. A maioria dos bancos de dados possui um cache de memória, e quando você seleciona dados que foram selecionados momentos antes, a resposta pode ser encontrada na memória, sem necessidade de acessar disco ou outro armazenamento. O tempo decorrido depende do número de linhas retornadas e da quantidade de processamento necessária Comparar datas e horas com strings pode ser caro, espero que seus bancos de dados tenham feito o contrário.

    Mantenha o bom trabalho!

    • 1

relate perguntas

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve