【问题标题】:60 million entries, select entries from a certain month. How to optimize database?6000万条条目,选择某月的条目。如何优化数据库?
【发布时间】:2011-07-23 23:57:57
【问题描述】:

我有一个包含 6000 万个条目的数据库。

每个条目包含:

  • 身份证
  • 数据源ID
  • 一些数据
  • 日期时间

  1. 我需要选择某个月份的条目。每个月包含大约 200 万条条目。

     select * 
       from Entries 
      where time between "2010-04-01 00:00:00" and "2010-05-01 00:00:00"
    

    (查询大约需要 1.5 分钟)

  2. 我还想从给定的 DataSourceID 中选择某个月份的数据。 (大约需要 20 秒)

大约有 50-100 个不同的 DataSourceID。

有没有办法让它更快?我有哪些选择? 如何优化这个数据库/查询?


编辑:大约有。每秒 60-100 次插入!

【问题讨论】:

  • 我目前没有索引。表格只是按 ID 排序。

标签: mysql sql query-optimization


【解决方案1】:

要更快地获取特定月份、特定年份的条目 - 您将need to index the time column

CREATE INDEX idx_time ON ENTRIES(time) USING BTREE;

另外,使用:

SELECT e.* 
  FROM ENTRIES e
 WHERE e.time BETWEEN '2010-04-01' AND DATE_SUB('2010-05-01' INTERVAL 1 SECOND)

...因为 BETWEEN 具有包容性,因此您发布的查询会得到任何日期为“2010-05-01 00:00:00”的内容。

我还想从给定的 DataSourceID 中选择某个月份的数据

您可以为 datasourceid 列添加单独的索引:

CREATE INDEX idx_time ON ENTRIES(datasourceid) USING BTREE;

...或设置一个覆盖索引以包含两列:

CREATE INDEX idx_time ON ENTRIES(time, datasourceid) USING BTREE;

覆盖索引要求必须在查询中使用最左边的列才能使用索引。在此示例中,首先使用 time 将适用于您提到的两种情况 - 不必使用 datasourceid 来使用索引。 但是,您必须通过查看 EXPLAIN 输出来测试您的查询,以真正了解什么最适合您的数据以及对这些数据执行的查询。

也就是说,索引会减慢 INSERT、UPDATE 和 DELETE 语句的速度。如果列数据具有很少的不同值,则索引不会提供很多价值 - IE:布尔列是索引的错误选择,因为基数很低。

【讨论】:

  • 请求 200 万行的查询通常更喜欢表扫描而不是索引范围搜索,所以我认为 MySQL 不会使用索引 (time)。 200 万个表查找太昂贵了。该索引必须覆盖。
  • @Andomar:是的,但我喜欢提供选项和工具来审查,以便人们学习。我们也看到人们隐藏细节。
  • 是否会按时添加索引,datasourceid 会显着减慢添加到 ENTRIES 表的速度?因为这种情况经常发生。
  • @JBeurer:如上一段所述,是的。一切都会有所取舍,您必须决定什么更重要,并可能进行重组以更好地支持两者。
  • 哇。按时添加索引将查询时间从 1.5 分钟缩短到 3 秒。现在,如果这不会显着减慢插入速度。
【解决方案2】:

利用 innodb 聚集的主键索引。

http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html

这将非常高效:

create table datasources
(
year_id smallint unsigned not null,
month_id tinyint unsigned not null,
datasource_id tinyint unsigned not null,
id int unsigned not null, -- needed for uniqueness
data int unsigned not null default 0,
primary key (year_id, month_id, datasource_id, id)
)
engine=innodb;

select * from datasources where year_id = 2011 and month_id between 1 and 3;

select * from datasources where year_id = 2011 and month_id = 4 and datasouce_id = 100;

-- etc..

编辑 2

忘记了我正在运行第一个测试脚本,其中包含 3 个月的数据。这是一个月的结果:0.34 和 0.69 秒。

