PostgreSQL — отличнейшая БД, планировщик которой достаточно интеллектуален.
Однако в ряде случаев мощь интеллекта планировщика вырастает настолько, что он превращается в сверх-интеллект, ну и как всякий сверх-интеллект — объявляет войну своему создателю, а прежде всего начинает с войны с проектом в котором живет.



Образумливать взбунтовавшийся интеллект иногда очень сложно. Поделюсь недавней "находкой" в этой области.


Предположим что у Вас есть вебсайт, на котором есть несколько страничек, которые выводят выборки из БД (в общем-то типовой случай).


Рассмотрим простейший пример. Допустим Вы выбираете список из одной таблицы по пересечению условий AND:


/* Запрос № 1*/
SELECT
    *
FROM
    "table1"
WHERE
        "a" = 1
    AND "b" = 2
    AND "c" = 3

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



CREATE INDEX CONCURRENTLY "table_index1" ON "table1"("a","b","c");

Или даже частичный, если какое-то из полей — константа:



CREATE INDEX CONCURRENTLY "table_index1" ON "table1"("a","b")
    WHERE "c" = 3;

На другой странице у Вас может быть выборка по "c" и "e":


/* Запрос №2 */
SELECT
    *
FROM
    "table1"
WHERE
    "c" = 456
ORDER BY
    "e"
LIMIT
    10

соответственно для другой страницы у Вас будет индекс:



CREATE INDEX CONCURRENTLY "table_index2" ON "table1"("c","e");

Ну и если проект у Вас большой, то вполне вероятно наличие третьего индекса:



CREATE INDEX CONCURRENTLY "table_index3" ON "table1"("a","b","e");

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


В какой-то момент случается факап. Вы заходите pg_dump и видите сотни запросов №1. Вы начинаете исследовать EXPLAIN этого запроса и обнаруживаете нечто вроде следующего:


 Bitmap Heap Scan on table1  (cost=43482.22..54652.36 rows=2806 width=2494) (actual time=2544.520..2602.755 rows=337 loops=1)
   Recheck Cond: ((a = 1) AND (b = 2) AND (c = 3))
   Heap Blocks: exact=86917
   ->  BitmapAnd  (cost=43482.22..43482.22 rows=2806 width=0) (actual time=2516.333..2516.333 rows=0 loops=1)
         ->  Bitmap Index Scan on table_index2  (cost=0.00..7643.88 rows=150331 width=0) (actual time=1791.934..1791.934 rows=3982643 loops=1)
               Index Cond: (c = 3)
         ->  Bitmap Index Scan on table_index3  (cost=0.00..35836.69 rows=1258378 width=0) (actual time=91.829..91.829 rows=377222 loops=1)
               Index Cond: ((a = 1) AND (b = 2))
 Planning time: 2.706 ms
 Execution time: 2612.418 ms
(10 строк)

То есть начиная с какого-то размера таблицы постгрис решил что вместо того чтобы выбрать 337 записей напрямую из предназначенного для этого запроса (возможно частичного!) индекса, он лучше сделает выборку на 4+ млн записей, потом перемножит ее с выборкой на 0.3+ млн записей и вернет Вам результат из 337 записей.


Как бороться с этим?


Гугл выдает много ссылок с аналогичными вопросами, но мало ответов.


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


В общем нам, смигрировавшим за последние пару лет между 9.0 -> 9.1 -> 9.3 -> 9.5, планирующим апгрейд на 10.x, такой способ не очень подходит.


Заставить PostgreSQL использовать Ваш индекс можно и штатным способом. Для этого нам потребуется фейковая функция:


CREATE FUNCTION "selindex" ("name" TEXT)
    RETURNS BOOLEAN
AS $$
    BEGIN
        RETURN TRUE;
    END;
$$
LANGUAGE plpgsql
IMMUTABLE
;

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


Далее перестраиваем индексы примерно таким образом:



CREATE INDEX CONCURRENTLY "table_index1" ON "table1"("a","b","c");
    WHERE "selindex"('table_index1')

Если секция WHERE уже имеется — добавляем в нее соответствующее условие AND:



CREATE INDEX CONCURRENTLY "table_index1" ON "table1"("a","b")
    WHERE "c" = 3 AND "selindex"('table_index1');

Ну а в запросы дописываем AND "selindex"('table_index1'):


