2014-11-17 3 views
0

Итак, у меня есть таблица примерно с 48 миллионами строк.Улучшение выполнения запросов суммарной суммы Mysql

User Count 
1232 12 
12331 4534 
...  ... 
...  .... 

Это таблица только для чтения. Там не будет больше данных, добавленных к этому. Мне нужно найти сумму счета столбца для всех пользователей между двумя идентификатору пользователя ...

Запрос я прямо сейчас:

Select sum(count) from table where user between x and Y. 

Но это занимает, как более чем на 2 секунды для каждого запрос. Мне нужно резко снизить его. Есть ли способ, которым я мог бы это сделать? Нужно ли создавать кластерный индекс или любую другую конфигурацию, о которой вы можете думать?

UPDATE: У меня уже есть индекс на столбце пользователя

+0

Просто указатель на 'User'. Если это не поможет, вам может потребоваться предварительно рассчитать суммы на куски вашей базы данных. – Amadan

+0

> Итак, у меня есть таблица, подобная этой, с более чем миллионом строк. Это не так много и не должно приводить к снижению производительности. У вас есть указатель на User-Column? Если нет, это увеличит производительность, поскольку база данных не потребуется сначала заказывать таблицу, а затем сканировать предоставленные идентификаторы пользователей. Что объясняет план выбора суммы (подсчета) из таблицы, где пользователь между x и Y говорит о том, как база данных обрабатывает ваш запрос? –

+0

Его фактически 48 миллионов. извините за неправильную информацию – Sparsh

ответ

0

Немного вот-тек ответ, может быть, из-самоучки человека незнакомого с некоторыми из более продвинутых методик. Отказ от ответственности завершен. Вот что я сделал бы, если бы я ЗНАЛ, что данные НЕ будут меняться.

Я бы создал скрипт, чтобы разбить его на несколько десятков или даже сто отдельных таблиц, сгруппированных и названных в соответствии с диапазоном. Например, таблицу можно назвать «cluster_1_to_10000» или что-то другое, основанное на том, что вы знаете о диапазонах и номерах пользователей.

Таким образом, когда вы запрашиваете, вы можете сделать динамическое имя таблицы в своем коде в соответствии с диапазоном и сэкономить время на фильтрацию через ошибки нерелевантных данных. Это сделает PHP? более сложный, особенно если диапазон упал между несколькими таблицами, но я думаю, что дополнительные 30 строк или около того PHP-кода были бы полезны, чтобы пропускать несколько сотен тысяч строк каждый раз.

Не уверен, что это объясняется хорошо, я могу привести примеры псевдокода, если хотите.

Edit для псевдокода

Таблицы названы: «table_1_to_499999», «table_500000_to_999999» и т.д. Вы должны были бы написать сценарий, чтобы разбить их и создать каждую таблицу, и, очевидно, сохранить оригинал, просто в случае.

псевдокод (жаль не знаком с Java):

Var StartTable=""; 
Var EndTable=""; 

var Table=array(); 

Table=//populate table from select tables statement to get them in order 
//make each tables key the start value as it pulls the table list from your database 
//for example 
Table[1]="table_1_to_499999" 
Table[500000]="table_500000_to_999999", etc. 

//now you have your two user id's 
user-id1 and user-id2; 

var table1=''; 
var table2=''; 

var key1=0; 
var key2=0; 

foreach(Table as key=>val) 
{ 
    if(user_id1>key) 
    { 
     table1=val; 
     key1=key; 
    } 
} 

foreach(Table as key=>val) 
{ 
    if(user_id2>key) 
    { 
     table2=val; 
     key2=key; 
    } 
} 

if(key1==key2) 
{ 
    //do your query here, all from the same table, both id's are in the same table, so you can query either table1 or table2 to get your data 
} 
else if(key1>key2) 
{ 
    //query all results greater than or equal to user-id1 in table 1 
    //query all results less than or equal to user-id2 in table 2 
    //add the two results together 
} 
else if(key1<key2) 
{ 
    //query all results less than or equal to user-id1 in table 1 
    //query all results greater than or equal to user-id2 in table 2 
    //add the two results together 
} 