select d.* from datasources d where d.year_id = 2010 and d.month_id = 3 and datasource_id = 100 order by d.id desc limit 10;
+---------+----------+---------------+---------+-------+
| year_id | month_id | datasource_id | id      | data  |
+---------+----------+---------------+---------+-------+
|    2010 |        3 |           100 | 3290330 | 38434 |
|    2010 |        3 |           100 | 3290329 |  9988 |
|    2010 |        3 |           100 | 3290328 | 25680 |
|    2010 |        3 |           100 | 3290327 | 17627 |
|    2010 |        3 |           100 | 3290326 | 64508 |
|    2010 |        3 |           100 | 3290325 | 14257 |
|    2010 |        3 |           100 | 3290324 | 45950 |
|    2010 |        3 |           100 | 3290323 | 49986 |
|    2010 |        3 |           100 | 3290322 |  2459 |
|    2010 |        3 |           100 | 3290321 | 52971 |
+---------+----------+---------------+---------+-------+
10 rows in set (0.34 sec)

select d.* from datasources d where d.year_id = 2010 and d.month_id = 3 order by d.id desc limit 10;
+---------+----------+---------------+---------+-------+
| year_id | month_id | datasource_id | id      | data  |
+---------+----------+---------------+---------+-------+
|    2010 |        3 |           116 | 3450346 | 42455 |
|    2010 |        3 |           116 | 3450345 | 64039 |
|    2010 |        3 |           116 | 3450344 | 27046 |
|    2010 |        3 |           116 | 3450343 | 23730 |
|    2010 |        3 |           116 | 3450342 | 52380 |
|    2010 |        3 |           116 | 3450341 | 35700 |
|    2010 |        3 |           116 | 3450340 | 20195 |
|    2010 |        3 |           116 | 3450339 | 21758 |
|    2010 |        3 |           116 | 3450338 | 51378 |
|    2010 |        3 |           116 | 3450337 | 34687 |
+---------+----------+---------------+---------+-------+
10 rows in set (0.69 sec)

编辑 1

决定用大约测试上述模式。 6000 万行分布在 3 年内。每个查询都是冷运行的,即每次单独运行,然后重新启动 mysql,清除任何缓冲区并且没有查询缓存。

完整的测试脚本可以在这里找到:http://pastie.org/1723506 或以下...

正如您所见,即使在我简陋的桌面上,它也是一个非常高效的架构 :)

select count(*) from datasources;
+----------+
| count(*) |
+----------+
| 60306030 |
+----------+

select count(*) from datasources where year_id = 2010;
+----------+
| count(*) |
+----------+
| 16691669 |
+----------+

select
 year_id, month_id, count(*) as counter
from
 datasources
where 
 year_id = 2010
group by
 year_id, month_id;
+---------+----------+---------+
| year_id | month_id | counter |
+---------+----------+---------+
|    2010 |        1 | 1080108 |
|    2010 |        2 | 1210121 |
|    2010 |        3 | 1160116 |
|    2010 |        4 | 1300130 |
|    2010 |        5 | 1860186 |
|    2010 |        6 | 1220122 |
|    2010 |        7 | 1250125 |
|    2010 |        8 | 1460146 |
|    2010 |        9 | 1730173 |
|    2010 |       10 | 1490149 |
|    2010 |       11 | 1570157 |
|    2010 |       12 | 1360136 |
+---------+----------+---------+
12 rows in set (5.92 sec)


select 
 count(*) as counter
from 
 datasources d
where 
 d.year_id = 2010 and d.month_id between 1 and 3 and datasource_id = 100;

+---------+
| counter |
+---------+
|   30003 |
+---------+
1 row in set (1.04 sec)

explain
select 
 d.* 
from 
 datasources d
where 
 d.year_id = 2010 and d.month_id between 1 and 3 and datasource_id = 100
order by
 d.id desc limit 10;

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-----------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  |rows    | Extra                       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-----------------------------+
|  1 | SIMPLE      | d     | range | PRIMARY       | PRIMARY | 4       | NULL |4451372 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-----------------------------+
1 row in set (0.00 sec)


select 
 d.* 
from 
 datasources d
where 
 d.year_id = 2010 and d.month_id between 1 and 3 and datasource_id = 100
order by
 d.id desc limit 10;