SELECT
    *
FROM
    "table1"
WHERE
        "a" = 1
    AND "b" = 2
    AND "c" = 3

    AND "selindex"('table_index1')

PS: Честно говоря я подустал разгребать факапы связанные со "сверхинтеллектом". Отладка, интеграционные тесты, нагрузочные тесты Вам не гарантируют что в перспективе PostgreSQL не откажется от использования того или иного индекса в пользу двух-пяти других. Но пока изящного решения как бороться с этим я не нашел.

Комментарии (69)


  1. Komzpa
    01.12.2017 20:11
    +1

    А почему вы сразу пытаетесь начать с обмана планировщика, вместо того, чтобы сделать ANALYZE проблемной таблице и убедиться, что по ней вовремя ходит autovacuum?


    1. linuxover Автор
      02.12.2017 10:42

      я посчитал ненужным об этом писать в статье. Конечно по таблицам ходит автовакуум. пробовали даже в моменты факапов сделать VACUUM FULL, ANALYZE. Но если постгрис выбрал стратегию которая предполагает другие индексы, то он выбрал.

      там есть ручки влияющие на стратегию, но увы они глобальные.


      1. maksm
        04.12.2017 13:05

        1. У вас здесь налицо искажение cardinality для index scan'ов по (с) и (a, b) на целый порядок — 150331 против 3982643! и 1258378 против 377222. Поэтому имхо лучше начать атаковать эту проблему. Например, если вы делали vacuum full — analyze, увеличитьте выборку по a,b,c при сборе статистики ALTER TABLE SET STATISTICS. Более подробно про оценку кардинальности можно почитать в доке www.postgresql.org/docs/9.5/static/planner-stats-details.html.

        2. При построении составного индекса первый параметр должен быть наиболее селективным в запросах среди остальных. У вас планер таковым считает c (150331 строк против 1258378 для (a, b) от общего числа в table1), поэтому и отказывается использовать индекс по (a,b,c). Это необходимо править через более тщательный сбор статистики.


        1. linuxover Автор
          04.12.2017 13:27

          Например, если вы делали vacuum full — analyze, увеличитьте выборку по a,b,c

          Близкий к реальному у меня пример такой:


          CREATE INDEX ... ON table ("a", "b") WHERE "c" IN (1,2,3);
          
          SELECT
            *
          FROM
             "table"
          WHERE
                 "a" = 1
             AND "b" = 2
             AND "c" IN (1,2,3)

          по c имеется свой индекс (полный), по a,b — свой (полный).


          я сейчас перевел одну реплику в мастер и поэкспериментировал с полем c: SET STATISTICS 1000; и SET STATISTICS 100; и SET STATISTICS 10000; (больше последнего варианта нельзя делать) — нет никакой разницы — запрос строится неверно.


          с полями a,b ща еще эксперименты в процессе — закончу напишу. Но расширить статистику до 10000 — ИМХО мало что даст: в таблице 82 млн записей.


  1. MaZaAa
    01.12.2017 20:15
    -1

    Переходите на MySQL или MariaDB


    1. linuxover Автор
      02.12.2017 10:47
      +1

      проблема MySQL в том что в ней нет нормального SQL. Нет нормальных индексов, соответственно простейшие задачи решаются при помощи плясок с бубном.


      1. MaZaAa
        02.12.2017 12:28

        проблема MySQL в том что в ней нет нормального SQL.

        Поясните пожалуйста.
        Нет нормальных индексов, соответственно простейшие задачи решаются при помощи плясок с бубном.

        И вот тут пожалуйста) И желательно с примером.


        1. linuxover Автор
          02.12.2017 12:49

          Поясните пожалуйста.

          RETURNING, WITH, JSON-операций и индексирования по ним


          И вот тут пожалуйста) И желательно с примером.

          нет GIST, GIN, на которых решаются такие задачи как реализация всяких автокомплитеров и геопоисков. Даже индексированный поиск по регулярным выражениям возможен.


          пример простой: вот скажем пишем автокомплитер-подсказку фраз. причем начинать фразу пользователь может с любого места (классический вариант такого подсказчика — подсказка адресов улиц: пользователь может начинать писать: маршала жукова или просто жукова, ему надо подсказывать варианты содержащие от начала слова до конца фразы — то что совпадает)
          на GIN-индексе такая задача решается написанием одной функции SQL, сплитящей фразы на парты и простановкой этой функции в запрос и в индекс.
          на MySQL без внешних приблуд эта задача не решается от слова вообще (пять-десять BTREE индексов и ручное пересечение по ним мы не рассматриваем)


    1. tgz
      02.12.2017 15:03

      О, да! Там вас ждут совсем другие баги.
      Например везде надо эскейпить underscore (зачем, Карл?), кроме запроса GRANT. Там не надо. Очень удобно. Сразу начинаешь жить полноценной жизнью, не то что в постгресе, где какой то сраный планировщик умнее тебя.


    1. Kirill80
      02.12.2017 17:43
      +1

      В большинстве установок, подавляющем, MySQL это не СУБД вообще. Нет транзакций, нет MVCC, не соблюдается ACID.


      1. MaZaAa
        02.12.2017 17:49

        InooDB, не, не слышал


        1. slonopotamus
          02.12.2017 18:27

          DDL все-равно нетранзакционный.


          1. symbix
            02.12.2017 18:33

            В 8.0 уже таки да. И даже настройки по умолчанию вменяемые. В общем, уже стало похоже на настоящее.


        1. Kirill80
          02.12.2017 18:32

          Inno пользуются немногие. Среди этих немногих найдутся единицы, которые делают это осмысленно.


          1. MaZaAa
            02.12.2017 23:44

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


            1. Kirill80
              03.12.2017 00:59

              MySQL было довольно нелепой в результате попыткой сделать общую платформу для реализаций различных парадигм. Но общего оказалось слишком мало. Да и 99% пользователей Дельфина никогда за пределы MyISAM не выходили и не выходят. И насколько быстро и надёжно, и надёжно ли вообще, всё это барахло работает их никогда не волнует.


              1. MaZaAa
                03.12.2017 11:47

                Так это проблемы в этих людях, а не в дельфине.


  1. novoxudonoser
    01.12.2017 22:21

    готов поспорить что просто кто то не сделал VACUUM ANALYZE или неправильно настроил стоимости операций


    1. linuxover Автор
      02.12.2017 10:45

      Вакуум делается, а стоимости операций — ручки глобальные.

      вот у Вас 50 таблиц размерами от 1Мб до 200Гб.
      вопрос: как правильно настроить стоимости операций чтобы всем таблицам было хорошо?

      Если дадите ссылку на документ, описывающий правильную стратегию — буду благодарен :)


      1. novoxudonoser
        02.12.2017 18:50
        +2

        как я и думал, 2 вариант

        хинт: какая разница сколько у вас таблиц, индексов и что сколько весит, у вас всё лежит на одной и той же дисковой подсистеме (если нет, то наверно вы не знаете что и зачем делаете), поэтому глобальные настойки вам подходят для всех таблиц

        >вопрос: как правильно настроить стоимости операций чтобы всем таблицам было хорошо?
        взять и измерить, записать в конфиг

        без актуальных данных по стоимости операций вся эвристика и оптимизации постгреса летят в трубу, что вы и наблюдаете, пытаться явно задать индекс для запроса в данной ситуации — ламерское решение (кстати и это вы делаете не совсем хорошо, есть специальное расширение, которое позволяет указывать какой план и индексы использовать)


        1. linuxover Автор
          02.12.2017 20:13

          хинт: какая разница сколько у вас таблиц, индексов и что сколько весит, у вас всё лежит на одной и той же дисковой подсистеме (если нет, то наверно вы не знаете что и зачем делаете), поэтому глобальные настойки вам подходят для всех таблиц)

          я только не понимаю как хинт поможет постгрису по другому выбирать индексы.
          вот он посмотрел на статистику индекса 1 и индексов 2 и 3 и выбрал выбирать из индексов 2 и 3, что является ошибкой в 100% случаев. Как помогут ручки про страницы в данном случае?


          1. novoxudonoser
            02.12.2017 22:32

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


            1. linuxover Автор
              04.12.2017 12:40
              +1

              > когда постгрес строит план запроса он смотрит не только лишь на статистику индексов, а пытается найти такой план в котором грубо говоря (потому что учитывается много чего в т.ч. текущая доступность cpu и дисков, гистограммы, и т.д. и это немного по другому работает)

              я это знаю. Я говорю, что выбор плана, предполагающего мерж двух выборок — при наличии точного индекса — является 100%-й ошибкой планирования.


        1. sevikl
          04.12.2017 10:29

          кстати и это вы делаете не совсем хорошо, есть специальное расширение, которое позволяет указывать какой план и индексы использовать

          вместо того, чтобы желчью плеваться, можно бы написать, что за расширение и какие там есть ручки.


  1. Kirill80
    02.12.2017 10:42

    Проблема, видимо, в том, что ваши «таблицы» уже крайне фрагментированы, индексы давно потеряли актуальность, а настройка сбора статистики неадекватна ситуации. И имело бы смысл решать именно эти проблемы, а не заставлять Слона пользоваться кривыми индексами.


    1. linuxover Автор
      02.12.2017 10:54

      первый раз, столкнувшись с такой проблемой я попробовал VACUUM FULL + REINDEX всех индексов и ANALYZE.
      проблеме это не помогло.


      если у вас есть индексы a,b,c и a,b,e + e, то существует ненулевая вероятность что выборке из a,b,c планировщик предпочтёт перемножение выборок a,b,e, и e.
      делает он это на основании статистики с индексов. Причем эта статистика базируется на коэффициентах и (по видимому) не учитывает абсолютные размеры таблиц.


      на первых порах мы это "лечили" путем отказа от одного из индексов: a,b,c или a,b,e. То есть чтобы у планировщика не было из чего выбирать.
      но поскольку проект большой, варианты у него рано или поздно появляются… увы.


      1. linuxover Автор
        02.12.2017 11:04

        Причем эта статистика базируется на коэффициентах и (по видимому) не учитывает абсолютные размеры таблиц.

        если смотреть на данный EXPLAIN то выборка из одного индекса дает коэффициент 4млн/80 млн == 0.05, а вторая выборка дает коэффициент 0.3 / 80 = 0.004. Возможно Pg видя что коэффициенты маленькие (это моё предположение, а не знание) предпочел выборку из этих двух индексов, по аналогии с тем, как он на основании статистики предпочитает иногда сделать seq-scan вместо выборки из индекса


        1. Komzpa
          02.12.2017 14:28

          По умолчанию Postgres считает, что в данных нет внутренних корреляций и селективность одной колонки можно просто перемножить на другую, чтобы получить общую селективность. Нативный путь чинить это — создать статистику на пару колонок, www.postgresql.org/docs/10/static/sql-createstatistics.html


          1. linuxover Автор
            02.12.2017 14:43
            -1

            CREATE STATISTICS появилось только в Pg10, мы пока еще огребаем от нестабильности 9.5, ставить 10 в прод, я считаю, еще где-то год нельзя будет.

            PS: из последних проблем 9.5 — коррапт индексов: потребовалось обновить 9.5.5 на 9.5.7 и перестроить индексы. На инстансах 9.3 таких проблем не встречается.
            ну а на 10 — пока разбивается нос о проблемы 9.5 ехать продом совсем-совсем уж рано.


            1. linuxover Автор
              02.12.2017 14:50

              Не знаю что минусовать. Я ж не выдумываю:


              С индексами разбивали нос об эту проблему:



              очень долго диагностили что не так с 9.5, почему индексы ломаются. пытались собрать тесткейз, а потом очень кстати (уже было собрались делать масштабный откат на 9.3) вышло обновление от коммюнити с фиксом.


              1. Kirill80
                02.12.2017 18:09

                А вы пытались лепить индексы в конкурентном режиме?


                1. linuxover Автор
                  02.12.2017 20:14

                  на больших БД в продакшенах индексы возможно строить только в конкурентном режиме. Если не рассматривать вариант простоя БД конечно.


                  1. Kirill80
                    03.12.2017 01:19

                    Скажем так, в конкурентом режиме, но с одним или многими активными сеансами? Просто у меня застарелая привычка не вводить индексы, когда кто-то ещё пользуется БД. Вот и не знаю — отказываться от неё или пока нет.


      1. Kirill80
        02.12.2017 17:52

        Ну, вы понимает, что это просто светская беседа. Я не знаю вашего опыта, вы — моего. Если брать из арсенала общих «измышлений», то проблемы с индексами всегда проблемы со статистикой, проблемы с фрагментацией «таблиц», которые эти индексы обслуживают, ну и проблемы с пониманием того, что много индексов это совсем не хорошо, а полное сканирование далеко не всегда плохо.
        В случае Слона, на мой взгляд, нужно вдумчиво курить его реализацию MVCC и читать по теме Index Bloat. Адекватная настройка автовакуума хоть и творит чудеса, но в конце концов всё равно без CLUSTER-а не обойтись.


        1. linuxover Автор
          02.12.2017 18:02

          много индексов это совсем не хорошо, а полное сканирование далеко не всегда плохо

          начиная с какого-то размера таблиц полное сканирование становится всегда плохо


          ну а много индексов плохо только при update’ах


          а вот запрос по индексу + filtered секция в explain в моём опыте всегда приводит в итоге к факапу


          1. Kirill80
            02.12.2017 18:19

            Много индексов всегда плохо, на мой взгляд. Полное сканирование, даже очень больших таблиц, зачастую вполне адекватное решение. Потому что на поддержание огромного кол-ва индексов в актуальном состоянии, к тому же на поддержание адекватной статистики, может просто не хватить (никаких мыслимых) ресурсов.


            1. linuxover Автор
              02.12.2017 20:16

              ну много это понятие относительное.


              мы практикуем частичные индексы: вот страница, вот выборка для нее. если есть константные данные — они уезжают из индекса в его условие. в итоге получается скажем в индексе на таблице о 80 млн записей всего 1-2 тыс записей.
              и таких индексов примерно 10-15. Много это?


  1. Kirill80
    02.12.2017 18:41

    А вы не пробовали реализовать ваши ситуацию в какой-нибудь… «нормальной» (я не всерьёз) СУБД? В том же Оракле. Я к тому, что если в нём планировщик что-то подобное нарисует, то стоит задуматься об адекватности модели данных вообще.


    1. linuxover Автор
      02.12.2017 20:28

      у Оракла есть вот и вот, может и постгрису стоит подумать в этом ключе?


      для постгриса увы подобное решение — "внешнее". Закладывать на него прод == брать на себя его поддержку и [или] надеяться


      1. Kirill80
        02.12.2017 22:42

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


        1. Woodroof
          05.12.2017 16:48

          Не всегда.
          Пример: есть пустая таблица с индексом, которая заполняется запросом с подзапросом, ссылающимся (по индексу) на эту же таблицу.
          Статистику можно обновить только между запросами, а вот задать хинтом nested loop по индексу помогает. Точнее, помогало бы, если бы Oracle аж с версии 10 не начал эти хинты посылать лесом.
          В итоге рабочее решение — заполнить таблицу данными, собрать статистику, выбрать нужный план из нескольких и прибить его. После этого можно уже очищать и работать по обычной схеме. Так вот, это куда менее удобно, чем просто хинты.
          Ну и опять же отсутствие хинтов приводит к тому, что необходимость прибивать планы возникает в неожиданных местах в неожиданное время.
          Рабочие хинты да, как правило уменьшили бы скорость выполнения, но при правильном использовании гарантировали бы отсутствие нежданчиков.


          1. linuxover Автор
            05.12.2017 19:46
            +1

            > Рабочие хинты да, как правило уменьшили бы скорость выполнения

            увеличили, как правило.

            человек, пишущий хинт, как правило понимает что делает.
            человек, который не понимает, не пишет хинт.

            вот в примере что в статье: выборка из точного индекса ВСЕГДА будет быстрее мержа двух-трех выборок. И то что Pg предпочитает иногда две выборки — одной — стопроцентная бага планировки.
            Если б можно было написать хинт, то написали и поехали дальше.
            а тут живешь как на пороховой бочке: построил индекс для X, а Y из за этого стало работать медленнее или вообще факапить. Почему? потому что индекс для Y, который несколько лет успешно пользовался, оно взяло и заменило на мерж выборок из индексов X (появился) и Z (раньше был)


            1. qw1
              05.12.2017 19:54

              выборка из точного индекса ВСЕГДА будет быстрее мержа двух-трех выборок. И то что Pg предпочитает иногда две выборки — одной — стопроцентная бага планировки
              Такая же беда у firebird. Наверное, разработчики тестируют на случаях, когда по группе индексов нужно выбрать 20% данных, а не сотню-другую записей из 10 млн.


            1. symbix
              05.12.2017 20:59
              -1

              Если в вашем случае действительно ВСЕГДА, то, может, выставить enable_mergejoin = off?


      1. september669
        04.12.2017 12:42

        Которые оптимизатор оракла весело игнорит


  1. u_story
    02.12.2017 21:07

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

    Можете настройки сервера выложить?
    Какой объём ОЗУ, какое кол-во конектов максимальное, что стоит в настройках следующих:

    • max_connections
    • shared_buffers
    • effective_cache_size
    • work_mem
    • maintenance_work_mem
    • min_wal_size
    • max_wal_size
    • checkpoint_completion_target
    • wal_buffers
    • default_statistics_target


    1. novoxudonoser
      02.12.2017 22:43

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


      1. linuxover Автор
        04.12.2017 12:48

        Ваша настройка не поможет проблеме, ибо речь о seqscan не идет.


    1. linuxover Автор
      04.12.2017 12:47
      +1

      > проще пройтись по таблице с диска

      блин уже который пост читаю это. Проблема не в том что делается секскан. Наоборот seqscan не делается.

      проблема в том что делая выборку по a,b,c есть несколько индексов, содержащих a,b,c:

      1. a,b,c — точно подходящий для выборки
      2. a,b,e — дающий срез по ab
      3. c,d,e — дающий срез по c
      4. итп

      и вот Pg вместо того чтоб взять и выбрать из abc запрос по abc (индекс специально для запроса и строился, в нем даже фильтровать ничего не надо) берет и мержит две выборки по a,b,e и c,d,e


      1. u_story
        04.12.2017 13:46

        Планировщик ошибся.
        Что у вас стоит в default_statistics_target?


        1. linuxover Автор
          04.12.2017 13:50

          50, но простановка в 10000 — ничего не меняет в ошибке. я отвечал выше по этой настройке.

          PS: кстати поле e у меня имеет всего 10 разных вариантов, соответственно от этой «ручки» вообще не зависит.


          1. u_story
            04.12.2017 13:58

            Не заметил сразу.
            А память свободная для постгреса есть? Он не свопится?


            1. linuxover Автор
              04.12.2017 15:00

              когда факап происходит (причина его установлена — неверный выбор индексов) — тогда тоже не свопит: свопа в системе нет :)


    1. linuxover Автор
      04.12.2017 13:46

      Можете настройки сервера выложить?

      $ grep -E 'max_connections|shared_buffers|effective_cache_size|work_mem|maintenance_work_mem|min_wal_size|max_wal_size|checkpoint_completion_target|wal_buffers|default_statistics_target' /etc/postgresql/9.5/main/postgresql.conf|grep -v '^#'|sort 
      
      checkpoint_completion_target = 0.9
      default_statistics_target = 50
      effective_cache_size = 80GB
      maintenance_work_mem = 1GB
      max_connections = 200
      shared_buffers = 27GB
      wal_buffers = 8MB
      work_mem = 768MB


      1. u_story
        04.12.2017 13:56

        del


  1. qw1
    02.12.2017 22:01

    В других СУБД всё куда проще

    SELECT * FROM table1 WITH(INDEX(table_index1)) WHERE c='3'

    SELECT * FROM orders o
    JOIN clients c WITH(INDEX(PK_Client_Id)) ON c.ClientId=o.ClientId
    WHERE o.OrderDate='2017-12-02'

    Неужели в PostgreSQL нет явных хинтов?


    1. Kirill80
      02.12.2017 22:47

      Хинты это даже хуже практики использования курсоров. Хинты в нормальной реляционной MVCC-СУБД не нужны.


      1. linuxover Автор
        04.12.2017 12:51

        если Pg сделал неверный план запроса, то нет способа заставить его сделать верный план (не меняя запрос, не меняя список индексов). Стоимости страниц в данном случае не влияют: я провел эксперимент и поднял стоимость с 4-х до 400 (то есть увеличил стоимость страницы на два порядка) как в плане запроса фигурировал неправильный набор индексов, при имеющемся **точно подходящем, ЧАСТИЧНОМ** индексе, так это и осталось.

        seq_page_cost = 1
        random_page_cost = 400


    1. novoxudonoser
      02.12.2017 23:07

      в постгресе нет их т.к. они не нужны, при правильной настройке и обслуживании 99.99% запросов будут выполнятся самым оптимальным образом, в случае сверх редкого кейса не оптимального использования индексов, можно воспользоваться расширением, при помощи которого можно явно задать план запроса


      1. qw1
        03.12.2017 00:40

        Теоретически, да. А практически часто встречается, что СУБД не может подобрать оптимальный порядок JOIN-ов при объединении нескольких таблиц, если также сильные ограничения есть в WHERE (знать надо предметную область и типичные ситуации с данными).

        И получается разница 200ms против 50ms, оптимизированных вручную. Или считаете, что этим можно пренебречь: не fullscan, и ладно?


        1. Kirill80
          03.12.2017 01:06

          Практически не встречается никогда. Хинты во всех тиражных «платных» СУБД дают, если и дают, только частный результат (в Оракле хинты оставили в качестве традиции, они в нём теперь бесполезны совершенно; в Сиквеле они греют душу неосилянтам, хотя тоже теперь бесполезны; в ДБ их похерили очень давно; в Сибе хинтов нет). Т.е. на момент попыток выполнить конкретный запрос в конкретных временных рамках. Если хинтом удалось в один прекрасный момент получить какой-то сказочный результат, то модель нужно переделывать, она явно неадекватная.


          1. qw1
            03.12.2017 08:42

            select * from Orders where OrderKind=?k and OrderDate between ?s and ?e
            при наличии индексов и по OrderKind, и по OrderDate, при выполнении prepare какой должен быть план?
            Или надо делать новый индекс по двум столбцам (и так каждый раз при появлении нового запроса?).


            1. mayorovp
              03.12.2017 10:01

              Мне как разработчику очень не хотелось бы заниматься расстановкой хинтов каждый раз при появлении нового запроса.


              1. qw1
                03.12.2017 10:23

                Мне бы тоже не хотелось. Однако, реальность бывает такая, что некоторые популярные запросы, поступающие параллельно с 500 рабочих мест, приходится оптимизировать вручную, и от ORM отказываться, лишь бы быстрее работало.


        1. linuxover Автор
          04.12.2017 12:58

          после появления WITH можно контроллировать порядок реальных объединений прописывая в WITH выборки по порядку.


          у Pg кстати болезнь — делать JOIN не в том порядке, особенно это касается LEFT JOIN


          SELECT
            *
          FROM
            "t1"
          LEFT JOIN "t2" ON "t1"."t2_id" = "t2"."id"
          
          WHERE
            "t1"."some" = 'bla' -- обратите внимание, фильтруется только t1

          Глядя на подобный запрос, очевидно, что разворот JOIN'ов по отношению к написанному — будет ошибкой в 100% случаев (был бы простой, не LEFT JOIN — разговор был бы другой), однако очень часто натыкались на подобный разворот и пару лет назад взяли за практику писать WITH в подобных случаях.


  1. Kirill80
    02.12.2017 22:54

    Вообще вы давно наболевшую проблему подняли в очередной раз. По моему глубокому убеждению разработчик не должен всем этим заморачиваться — настройкой СУБД — он должен просто брать и реализовывать свою модель. А все эти напрасные сложности с настройкой самой СУБД просто не должны проявляться в типичных случаях, или если и должны, то явно и однозначно (посмотрел в журнал событий — и всё стало ясно). А ведь до сих пор, откровенно, настройка типичной «большой» СУБД задача редко тривиальная.


    1. linuxover Автор
      04.12.2017 12:59

      По моему глубокому убеждению разработчик не должен всем этим заморачиваться — настройкой СУБД — он должен просто брать и реализовывать свою модель

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


  1. Komzpa
    04.12.2017 10:43

    Вообще, дорога вам лежит в postgresql-hackers, в тред www.postgresql-archive.org/Bitmap-scan-is-undercosted-td5995072.html — там как раз начали обсуждать, почему bitmap and считается более скоростным планом, чем скан по одному индексу.


  1. CherAlexV
    04.12.2017 13:00

    Не совсем в тему, но.
    Мы в проекте переносим всякие выборки из слона в эластику. То есть, слон используется как хранилище, которое много что умеет. Типа работы с json и т.д. И в нем выборка, в основном, идёт по ключам. Все остальное — только в эластике.
    Конечно, благо, пока никаких агрегаций нет. Но, думаю, эластика справится.)