О! А я заметил, что если у меня партиционирование по времени, и я делаю запрос по интервалу времени, который целиком укладывается в одну партицию, то намного быстрее запрос идет, если делать его напрямую к ней, а не к сводной таблице.
Не помню уже — то ли разы разницы, а то ли и порядок.
Запросы там на несколько сотен символов, непростые. Выводить из этого минимальный повторяющийся случай будет слишком долго, у меня нет времени. Очень много работы.
Мне кажется, суть вполне понятна — вот есть запрос, в нем есть условие по полю intarray, всё работает по индексу (индексу именно по Intarray, именно это поле наиболее селективно), чудесно. Добавляем ещё одно условие на это поле — и кровь кишки :-)
Я не пытаюсь сказать, что тут гарантированно виноват PostgreSQL, я безусловно не понимаю ситуацию до конца, чтобы вот прямо багрепорт пойти написать — но это безусловно было очень странно.
Я не знаю лучшего способа, чем выключить в конфиге стратегию, которая ему нравится, перезапустить сервер и сравнить cost запросов.
Вообще, если планировщик явно тупит, то первое, что стоит сделать — это ANALYZE таблицы. Ещё можно попробовать сбросить статистику планировщика и дать ей накопиться снова — мало ли, характер нагрузки на сервер изменился.
Но перед сбросом — подумать, почему PostgreSQL может себя так вести.
Например, у вас маленькая таблица, она в кеше — её проще перебрать так, действительно.
Или у вас неселективный запрос — тогда тоже проще считать всё и отфильтровать; потому что если постгрес сначала пойдет в индекс — ему после получения списка строк из индекса придется за каждой пойти в таблицу и перепроверить — получается больше работы с диском, чем подряд.
Это связано с MVCC; в 9.2 появились index-only запросы, но и у них, кажется, есть свои ограничения, я не помню деталей.
По-моему, вот этой причиной — проще считать таблицу, чем делать кучу seek-ов по диску — объясняется большинство случаев выбора Seq Scan постгресом.
Может быть ещё забавное. У меня вот недавно была ситуация — я получал несколько сотен тысяч айдишников в скрипте и по 1000 за раз брал их из базы. Получал таймауты (2 секунды у нас в коде прикручено). Отсортировал айдишники перед тем, как брать из них порции — таймауты ушли :-) И это понятно.
По поводу три — первая мысль «он нарисовал круг», да. Но можно же хитрее — «он сделал жест, который приложение понимает как нарисовать круг вокруг текущей активной точки» :-)
Массовый дроп может быть плох, если у Вас после этого приложение ломанется в базу так активно, что ляжет :)
Речь только о массовом, теги так вряд ли используются, с ними должно быть ок.
Менять префиксы не самый лучший способ.
Ну, я все это рассказываю в применении к memcached. Там старые данные рано или поздно уплывут, риска переполнения кеша вообще нет.
То есть, например, когда в таблице пользователей появляется новое поле, надо все старые кеши инвалидировать?
Ну или просто по какой-то причине мы знаем, что у нас половина кеша сломана?
Искать по префиксу в мемкеше вроде и не надо, просто меняем префикс в коде и оп, все пошли за свежим кешем.
А если вот так разом сбрасывать опасно — можно написать скриптик, который будет проходить по базе и дропать кеши постепенно, опционально — вызывать ф-цию, которая проставит правильный кеш.
Вообще ключи я привык формировать так: NAMESPACE-PREFIX-id, где NAMESPACE отделяет друг от друга виды сущностей, а PREFIX отдельные кеши внутри сущности.
А именно про теги в мемкеше я нагуглил, но у меня сразу возникли вопросы «а как это работает, когда у меня ключи на десяти серверах» и «а где они хранят информацию о тегах, ведь memcached не персистентное хранилище и может выкинуть из кеша что угодно когда угодно».
Надо немного конкретизировать, чтобы мы оставались в общем контексте.
Я так понимаю, вы имеете в виду ситуацию, когда, например, у нас есть некоторая сводная страница, где надо показать не одну сущность — пользователь, сайт, etc — а некую их группу или группы. Вероятно, в каком-то определенном порядке.
При этом информация о каждом отдельном элементе каждой группы в кеше есть.
Как бы я поступал в таком случае.
Во-первых, надо определиться, допустимы ли тут устаревшие данные и если да, то насколько.
Для начала, пусть допустимы.
Тогда я бы получал список групп для показа, в нужном порядке, и кешировал бы эти группы — в виде id сущностей. На какое-то время, определяемое задачей. Скажем, 5 минут.
Потом на каждый запрос я бы обращался к этому кешу, собирал все id, и забирал отдельный кеш каждой сущности.
В memcached есть get_multi, кстати — экономия на накладных расходах существенная. Да и запросы в базу для cache miss тоже можно группировать, так что, конечно, групповые lookup-ы надо для каждой сущности реализовать.
Рендерил страницу, отдавал.
Теперь пусть неактуальность недопустима.
Снова развилка — как часто потенциально может обновляться информация на сводной странице?
Скажем, редко. Это маловероятная ситуация, сейчас станет понятно, почему.
Тогда можно дропать сводный кеш каждый раз, когда происходит что-то, что может повлиять на сводную страницу — то есть на каждый апдейт каждой сущности, которая теоретически может там присутствовать.
Скорее всего, так ключ будет дропаться чуть ли не чаще, чем запрашиваться.
Поэтому переходим к случаю часто обновляемой сводной страницы, которая всегда должна быть актуальна.
А тут рецепт прост — учиться получать список id сущностей, которые вы будете показывать — быстро.
Либо это грамотно огранизованная таблица/таблицы в РСУБД с правильными индексами, либо это, скажем, Redis или что-то ещё, работающее в памяти.
В итоге мы все равно получим список id сущностей, которые будут лукапиться вторым шагом.
В общем, весь этот очень длинный текст сводится к тому, что не надо дважды одну информацию класть в два места, если вам важна когерентность этих мест. Просто вот не надо и всё. Придумывайте по-другому.
Если у вас одновременно работают десять тысяч пользователей, то, наверно, популярность вашего приложения позволит вам и (целый!) гигабайт выделить на кеш.
Ну, кеш на сервере с одной стороны это хорошо, близко.
С другой стороны, разумно сделать кластер memcached-ов на этих 10 серверах и не париться.
Если становятся важны единицы миллисекунд — то ок, можно перенести кеши на каждый сервер в отдельности; но тогда надо придумывать способы, чтобы пользователь всегда приходил на один сервер.
Просто проверка пары дат.
Красиво будет разобраться, конечно, но оно работает — а более животрепещущих проблем навалом.
Но спасибо, я буду в будущем меньше опасаться партиций.
Не помню уже — то ли разы разницы, а то ли и порядок.
Запросы там на несколько сотен символов, непростые. Выводить из этого минимальный повторяющийся случай будет слишком долго, у меня нет времени. Очень много работы.
Мне кажется, суть вполне понятна — вот есть запрос, в нем есть условие по полю intarray, всё работает по индексу (индексу именно по Intarray, именно это поле наиболее селективно), чудесно. Добавляем ещё одно условие на это поле — и кровь кишки :-)
Я не пытаюсь сказать, что тут гарантированно виноват PostgreSQL, я безусловно не понимаю ситуацию до конца, чтобы вот прямо багрепорт пойти написать — но это безусловно было очень странно.
Вообще, если планировщик явно тупит, то первое, что стоит сделать — это ANALYZE таблицы. Ещё можно попробовать сбросить статистику планировщика и дать ей накопиться снова — мало ли, характер нагрузки на сервер изменился.
Но перед сбросом — подумать, почему PostgreSQL может себя так вести.
Например, у вас маленькая таблица, она в кеше — её проще перебрать так, действительно.
Или у вас неселективный запрос — тогда тоже проще считать всё и отфильтровать; потому что если постгрес сначала пойдет в индекс — ему после получения списка строк из индекса придется за каждой пойти в таблицу и перепроверить — получается больше работы с диском, чем подряд.
Это связано с MVCC; в 9.2 появились index-only запросы, но и у них, кажется, есть свои ограничения, я не помню деталей.
По-моему, вот этой причиной — проще считать таблицу, чем делать кучу seek-ов по диску — объясняется большинство случаев выбора Seq Scan постгресом.
Может быть ещё забавное. У меня вот недавно была ситуация — я получал несколько сотен тысяч айдишников в скрипте и по 1000 за раз брал их из базы. Получал таймауты (2 секунды у нас в коде прикручено). Отсортировал айдишники перед тем, как брать из них порции — таймауты ушли :-) И это понятно.
И это прекрасно, очень приятно с Postgres-ом работать.
То есть любой ключ может пропасть раньше времени наступления expire-а.
Одна из нод кластера может ребутнуться, наконец.
Речь только о массовом, теги так вряд ли используются, с ними должно быть ок.
Ну, я все это рассказываю в применении к memcached. Там старые данные рано или поздно уплывут, риска переполнения кеша вообще нет.
Ну или просто по какой-то причине мы знаем, что у нас половина кеша сломана?
Искать по префиксу в мемкеше вроде и не надо, просто меняем префикс в коде и оп, все пошли за свежим кешем.
А если вот так разом сбрасывать опасно — можно написать скриптик, который будет проходить по базе и дропать кеши постепенно, опционально — вызывать ф-цию, которая проставит правильный кеш.
Вообще ключи я привык формировать так:
NAMESPACE-PREFIX-id
, где NAMESPACE отделяет друг от друга виды сущностей, а PREFIX отдельные кеши внутри сущности.А именно про теги в мемкеше я нагуглил, но у меня сразу возникли вопросы «а как это работает, когда у меня ключи на десяти серверах» и «а где они хранят информацию о тегах, ведь memcached не персистентное хранилище и может выкинуть из кеша что угодно когда угодно».
Я так понимаю, вы имеете в виду ситуацию, когда, например, у нас есть некоторая сводная страница, где надо показать не одну сущность — пользователь, сайт, etc — а некую их группу или группы. Вероятно, в каком-то определенном порядке.
При этом информация о каждом отдельном элементе каждой группы в кеше есть.
Как бы я поступал в таком случае.
Во-первых, надо определиться, допустимы ли тут устаревшие данные и если да, то насколько.
Для начала, пусть допустимы.
Тогда я бы получал список групп для показа, в нужном порядке, и кешировал бы эти группы — в виде id сущностей. На какое-то время, определяемое задачей. Скажем, 5 минут.
Потом на каждый запрос я бы обращался к этому кешу, собирал все id, и забирал отдельный кеш каждой сущности.
В memcached есть get_multi, кстати — экономия на накладных расходах существенная. Да и запросы в базу для cache miss тоже можно группировать, так что, конечно, групповые lookup-ы надо для каждой сущности реализовать.
Рендерил страницу, отдавал.
Теперь пусть неактуальность недопустима.
Снова развилка — как часто потенциально может обновляться информация на сводной странице?
Скажем, редко. Это маловероятная ситуация, сейчас станет понятно, почему.
Тогда можно дропать сводный кеш каждый раз, когда происходит что-то, что может повлиять на сводную страницу — то есть на каждый апдейт каждой сущности, которая теоретически может там присутствовать.
Скорее всего, так ключ будет дропаться чуть ли не чаще, чем запрашиваться.
Поэтому переходим к случаю часто обновляемой сводной страницы, которая всегда должна быть актуальна.
А тут рецепт прост — учиться получать список id сущностей, которые вы будете показывать — быстро.
Либо это грамотно огранизованная таблица/таблицы в РСУБД с правильными индексами, либо это, скажем, Redis или что-то ещё, работающее в памяти.
В итоге мы все равно получим список id сущностей, которые будут лукапиться вторым шагом.
В общем, весь этот очень длинный текст сводится к тому, что не надо дважды одну информацию класть в два места, если вам важна когерентность этих мест. Просто вот не надо и всё. Придумывайте по-другому.
В случае memcached «добавить expire» выливается в добавление "
, $expire
".Если в вашей системе кеширования нет встроенного expire, то я надеюсь, что там есть хотя бы вытеснение старых данных новыми — сойдет и это.
С другой стороны, разумно сделать кластер memcached-ов на этих 10 серверах и не париться.
Если становятся важны единицы миллисекунд — то ок, можно перенести кеши на каждый сервер в отдельности; но тогда надо придумывать способы, чтобы пользователь всегда приходил на один сервер.