+---------+----------+---------------+---------+-------+
| year_id | month_id | datasource_id | id      | data  |
+---------+----------+---------------+---------+-------+
|    2010 |        3 |           100 | 3290330 | 38434 |
|    2010 |        3 |           100 | 3290329 |  9988 |
|    2010 |        3 |           100 | 3290328 | 25680 |
|    2010 |        3 |           100 | 3290327 | 17627 |
|    2010 |        3 |           100 | 3290326 | 64508 |
|    2010 |        3 |           100 | 3290325 | 14257 |
|    2010 |        3 |           100 | 3290324 | 45950 |
|    2010 |        3 |           100 | 3290323 | 49986 |
|    2010 |        3 |           100 | 3290322 |  2459 |
|    2010 |        3 |           100 | 3290321 | 52971 |
+---------+----------+---------------+---------+-------+
10 rows in set (0.98 sec)


select 
 count(*) as counter
from 
 datasources d
where 
 d.year_id = 2010 and d.month_id between 1 and 3;

+---------+
| counter |
+---------+
| 3450345 |
+---------+
1 row in set (1.64 sec)

explain
select 
 d.* 
from 
 datasources d
where 
 d.year_id = 2010 and d.month_id between 1 and 3
order by
 d.id desc limit 10;

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-----------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  |rows    | Extra                       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-----------------------------+
|  1 | SIMPLE      | d     | range | PRIMARY       | PRIMARY | 3       | NULL |6566916 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-----------------------------+
1 row in set (0.00 sec)


select 
 d.* 
from 
 datasources d
where 
 d.year_id = 2010 and d.month_id between 1 and 3
order by
 d.id desc limit 10;

+---------+----------+---------------+---------+-------+
| year_id | month_id | datasource_id | id      | data  |
+---------+----------+---------------+---------+-------+
|    2010 |        3 |           116 | 3450346 | 42455 |
|    2010 |        3 |           116 | 3450345 | 64039 |
|    2010 |        3 |           116 | 3450344 | 27046 |
|    2010 |        3 |           116 | 3450343 | 23730 |
|    2010 |        3 |           116 | 3450342 | 52380 |
|    2010 |        3 |           116 | 3450341 | 35700 |
|    2010 |        3 |           116 | 3450340 | 20195 |
|    2010 |        3 |           116 | 3450339 | 21758 |
|    2010 |        3 |           116 | 3450338 | 51378 |
|    2010 |        3 |           116 | 3450337 | 34687 |
+---------+----------+---------------+---------+-------+
10 rows in set (1.98 sec)

希望这会有所帮助:)

【讨论】:

  • 这将如何影响插入的性能?因为在包含 6000 万个条目和每秒 100 次插入的数据库中,这可能会成为问题,不是吗?
  • inserts 应该不是问题,因为您可能以集群 PK 顺序插入,即使对于 innodb 也是高效的:P
  • 如果行没有更新或删除,如果你知道我的意思,数据是否会自然地聚集在他的“时间”列周围,作为按“时间流逝”顺序插入行的副作用意思是?分配块的底层机制将决定这一点,但我不知道 MySQL 是如何做到的。你知道它是如何工作的吗?我的意思是,使用带有自动递增主键的 InnoDB 表可能会提供相同的集群优势,但没有胖键。
  • 如果他使用 myisam 并按时间顺序插入,则新数据将附加到文件末尾。但是,myisam 不支持集群主键,这意味着比等效的 innodb 模式更多的 I/O。
  • 3 秒 vs. 0.36 - 没有比赛 imo
【解决方案3】:

您可以使用索引来交换磁盘使用量以换取查询速度。以time 列开头的索引可以加快查询特定月份的查询速度:

create index IX_YourTable_Date on YourTable (time, DataSourceID, ID, SomeData)

因为索引以time 字段开头,MySQL 可以对索引进行键范围扫描。这应该尽可能快。索引应该包括查询中的所有列,否则 MySQL 将不得不从索引中查找表中每一行的数据。由于您要求 200 万行,MySQL 可能会忽略未覆盖的索引。 (覆盖索引 = 包含查询中所有行的索引。)

如果您从不查询 ID,您可以重新定义表以使用 (time, DataSourceID, ID) 作为主键:

alter table YourTable add primary key (time, DataSourceID, ID)

这将加快在time 上的搜索速度,而不会占用磁盘空间,但在ID 上的搜索速度会非常慢。

【讨论】:

    【解决方案4】:

    如果您还没有在时间字段上,我会尝试放置一个索引。

    对于 DataSourceID,您可以尝试使用 Enum 而不是 varchar/int。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-13
      • 2011-01-25
      • 1970-01-01
      • 2018-02-10
      • 2014-11-01
      相关资源
      最近更新 更多