MySQL: select ... limit много, count = долго

Тема в разделе "WASM.HEAP", создана пользователем _DEN_, 8 янв 2012.

  1. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Благодаря вот этому посту обнаружил такую вещь. limit не работает с индексами, и offset, похоже, просто перебирает значения. Если в таблице много записей, то получаются тормоза.

    location - таблица на ~миллион записей. После рестарта мускуля (тобишь - сброса его кешей), вот такие запросы отрабатываются ~20 секунд:

    Код (Text):
    1. select * from location limit 850000, 50
    2.  
    3. select * from location order by id limit 850000, 50
    Собственно, wtf? Размеры строк в таблице фиксированного размера. Физические позиции строк растут вместе со значением кластерного индекса. Следовательно, select без order by теоретически должен давать константное позиционирование, а с order by [индектированное поле] - log2 (n) поцизионирование. А тут, видимо, оно линейное. Как дальше жить?
     
  2. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    Переписать мускл?
     
  3. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    Вы в этом уверены? Записи в файле должны лежать чаще всего в порядке их инсертов (если не было удалений). А натуральный порядок выдачи SELECT * по возрастанию первичного ключа, если я не ошибаюсь. То есть, даже если из-за автоинкремента порядок записей в файле и возрастание ключа вроде бы должны совпадать, тем не менее, вы даете индексу совершенно чуждую ему задачу:

    "найди мне такую запись, перед которой в порядке возрастания первичного ключа имеется 850000 записей"

    Вы заставляете механизм индексирования заниматься полным обходом внутренних структур индекса для подсчета количества записей, тогда как его родная задача - быстро найти запись по значению.
     
  4. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Dmitry_Milk

    http://ru.wikipedia.org/wiki/Индекс_(базы_данных):
    http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html:
     
  5. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    Да да. Вот что вы понимаете под этими словами? тут ведь не сказано, что в таком порядке располагаются записи в файле. Тут сказано, что SELECT * FROM TABLE обязательно выдаст записи в таком порядке.

    Постраничный вывод требуется? Или какая-то другая задача? надо отойти подальше и посмотреть по-другому.
     
  6. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Dmitry_Milk
    http://www.ovaistariq.net/521/understanding-innodb-clustered-indexes/
    А что еще может подразумеваться под "physical order", если не физическое расположение в файле?
     
  7. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Dmitry_Milk
    Он самый. select ... from ... limit page_number * items_per_page, items_per_page # page_number = over9000
     
  8. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Dmitry_Milk

    Ну и еще раз посмотрим сюда: http://dev.mysql.com/doc/refman/5.6/en/innodb-table-and-index.html

    Опять же, "physically in insertion order". Как это понимать, кроме как последовательность в файле?
     
  9. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
  10. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    Хм, с этим поспорить сложнее. Тем не менее, поддержание физического упорядочивания записей в файле - нонсенс. Представьте себя на месте разработчиков СУБД. Представьте, что при наличии первичного ключа требуется вставить в таблицу такую запись, что ключ требует ее положения в самом начале. Что, переписывать весь файл? В какой файловой системе вам известна операция "вставить последовательность байтов в файл с раздвиганием"?

    По поводу страниц, варианты:

    а). уговорить заказчика на то, что ради производительности можно пожертвовать требованием строгого количества записей на странице, что количество записей на странице может изменяться после удаления. Тогда завести дополнительное поле page и сделать по нему дополнительный неуникальный индекс. Естественно, заполнять поле сразу при вставке записи, в любом случае select max(page) from item;select count(*) from item where page=N отработают быстро, если есть индекс.
    Кстати, хороший плюс от этого варианта для SEO - содержимое большинства страниц будет оставаться неизменным. Доастаточно весомый аргумент, если заказчика волнует важность SEO.
    Еще один недостаток, помимо нестрого количества - размер страницы придется выбрать заранее.

    б). Отказаться от автоинкремента ID (но оставив первичным ключом) и поддерживать сплошную нумерацию, чтоб ID строго совпадал с порядковым номером. То есть, если вставка в конец - то
    insert into item(ID,поля...) select max(id)+1,значения-константы... from item
    Если удаление - то
    delete from item where id=N
    update item set id=id-1 where id>N
    Недостаток - долго работает удаление
    Ну и с вставкой в середину еще надо придумать что-то, если таковая требуется. Просто так не получится, из-за уникальности id.
     
  11. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Dmitry_Milk
    1. В энторнетах читал, что такая проблема есть - инсерт в центр ведет к перезаписи фрагманта таблицы. И в этом, имхо, все правильно (см п.3)
    2. У мускуля есть опция raw disk, работа с диском напрямую, минуя ФС (правда хз, поможет ли это в данном случае).
    3. Заглядывать в значение PK - идеологически неверно :) Он должен быть черным ящиком с единственно операцией проверки на равенство, т.е. = / != / in / not in. В энторнетах, опять же, советуют, что если вам захотелось заглядывать в значение PK, то лучше ввести второе вспомогательное поле с UNIQUE индексом, и заглядывать уже в него. Поэтому PK должен быть int autoincrement, и должен рассматриваться как черный ящик - можно только спросить "ты - не ты". PK нужен для идентификации объекта, и обеспечении целостности отношений. Завязывать логику на его внутреннее устройство неправильно, и очень черевато.

    Не выйдет, т.к. я на уговоры не поддаюсь :lol:

    Тогда уж см. п.3 - дополнительное поле. А то этот способ соснет при наличии FK на эти PK. [UPD]: конечно, можно сделать каскадное обновление на update, но это никому не нужный онанизм.
     
  12. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    Вот ведь человек, сам себе гемор создает. Нет чтоб пойти на компромисс со своими желаниями (вполне ли обоснованными?), облегчив себе участь :)

    Насчет дополнительного поля -действительно завести тогда дополнительное поле, order_num, повесить на него уникальный индекс и работать по страницам с ним. FK из других мест на order_num создавать не надо. Если доступны триггера - то вроде все должно гладко получаться.
     
  13. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Dmitry_Milk

    Так...) Пообщался с разработчиком MySQL :) Задача в лоб нерешаема. Пагинация мульярдов данных в любом случае даст o(n), вместо o(1). Нужно какое-то хитрое решение. Написали в google groups, ждем кто что насоветует :)
     
  14. scf

    scf Member

    Публикаций:
    0
    Регистрация:
    12 сен 2005
    Сообщения:
    386
    Вот тебе вариант:
    Если тебе нужен быстрый пейджинг только для сортировки по ПК, то ты можешь опираться на ПК при выборке. Т.е. страницу задает смещение и ПК последнего элемента предыдущей страницы. Тогда получить следующую страницу можно эффективным запросом select * from list where id > $last_id limit 20.

    Что делать с рандомным доступом к страницам? Можно либо на них забить как на редкое явление, либо сделать кеш с парами page_num -> last_id. Разумееется, при обновлении этого списка кеш придется сбрасывать, но операций чтения, как правило, намного больше, чем операций записи.
    Можно сделать "продвинутую технику" - при добавлении нового элемента в список не сбрасывать кеш, а сохранять поправки (например, теперь надо выбирать не 20, а 21 элемент и первый из них игнорировать).

    Еще один вариант - строить индексы для пагинации самостоятельно в памяти - обрабатывать добавления-удаления-изменения списка и хранить в памяти такую структуру, по которой можно извлечь набор ПК элементов для нужной страницы и сортировки. Готовые решения уже наверняка есть, если и не для ПХП, то для явы - однозначно.
     
  15. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    А откуда ноги растут? Может в консерватории что изменить?
     
  16. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Dmitry_Milk
    Эм... Мне кажется что я знаю не все значения слова "консерватория" :)
     
  17. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    http://www.jvanetsky.ru/data/text/t8/konservatoria/

    Стало крылатой фразой со смыслом - "возможно, есть какая-то более общая/глубокая вещь, являющаяся источником проблем по разным направлениям, и надо попробовать поискать и попытаться изменить общую причину".

    Я имел в виду - зачем именно возникла необходимость в "пагинации мульярдов данных"? ПРичем в такой пагинации, когда конкретные записи не будут закреплены за конкретными страницами, а будут плавать со страницы на страницу при удалениях вставках. Такая пагинация на огромном количестве данных не годится ни для быстрого поиска, ни для последовательного просмотра какого-либо участка этого огромного массива данных. Скорее всего именно поэтому для такой задачи (на огромном количестве данных) до сих пор и не придумано нормальной реализации, потому что задача надуманная. Скажем, в MSSQL тоже только TOP() с количеством, но не со смещением.

    Может быть нужна не пагинация, а что-то другое? Или не такая пагинация?
     
  18. semen

    semen New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2004
    Сообщения:
    334
    Адрес:
    Russia
    Может это поможет http://habrahabr.ru/company/badoo/blog/135966/
    Но секунда на 10млн меня бы не устроила, я в свое время решал отказом от mysql.