Я думаю, что о охватывает его ... может быть, я на обед, но я думаю, что это будет при увеличении сложности один раз, резко экономить по запросам. Вы МОЖЕТЕ делать два запроса, чтобы вытащить данные, увеличив некоторые из ваших накладных расходов, но вы только запрашиваете миллионы строк, вместо 48 миллионов. Меньше зависит от того, как вы их разделяете.

+0

Я как бы понимаю, что вы говорите. Я не использую PHP. Я использую java для запроса таблицы. Было бы полезно использовать псевдокод. Кроме того, существует 48 миллионов строк, поэтому я не знаю, какие куски нужно разбить на – Sparsh

+0

Возможно, 500 000 за стол? Еще меньше оптимизировать. Вы можете использовать оператор switch, чтобы сузить таблицы до запроса на основе номеров идентификаторов, которые вы используете. – Dale

+0

Но не будет ли такого типа требовать множественные вызовы в базу данных? У меня также есть 10000rps доступа к базе данных. Итак, было бы возможно? – Sparsh

1

Если вы получаете неудовлетворительную производительность с индексом на месте, и ваша база данных доступна только для чтения, вы можете рассмотреть метод, который я упомянул в комментариях: прекомпретировать суммы для кусков. Он идет на один шаг дальше, чем разбиение на разделы: разбиение будет способно вычислять вашу сумму параллельно, но предварительно рассчитанные суммы превзошли его с большим отрывом. Идеальный размер куска находится где-то вокруг квадратного корня из числа строк в вашей таблице.

Скажите, что это ваш стол:

CREATE TABLE foo (
    user INTEGER AUTO_INCREMENT PRIMARY KEY, 
    cnt INTEGER 
); 

INSERT INTO foo (cnt) VALUES (1), (4), (9), (16), (25), (36), (49), (64), (81), (100); 

Теперь сделайте предварительно вычисленную таблицу куска суммы.Я использую переменные SQL для ясности, вы, вероятно, не нужно будет использовать те, как вы будете строить свой запрос с другого языка программирования:

SET @block = 3; 

CREATE TABLE foosums (
    block INTEGER PRIMARY KEY, 
    cntsum INTEGER 
) 
SELECT FLOOR((user - 1)/@block) AS block, SUM(cnt) AS cntsum 
FROM foo GROUP BY block; 

Теперь, чтобы вычислить сумму между @from и @to, вы бы захватить сумму всех полных кусков между этими двумя, и добавить все отдельные строки, которые были до кусков и после кусков. В этом примере, чтобы добавить строки 1..10, мы возьмем кусок 1 ... 3, кусок 4..6, 7..9 кусок, и отдельную строку 10.

SET @from=1, @to=10; 

SELECT 
    COALESCE((
    SELECT SUM(cnt) 
    FROM foo 
    WHERE user >= @from AND user < CEILING((@from - 1)/@block) * @block + 1 
), 0) 
+ COALESCE((
    SELECT SUM(cntsum) 
    FROM foosums 
    WHERE block >= CEILING((@from - 1)/@block) AND block < FLOOR(@to/@block) 
), 0) 
+ COALESCE((
    SELECT SUM(cnt) 
    FROM foo 
    WHERE user > FLOOR(@to/@block) * @block AND user <= @to 
), 0) 
AS blocked_total; 

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

SELECT SUM(cnt) AS individual_total FROM foo WHERE user >= @from AND user <= @to; 

И в конце концов, визуализация, чтобы помочь вам увидеть, какие именно данные охватывается оптимизированный запрос:

SELECT * FROM foo WHERE user >= @from AND user < CEILING((@from - 1)/@block) * @block + 1; 
SELECT * FROM foosums WHERE block >= CEILING((@from - 1)/@block) AND block < FLOOR(@to/@block); 
SELECT * FROM foo WHERE user > FLOOR(@to/@block) * @block AND user <= @to; 

SQLFiddle


*) "chunk" == "block". Я написал код перед текстом и не хотел менять терминологию: p

+0

Actualy закончил тем, что реализовал то же самое – Sparsh

Смежные вопросы