Email or username:

Password:

Forgot your password?
Top-level
Joinny Hash

Про статистику.

Для оценки селективности и кардинальности используется статистика. Она состоит из n_distinct - число уникальных значений, MCV + MCF - самых популярных значений и их частоты (при больших разбросах там аж гистограмы подрубаются с бакетами), доли null значений, средний размер полей вариативной длины, корелляцию — насколько совпадает физическое расположение строк в таблице с их порядком выдачи из индекса, чем меньше, тем хуже, но в случае с SSD не ясно, _насколько_ хуже, типа проблема ж не в рандомном чтении без префетчей, а в (перепро)чтении лишних страниц, как я понял.

#Статистика - бро твоего планировщика и позволяет автоматически дрочить диск оптимальным способом, если она, конечно, есть и правдива. Статистика собирается полуслучайным образом, типа берём рандомные то ли 300, то ли 30 000 страниц, берём из них рандомные 30 000 (?) строк и АНАЛИЗИРУЕМ. Вакуум такой стоит, аж буферные кэши вытесняются.

Если честно, я порой охреневаю, какой это ебовый овер(?)килл и сколько всего порождает простая запись в табличку. И всё это нужно ведь прочитать, обсчитать, проанализировать перед непосредственным запуском читающего запроса. Вроде у планировщика есть кэш запросов, но ведь в него ещё и попадать надо. А ещё к нему надо обратиться перед планированием, вдруг есть чо, а если нет - это ж ещё одни накладные расходы. Зато SQL простой и декларативный язык, невероятно ведь круто описать что ты хочешь получить, не заморачиваясь вопросом "как".

12 comments
Joinny Hash

Про #кэширование

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

Joinny Hash replied to Joinny

Задумался, а как мне это всё в работе поможет и чот хз. Пока самое эффективное, что я делал, было избавление от insert ignore on conflict + update (разбивка по группам, почти равномерная по модулю от autoincrement integer id) всего что не заигнорилось, заменив это на вставку чанками по 1000 строк с чередованием этих групп и последующим выравниванием недавно вставленного с помощью хитрожопой математики. Благодаря этому получилось для 90% вставляемых строк создать всего одну версию строки, изначально, ещё при вставке выбрав им нужную группу. Дисковое I/O в итоге сократилось на 40% где-то, но помимо этого оно ещё и сгладилось (благодаря вставке чанками), а лаг репликации сократился без лишних апдейтов под той же транзакцией.

Всё это было бы не нужно, если бы все-все-все команды коллег взялись и организовали унифицированный экспорт данных с временными метками изменений, а все-все-все пользователи нашего сервиса писали бы SQL-запросы с учётом этих меток (CDC), чтобы не тянуть каждый раз много данных, и ещё все старые запущенные кампании поправили под это требование. Ух зажили бы.

Задумался, а как мне это всё в работе поможет и чот хз. Пока самое эффективное, что я делал, было избавление от insert ignore on conflict + update (разбивка по группам, почти равномерная по модулю от autoincrement integer id) всего что не заигнорилось, заменив это на вставку чанками по 1000 строк с чередованием этих групп и последующим выравниванием недавно вставленного с помощью хитрожопой математики. Благодаря этому получилось для 90% вставляемых строк создать всего одну версию строки, изначально,...

Joinny Hash replied to Joinny

TIL что NOT EXISTS / EXISTS ... вырождаются в anti/semi-join, типа LEFT JOIN ... WHERE key IS NULL и при наличии индекса на key, всё выглядит не так уж страшно.

А ещё задумался, что при массовой вставке строк с использованием моделей #ORM явно стоит перепроверить, какой #clocksource стоит в системе (и махнуть его на tsm, если не нужна сверхточность). Беда только в том, что в случае managed postgres, прямого доступа к БД нет и проверить это несколько сложновато (хотя, по-любому в psql есть встроенный remote cat и доступ к файловой системе), а потом ещё и с командой, которая постгрес крутит этот вопрос обкашливать, потом выяснится, что clocksource глобальный на всех, что кому-то эта точность нужна и вообще чего суету навёл, нормально ж и стабильно живём.

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

TIL что NOT EXISTS / EXISTS ... вырождаются в anti/semi-join, типа LEFT JOIN ... WHERE key IS NULL и при наличии индекса на key, всё выглядит не так уж страшно.

