AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 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

慢速选择索引。如何改进?

  • 772

我在单个表 (Engine=InnoDB) 上运行 select,不太复杂但有很多行。

第一次选择 id 比较慢,9M 行需要几秒钟,下一次选择要快得多,即使我更改查询也是如此。

我在 Windows 上尝试过 mysql,在 Linux 上尝试过 mariadb。

我像这样运行选择命令:

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';

或者像这样:

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

哪个查询先出现并不重要,第一个查询较慢。

当我在 linux 上的 mariadb 上运行时,结果 id 有时很快,但在 windows 上的 mysql 上它第一次也很慢。这让我觉得也许我错过了什么。

测试、结果和时间测量,可以在下面找到。

谢谢你的帮助!

这是我的数据库:

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
);

图形显示:

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)

我使用以下程序创建了 2 个 infile:

#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;
}

使用以下命令在 Linux 上编译它:

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

像这样运行 DataGen 程序:

DataGen 3000 3000 60

该程序创建 2 个文件:“items.dat”和“references.dat”

将数据加载到数据库中:

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

所以我用很多行填充了表格:项目有 3K 行,引用有 900 万行。

现在我在reference桌子上运行选择:

这是结果:

#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)

-- 只改变 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)

-- 其他查询 - 相同的 id - 快速

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)

-- 描述查询:

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 个回答
  • 92 Views

2 个回答

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

    哪个查询先出现并不重要,第一个查询较慢。

    正如 Gerald 解释的那样,这是因为从磁盘加载数据。

    第一个查询将受益于

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

    第二个:

    INDEX(id, counts)
    

    将使它非常快。

    当您未能提供PRIMARY KEY. 思考什么对表格有意义Reference。

    请注意 howDESCRIBE的描述性不如SHOW CREATE TABLE。

    听起来您的值也小得不合理innodb_buffer_pool_size——这控制着缓存。如果您有超过 4GB 的 RAM,我建议将 70% 的可用 RAM 分配给该设置。

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

    要是所有问题都得到了很好的记录就好了。可惜真的没有问题。大多数数据库都有内存缓存,当您选择之前选择的数据时,答案可以在内存中找到,无需访问磁盘或其他存储。经过的时间取决于返回的行数和所需的处理量将日期时间与字符串进行比较可能很昂贵,希望您的数据库以相反的方式进行。

    保持良好的工作!

    • 1

相关问题

  • 我在哪里可以找到mysql慢日志?

  • 如何优化大型数据库的 mysqldump?

  • 什么时候是使用 MariaDB 而不是 MySQL 的合适时机,为什么?

  • 组如何跟踪数据库架构更改?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

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

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve