60 миллионов записей, выберите записи за определенный месяц.Как оптимизировать базу данных? - PullRequest
8 голосов
/ 27 марта 2011

У меня есть база данных с 60 миллионами записей.

Каждая запись содержит:

  • ID
  • DataSourceID
  • Некоторые данные
  • DateTime

  1. Мне нужно выбрать записи за определенный месяц.Каждый месяц содержит примерно 2 миллиона записей.

     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 вставок в секунду!

Ответы [ 4 ]

7 голосов
/ 27 марта 2011

Чтобы получить записи в конкретном месяце, для определенного года, быстрее - вам нужно проиндексировать столбец time :

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)

... потому что МЕЖДУ включительно, поэтому вы получите что-нибудь от "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: логический столбец - плохой выбор для индексации, потому что количество элементов низкое.

6 голосов
/ 27 марта 2011

Воспользуйтесь преимуществами индексов кластеризованного первичного ключа 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

Решили проверить вышеуказанную схему с прибл.60 миллионов строк в течение 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)

Надеюсь, это поможет:)

2 голосов
/ 27 марта 2011

Вы можете использовать индекс, чтобы обменять использование диска на скорость запроса. Индекс, начинающийся со столбца time, может ускорить запросы, запрашивающие определенный месяц:

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

Поскольку индекс начинается с поля time, MySQL может выполнять сканирование диапазона ключей для индекса. Это должно быть так быстро, как только может. Индекс должен включать в себя все столбцы в запросе, иначе MySQL должен будет просмотреть индекс для данных таблицы для каждой строки. Поскольку вы запрашиваете 2 миллиона строк, MySQL, скорее всего, будет игнорировать индекс, который не покрывает. (Покрывающий индекс = индекс, включающий все строки в запросе.)

Если вы никогда не запрашиваете идентификатор, вы можете переопределить таблицу, чтобы использовать (time, DataSourceID, ID) в качестве первичного ключа:

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

Это ускорит поиск на time бесплатно на диске, но поиск на ID будет очень медленным.

1 голос
/ 27 марта 2011

Я бы попробовал поставить индекс, если вы еще не указали поле времени.

Для DataSourceID вы можете попробовать использовать Enum вместо varchar / int.

...