А ещё задумался, что при массовой вставке строк с использованием моделей #ORM явно стоит перепроверить, какой #clocksource стоит в системе (и махнуть его на tsm, если не нужна сверхточность). Беда только в том, что в случае managed postgres, прямого доступа к БД нет и проверить это несколько сложновато (хотя, по-любому в psql есть встроенный...

Joinny Hash replied to Joinny

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

Joinny Hash replied to Joinny

Хэш-таблицы с шириной строго являющейся степенью двойки - это довольно ловкий ход, позволяющий вместо остатка от деления (дорого) просто дёргать младшие N-битов хэша (дёшево).

Joinny Hash replied to Joinny

И снова к ширине строк таблиц. У меня в обоих проектах есть статусы некоторых объектов. В целом это короткие varchar, обычно до 16 символов длиной (хотя разок уткнулись, семантично ну никак не могли название придумать короче 18 символов). В python это, само собой, #Enum. Понятное дело, хочется там, где можно избавиться от TOAST'а, шоб всё было быстро и весело, все таблицы помещались в одну страницу итд, и вкатить enum'ы и в базе, 4 байта вместо 16 звучит привлекательно, а на 20 000 000 строк ещё привлекательнее, минус 76мб из веса таблицы, да ещё и дополнительная защита от дурака.

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

Бля, я чувствую как во мне просыпается техлид lingualeo.

И снова к ширине строк таблиц. У меня в обоих проектах есть статусы некоторых объектов. В целом это короткие varchar, обычно до 16 символов длиной (хотя разок уткнулись, семантично ну никак не могли название придумать короче 18 символов). В python это, само собой, #Enum. Понятное дело, хочется там, где можно избавиться от TOAST'а, шоб всё было быстро и весело, все таблицы помещались в одну страницу итд, и вкатить enum'ы и в базе, 4 байта вместо 16 звучит привлекательно, а на 20 000 000 строк ещё...

Joinny Hash replied to Joinny

Индексы по enum-полям ещё наверняка обосраться насколько выгодная тема, они ж скорее всего в int4_ops сводятся вместо text_ops.

Mahury replied to Joinny

@strizhechenko о, и енум тоже надо повторить. а то "ну это же просто, как вы это не понимаете", но задачки почему то не решаются, а как прикручивать из объяснений непонятно совсем.

Mahury replied to Joinny

@strizhechenko я про свою учёбу. что енум надо повторить. а то протыкал, но в голове вообще не отложилось.

Joinny Hash replied to Joinny

Чем мне нравится #postgresql - так это тем, что он имеет стандартизированные базовые блоки для операций над данными. Никаких велосипедов! А я их видел много и выгоды от этого кастома было мало (но иногда была, когда использовалась выверенная бенчмарками хэш-функция для специфической структуры данных для большого паттерн-матчинга с кучей доп. правил, которая при росте объёмов выигрывала у компилированной гигарегулярки/схожей реализации в постгре).

Joinny Hash replied to Joinny

Дочитал до реализации Merge Join (странное название, отдаёт тавтологией, но пусть). Кажется вот оно, место где понимание способов сортировки двух массивов имеет хоть какой-то приближённый смысл в этой нашей айтишечке. И это немного перекликается с предыдущим постом треда — одна (ну почти) реализация на способ (нет велосипедов), система даже сама оптимальный способ выберет (спасибо планировщику). Но тогда непонятно, зачем всё это знать рядовому разработчику, если это забота DBA? Рядовому достаточно знать как навесить индекс хотя бы на одно используемое в запросе поле, можно даже за тип индекса не заморачиваться, дефолтный btree 99% потребностей закрывает и,обычно, ничем не хуже hash, + знать что много индексов замедляют вставку. Всё!

Дочитал до реализации Merge Join (странное название, отдаёт тавтологией, но пусть). Кажется вот оно, место где понимание способов сортировки двух массивов имеет хоть какой-то приближённый смысл в этой нашей айтишечке. И это немного перекликается с предыдущим постом треда — одна (ну почти) реализация на способ (нет велосипедов), система даже сама оптимальный способ выберет (спасибо планировщику). Но тогда непонятно, зачем всё это знать рядовому разработчику, если это забота DBA? Рядовому достаточно...

Go Up