LINUX.ORG.RU

MySQL: а можно ли одним запросом?...


0

0

Есть таблица, данные из которой надо выводить с разбивкой по страницам. Данных МНОГО. LIMIT N,M тормозит ужасно. Поэтому в таблице давно есть поле `page`, где и хранится нужное число.

Всё хорошо, но иногда данные модифицируются так, что нужен пересчёт числа страниц. Сейчас это сделано внешне (считываем все тысячи записей по заданному критерию, крутим цикл в скрипте, сажая тысячи UPDATE, в каждую запись).

Подумалось тут - а, может, я торможу.

И можно как-то извратиться одним запросом

SET `page` = <some> WHERE ... ORDER BY ...

?

Пусть он хоть 2-3 секунды для 5000 записей думает, такие задержки перетерпеть можно, сейчас до 10 секунд уходит, что уже выглядит как тормоза при пересчётах...

★★★★★

Ответ на: комментарий от Karapuz

> А поцчему не взять какой-нить JForum попробовать? Он тоже над MySQL надстроен?

Вообще идея потестить форумы интересная, но функционал м.б. разный. А меня больше интересует потестить мускуля.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от qnikst

#include <assert.h>
#include <stdio.h>
#include <vector>
typedef unsigned int uint;

template<class E> class VectorWithSum
{
    public:
        void push_back(uint i) {
            body.push_back(i);
            summa+=i;
        }
        void decrement_at(uint i) {
            --body.at(i);
            --summa;
        }
        VectorWithSum(): body(), summa()  {}
        E sum() const                     { return summa; }
        uint size() const                 { return body.size(); }
        const E& operator[](uint i) const { return body.at(i); }
    private:
        std::vector<E> body; /// почему не наследуем? наследование сэкономит 3 строки
        E summa;
};
class Random
{
    public:
        Random(uint s): seed(s<<2+1) /* конвертируем в число, равное 1 по модулю 4 */ {
        }
        uint choice(const VectorWithSum<uint>& p) /// вероятность вернуть i равна p[i]/p.sum()
        {
            seed+=seed<<2;
            uint normalized = ( (long long unsigned int)seed*p.sum() )>>32;
            for(uint i=0, accumulated=0; i<p.size(); i++) {
                if( normalized < (accumulated+=p[i]) ) return i;
            }
            return p.size()-1;
        }
    private:
        uint seed;
};
int main(int argc, char** argv)
{
    assert(sizeof(uint)==4);
    /// sample usage:  ./a.out 123456789 987654321 7 30 4 1 2 2
    const int N=50;
    if( 5+2*N<argc or argc<5+2 or argc%2 != 1 ) return 1;

    Random r_size=atoi(argv[1]);    /// random seed, определяет длины топиков
    Random r_post=atoi(argv[2]);    /// random seed, определяет порядок INSERT/SELECT в топиках
    uint desired_sum=atoi(argv[3]); /// приблизительное количество мессаг в активных топиках
    uint max_id=atoi(argv[4]);      /// максимальное количество мессаг

    std::vector<uint> topic_size_possibility;
    VectorWithSum<uint> topic_size_probability;

    for( int i=5; i<argc; i+=2 ) {
        topic_size_possibility.push_back(atoi(argv[i  ])); /// длина топика, например 2000
        topic_size_probability.push_back(atoi(argv[i+1])); /// "ненормализованная вероятность", например 2
        /// вероятность получить именно такую длину топика --
        /// это "ненормализованная вероятность", деленная на сумму "ненормализованных вероятностей"
    }
    VectorWithSum<uint> topic_vacancy_count; /// количество незапощенных мессаг в топике

    for( uint id=1; id<max_id; id++ ) {
        while( topic_vacancy_count.sum() < desired_sum ) {
            uint value=topic_size_possibility[r_size.choice(topic_size_probability)];
            topic_vacancy_count.push_back(value);
            printf("# topic_vacancy_count.size()=%d value=%d topic_vacancy_count.sum()=%d\n",
                topic_vacancy_count.size(), value, topic_vacancy_count.sum() );
        }
        /// here is topic_vacancy_count.sum()>=desired_sum
        uint topic=r_post.choice(topic_vacancy_count);
        topic_vacancy_count.decrement_at(topic);
        printf("id=%d topic=%d topic_vacancy_count[topic]=%d\n",
           id, topic, topic_vacancy_count[topic] );
    }
    return 0;
}

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Замечания:

1. мне влом было оптимизировать алгоритм (того и гляди ошибусь) потому накатал на с++

2. выхлоп этой проги -- это заготовка для инсертов (превращается в нее перловым однострочником), зато такой выхлоп достаточно маленький и еще является и заготовкой для селектов

3. для генерации "других" селектов под те же длины топиков нужно оставить тем же первый random seed и поменять второй

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

В общем забенчил Только свой вариант:

Сделал 2500 тем с кол-вом сообщений от 1 до 2500 с определённым кривым распределением. Большое количество тем скорее всего подойдёт под вид форума:

SELECT topic_id, count( id ) cnt FROM `forum` GROUP BY topic_id ORDER BY cnt DESC LIMIT 20;

(1140,2499) (1593,2499) (1106,2498) (1169,2498) (30,2498) (1462,2498) ...

Тестил на 30,1169 и 1462 каждый раз меняя id, чтобы мускул меньше кешировал у себя внутрях.

Итог для 100 страницы: 4) UNIQUE (id,topic_id) /* да, знаю вариант тупой */

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

| 1 | SIMPLE | forum | index | NULL | id | 8 | NULL | 2539606 | Using where; Using index |

1.8 с

3). INDEX (topic_id) | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

| 1 | SIMPLE | forum | ref | topic_id | topic_id | 4 | const | 2310 | |

0.0064с

2). INDEX(topic_id,id)

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

| 1 | SIMPLE | forum | ref | topic_id | topic_id | 4 | const | 2719 | Using index |

0.0022с

1). INDEX (page)

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

| 1 | SIMPLE | forum | ref | page | page | 4 | const | 23 | Using where |

0.0005с

Примечания: перед тестом нового варианта иднексы обновлялись, unique и index не давали различия в рамках одного порядка.

В общем-то ЧТД

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Если что дамп, и более подробные данные скрипт заполения на пыхыпы могу скинуть.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Интересно,

1. откуда взялась разница в 4.5 раза 0.0022с --- 0.0005с

2. какой величины была база по сравнению с оперативкой

3. примерно что за (кривое) распределение было

Но в общем да, ЧТД.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>1. откуда взялась разница в 4.5 раза 0.0022с --- 0.0005с

Потому что mysql'ю сортировать надо не несколько тысяч строк (чтобы взять посление N записей среди них), а просто несколько :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

> Потому что mysql'ю сортировать надо не несколько тысяч строк (чтобы взять посление N записей среди них), а просто несколько

При наличии индекса (topic_id, id) мускул не сортирует ничего.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Сортирует :) Иначе как объяснить планомерное снижение скорости по мере приближения к концу большой выборки?

...

И, вообще, это как бы очень известная в Web'е проблема :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

1). разница, наверное, из explain rows = 2719 в первом случае и rows = 23 во втором

Т.е. получив 2719 строк мускул начинает по этим строкам искать требуемое, чего нету во втором случае

2). вечером могу сказать точно, по записей меньше чем 2500*2500, а оперативки 2Гб на незагруженном компе (где-то 600Мб занято включая кеши)

3). (x-2300)^2 + 2300^2 - нормировать на 1 лень

qnikst ★★★★★
()
Ответ на: комментарий от KRoN73

Скорее даже не сортировал, ему оно не надо - там же индекс (при сортировке и куже могло быть), а просто шёл по листам до 2475-той записи.

qnikst ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.