• Почему postgresql выбирает неоптимальный план выполнения для простого JOIN?

    @Miron11
    che_aa,
    Прошло почти две недели.
    За это время я нашел следующмй продукт:
    Расширение для Postgres, позволяющий использовать хинты в запросах базы данных.
    Он настолько популярен и востребован, что поддерживается в том числе облачными сервисами, например, Microsoft Azure
    В запросе, который рассматривается выше наверное подходящий хинт, или несколько будет выглядеть следующим образом:
    /*+Set(max_parallel_workers_per_gather 0)*/
    Дело в том, что запрос, который Вы попросили поправить, из - за сбоя с статистиками срывается в параллельный план. Такие вещи встречаются скорее часто и не только у Postgres, и именно в таких вот случаях использование хинт в запросе считается общепринятым и нормальным подходом. То есть Вы в данном случае просто говорите СУБД "не используйте многопоточный план, потому, что я знаю, что индексы и данные подходят лучше для однопоточного". Как правило с таким вот нежным шлепком СУБД отрабатывают оптимальный план.
    Это настолько просто, что я бы попробовал.
    Для подтверждения что хинт действительный и работает я оставил следующий вопрос разработчикам расширения.
    Итак, как должен выглядеть запрос, чтобы он работал правильно ( если хинт отработает так, как задумано )

    /*+Set(max_parallel_workers_per_gather 0)*/
    SELECT *
    FROM friends_info 
    LEFT JOIN friends_info i 
              ON friends_info.friend_id = i.user_id 
    WHERE friends_info.user_id = 1;
    Написано
  • Почему postgresql выбирает неоптимальный план выполнения для простого JOIN?

    @Miron11
    che_aa,
    это как раз обязанность оптимизатора запрос.
    Количество строк зависит от конкретного сочетания доменов и уникальности данных и никто не может сказать заранее, что даже тот же самый запрос выдаст заранее известное количество строк, поскольку в СУБД принято считать, опираясь на худший ( для планировщика ) из возможных сценариев. И худший сценарий, это изменение количества строк в момент запроса. Очередность выполнения запроса внутри этой "машины" СУБД обычно следующая.
    1 приходит текст запроса
    2 рекурсивная функция дробит запрос на выполнимые блоки
    3 оптимизатор получает запрос в виде такого вот результата рекурсивной функции, которые выглядят, как древо с хорошо проработанными деталями, которые должны быть достаточны для качественной оценки следующих шагов и выбирает как утилиты физического доступа к хранилищу данных должны действовать, как обобщить данные, использовать один потом или несколько и так далее.
    3.а этот шаг опирается на некоторый запас метаданных собранных и хранимых СУБД, который называется "статистики".
    4 каждое физическое действие сопровождается создание "слепка данных"
    --- в этом месте можно было бы вбить клинышек, и посчитать количество строк, но оптимизатор уже отработал. Последующие шаги ведут к помещению данных в память, где СУБД выполняет различные действия, и по мере того, как тот или иной блок задания выполнен СУБД записывает сколько строк собрано.
    ----
    Я могу быть не прав в тонких деталях, но в общем это достаточно близко к тому, что СУБД, как машина, делает для выполнения запроса. Из чего должно быть понятно, что мы, пользователи или администраторы, не имеем доступа, чтобы сказать оптимизатору запроса сколько строк он получит и посчитает в шаге 4.
    Что мы можем дать оптимизатору запроса это
    а. хорошо "подбитые" статистики данных, из которых он может точнее оценить как правильно построить шаги в 4.
    б. не так давно ( года 2 назад ) на рынке был продукт sr_plan, но он не поддерживается, во всяком случае публично, последние 2 года. С его помощью можно было предложить оптимизатору отказаться от 3, и предложить ему готовый план, который Вы считаете правильным, по сути принудить оптимизатор воспользоваться планом, который Вы считаете правильным.
    В совокупности sr_plan + aop + (ещё одна фишка) сейчас рекламируется Green Plum v7, как компетентное решение возможных проблем с оптимизатором запросов, совместимый с Postgres. Если Вас интересует не конкретный вопрос, а общий подход.
    -----
    Оценка количества строк это функция блока СУБД, который называется ( по - английски ) "cardinality estimator". Как раз в последние 10 лет многие СУБД озаботились их обновлением, возможно есть смысл погуглить на интернете такое обновление / альтернативное решение для Postgres. Опять же, если Вы ищете общий подход к похожему классу задач.
    ----
    В конкретном случае мне кажется необходимо сделать шаг назад и решить, что делать.
    В словах Melkij есть смысл. Ради эксперимента можно попробовать
    1 убрать индекс с user_id и оставить только unique constraint.
    2 удалить и заново создать unique constraint
    и посмотреть, если это решило проблему с не оптимальным запросом. Теоретически это, как "должно работать". Но у меня так на моем компьютере работает нет. Давайте так, если "теоретически правильный вариант" у Вас работает, все здорово, если нет, то вернемся и продолжим искать обходной путь.
    Если Вы прочитали мое решение, которое на моем компьютере работает "да", то необходимо добавить unique index. Он хорошо работает и сам себя поддерживает. В принципе, это и есть то, что решает проблему между 3 и 3.а выше ( и то, что Вы спросили, подладить верные статистики ).
    И хотя он выглядит как нахлобучка, в практике, если "нахлобучка" решает реальную проблему ей рады и это не считается чем - то чрезмерным.
    ----
    Вы не спросили здесь в комментарии, но этот вопрос есть в оригинальном запросе, это строка с
    set enable_bitmapscan = off;
    Для сужения области воздействия этой команды необходимо использовать опцию "local"
    set local enable_bitmapscan = off;
    и тогда эта опция оказывает действие только на сессию, в которой выполняется запрос. Но если выбирать между индексом нахлобучкой и SET опцией, я бы предпочел индекс.
    ----
    Почему я не предлагаю заменить unique constraint на unique index.
    При всей схожести, это разные вещи.
    Constraint, это инструмент имеющий область воздействия базы, эти объекты заносятся в каталоги имеющие область видимости базы данных и их внесение и удаление проходит под другим уровнем внутри машины СУБД. Индексы же, это инструмент работы с одним, отдельно взятым родством.

    должен поправить себя.
    В Postgres индексы так же имеют область видимости всей базы данных. Поэтому с индекс с уникальным именем может быть только один во всей базе данных.
    Что возвращает меня к моему оригинальному скрипту :)
    Видимо в процессе эксперимента с данными, которые не вошли в скрипт я убрал unique constraint и внес индекс. После чего план запроса стал оптимальным.
    Твк, как скрипт приведен, он выдаст сообщение
    ---
    NOTICE: relation "friends_info_user_id_friend_id_key" already exists, skipping
    CREATE INDEX
    ---
    Видно так искал проблему, что не уследил за каждым своим шагом и сделал ошибку.

    По этой причине есть некоторые выражения, используемые в запросах, на разрешение конфликтов на дубликаты, где используются имя constraint, в случае, если хотят использовать индекс ( надеясь, что оптимизатор запросов его вовремя идентифицирует, иначе это может стать очень - очень длинной операцией, которая принесет проблемы с функционалом ), то необходимо создать выражение с списком колонок. То есть удаление unique constraint и его замена на уникальный индекс потребует изучить базу и, возможно, изменить код некоторых процедур или скриптов. Поскольку прибавление индекса не смертельно, я бы добавил индекс, решив очевидную проблему, а затем продолжил в итеративном ключе его улучшение.
    Это план, на случай, если "теоретический подход" не работает.
    ---
    Ну и наконец, есть вариант с исследованием причин, по которым индекс, поддерживающий unique constraint ведет себя таким вот образом.
    Ваш наводящий вопрос подсказывает, что, возможно, этот индекс живет с регулировками статистик требующими близкого ознакомления. Стоит приглядеться к нему поближе. Это наиболее вероятный сценарий, посмотрю, если что - то можно найти пользуясь каталогом базы и утилитами.
    Но не исключено, что надо выполнить запрос, поставив сам Прогресс СУБД под построчный отладчик :). В этом случае должен признать, учитывая, что у Вас на руках есть решение с индексом нахлобучкой ( 15 минут ), и таким вот глубоким изучением проблемы в несколько часов на моей стороне, время взывать к совести :))) урр-мяу.
    Удачи.
    Написано
  • Почему postgresql выбирает неоптимальный план выполнения для простого JOIN?

    @Miron11
    Скрипт для GraphDB помог выявить в Age свои проблемы с оптимальностью запроса на таблице имеющей 1 миллион записей для user_id+friend_id.
    Ранее я работал с базой, где количество строк ограничивалось 200 тысяч, и там все выполнялось намного быстрее.
    С Вашего разрешения поделюсь скриптом с инженерами и вернусь, когда запросы в vertvex + edge имеющие 1 миллион записей и более выполняются побыстрее :)
    Я имею ввиду довольно быстрый поиск решения, который либо выявит ошибку в существующей поддержке индексов, либо есть наработки по альтернативному индексированию полей. Так или иначе, 1 неделя выглядит как срок.
    Вот сам скрипт:
    -- CREATE EXTENSION IF NOT EXISTS age;
    LOAD 'age';
    SET search_path = ag_catalog, public, "$user";
    
    do
    $RUN$
    DECLARE
        ag_map_var    agtype;
        ag_output_var agtype;
    BEGIN
        LOAD 'age';
        SET search_path = ag_catalog, public, "$user";
    
        IF NOT (ag_catalog.graph_exists('friends_graph')) THEN
            PERFORM create_graph('friends_graph');
        END IF;
    
        IF NOT (ag_catalog.vertex_exists('friends_graph', 'user')) THEN
            PERFORM create_vlabel('friends_graph', 'user');
        END IF;
    
    	CREATE INDEX IF NOT EXISTS ix_friends_graph_user ON "friends_graph"."user"
            (ag_catalog.agtype_access_operator(properties, '"user_id"'::agtype));
    
        IF NOT (ag_catalog.edge_exists('friends_graph', 'friend_of')) THEN
            PERFORM create_elabel('friends_graph', 'friend_of');
        END IF;
    
        RAISE NOTICE 'Starting creating vertex users %', clock_timestamp();
    
        FOR ag_map_var
        IN  SELECT ag_catalog.datum_to_agtype_map(fi.*)
    	    FROM public.friends_info fi
        LOOP
            SELECT INTO ag_output_var
                  *
            FROM cypher('friends_graph', $$
               CREATE (u:user {
            	      user_id: $user_id
                	, unix_time_check: $unix_time_check
               })
               RETURN u
            $$, ag_map_var) AS (e agtype);
    
        END LOOP;
    
        RAISE NOTICE 'Finished creating vertex users %', clock_timestamp();
    	
    END;
    $RUN$;
    
    
    do
    $RUN$
    DECLARE
        ag_map_var    agtype;
        ag_output_var agtype;
    
    	progress_ind_var bigint = 0;
    BEGIN
        LOAD 'age';
        SET search_path = ag_catalog, public, "$user";
    
        SELECT INTO ag_output_var
              *
        FROM cypher('friends_graph', $$
           MATCH (f:user), (u:user)
                   WHERE f.user_id = $friend_id
    			   AND u.user_id = $user_id
    			   AND NOT EXISTS ((f)<-[:friend_of]-(u))
                       CREATE (f)<-[e:friend_of]-(u)
               RETURN e
            $$, ag_map_var) AS (e agtype);
    
    		IF (0 = (progress_ind_var % 1000)) THEN
    		    RAISE NOTICE 'Created % users, %', progress_ind_var, clock_timestamp();
    		END IF;
    		
            progress_ind_var := (progress_ind_var + 1);
    
        END LOOP;
    END;
    $RUN$;
    
    SELECT *
    FROM cypher('friends_graph', $$
       MATCH (u:user)
       RETURN u
    $$) AS (u agtype);

    его самой интересной частью являются запросы между $$, это язык Cypher, GraphDB диалект.
    Он довольно прост и элегантен. Что - то здоровское подобрать, если есть время.
    Написано
  • Почему postgresql выбирает неоптимальный план выполнения для простого JOIN?

    @Miron11
    Melkij,
    --
    Вероятно несколько пользователей занимают значительную часть таблицы и это сбивает оценку селективности.
    --
    В планах, представленных пользователем, есть исчерпывающая информация по количеству выбранных линий. Их ровно 88 в плане, который выглядит не оптимальным.
    88 линий трудно предположить, как нечто похожее на то, что Вы предположили "Вероятно несколько пользователей занимают значительную часть таблицы и это сбивает оценку селективности."
    Наверное стоит читать план запроса предложенный пользователем несколько более внимательно.
    В моем ответе есть скрипт смоделировать данные и изучить поведение индексов, если у Вас есть желание.
    --- @che_aa
    По моему опыту, без этих индексов цена select`а взлетает в небо
    ---
    Теоретически можно иметь много различных мнений и аргументов, в данном случае Ваш практический опыт такой же, как и мой.
    И потом индексы не равны
    Первый вообще-то не индекс, а unique constraint. Тот факт, что он поддерживается индексом, это техническая деталь, поскольку его забота оградить родство от повторения домена user_id + friend_id.
    Учитывая тот факт, что данный unique constraint не поддерживает оптимальный план и у Вас и у меня, я склоняюсь к мнению, что насколько такая конструкция поддерживает оптимальность выборки требует отдельного исследования. Хотя, ради эксперимента его конечно стоит уничтожить и создать заново. Чтобы ушли все сомнения.
    Кроме того индекс
    "ix_friends_user_id" btree (user_id)
    не несет в себе поле "friend_id", и для тех запросов, где выборка не интересуется полем "friend_id" оптимизатор запросов предпочтет выборку по индексу наиболее соответствующему запросу.
    И, поскольку, как заметил предыдущий комментатор, данные могут быть распределены между пользователями не одинаково, индекс с ключом user_id имеет право на жизнь. Особенно, если есть foreign_key, который на него опирается, допустим, из таблицы "user_info".
    Написано
  • Почему postgresql выбирает неоптимальный план выполнения для простого JOIN?

    @Miron11
    Melkij,
    прежде, чем задавать вопросы, попытайтесь прочитать что написано внимательнее.
    Задача не высказать мнение, а найти способ выполнить запрос с планом имеющим оптимальные параметры.
    Мой ответ выполняет это требование пользователя.
    Возможно у Вас есть лучшее решение, но я его в Вашем ответе не нашел.
    Попытайтесь собрать свои комментарии вокруг того, что запросил пользователь.
    Если у Вас есть вопрос, который Вы хотите прояснить, в частности, почему необходимо делать то или иное действие, будьте добры, откройте отдельный вопрос, и если я найду, что у меня есть знания, чтобы на него ответить, с удовольствием Вам помогу.
    Но это должен быть отдельный вопрос.
    ---
    Отдельной строкой,
    в принципе, вопрос который Вы задали достаточно прост, и на него приятно ответить, чтобы дополнить оригинальный вопрос пользователя. Но таких дополнительных вопросов в деле оптимизации запроса в СУБД может быть очень - очень много.
    По этой причине, убедительная просьба, для дополнительных вопросов открывать свой, отдельный вопрос для ответа, который позволит сфокусироваться на том элементе оптимизации запроса, который Вас интересует.
    В частности ( если я правильно понял Ваше направление мысли )
    Вопрос № 1 почему необходимо создать индекс, когда есть UNIQUE CONSTRAINT на тех же полях.
    Для того, чтобы ответить на Ваш вопрос наверное придется смотреть код СУБД.
    ---
    Вопрос № 2 почему в индексы необходимо включать поля в части INCLUDE
    Это общий вопрос, по которому будут даны адреса соответствующей документации и небольшой текст с запросами языка SQL с примерами и пояснениями
    ---
    Проблема в том, что каждый из этих вопросов, если Вы посмотрите документацию, имеет развернутые пояснения с множеством документов, и включать эти общие вопросы по СУБД и оптимизации запросов в конкретный запрос пользователя не поможет пользователю улучшить запрос, а отвечающему найти ответ для пользователя.
    Написано
  • Как определить тип функции для шаблона?

    @Miron11
    Thanks, I did find your approach the most convincing and, if you will, correct, with spirit and aim of templates. However, the questions I have asked were the same, that stopped me from writing the code itself in the fist place, and instead share some general directions to approach in one of the earlier comments.
    Will review what's left to imagination to fill the picture at some later point.
    If anyone gets ahead of me, mucha gracias.
  • Как определить тип функции для шаблона?

    @Miron11
    Will this code, potentially, need "toString()" override ( for T)?
  • Как определить тип функции для шаблона?

    @Miron11
    Wataru,
    ну, так ил иначе, options have to be limited to specific type.
    Вопрос, когда эти самые options get limited.
    И поскольку и C и T заданы в декларации, мы не отступаем от наших, если позволите, принципов :)
  • Как определить тип функции для шаблона?

    @Miron11
    yatanai,
    I can understand the motivation
    But what I can't understand is type inference logic
    In theory template flow is from declaration ( when type resolution is imminent ) -> through parameters ( in diamond brackets <> ) -> hydrate types of the library. In which case it makes sense to add parameter to implementation class, in your example, Test, like this Test<T> and extend template with T type for the property data type.
    May be this is "too" obvious and I am getting in a carefully setup trap, but I am curious to find out :)
    Correct me if I am wrong, but I have a feeling that you would like to infuse template with something like "inversion - of - control - template - reflection" function, where library would get informed, at the time of implementation of what type it is dealing with to implement some ( potentially conditional ) logic. But this isn't really an template purpose, even if something like this ever featured. Usually template code, if type inference gets involved, encapsulates its "common" application to some very high level abstraction, such as constructor and destructor, which can handle pretty much type independent beings, while passing actual implementation to some "implementation" feature, which is resolved through type, once again, designated at declaration time.
    If my understanding is correct, your intent is to reverse the acyclic graph of compiled tree and get/extend a handler, to get an origin point for injection of some functionality, which could ( if it worked ) get into any assignment / variable reading operation, either as a feature, or, as instrumentation.
  • Redis vs Elasticsearch vs Sphinx для хранения данных и поиск по ним?

    @Miron11
    И как это сочетается с требованием №1
    Хранение данных каталога с быстрым доступом
    Solr это индекс текста для поиска NLP или похожего, так же на Lucene.
    Хранилище данных + быстрый доступ + индексация и быстрый поиск по контексту, это три противоречивых требования, которые пока не выполнил ни один производитель ПО в полном объеме.
    Вопрос, которым из 7 требований озвученных в вопросе можно пожертвовать? :)))
  • Может ли кэшироваться данные в EF Core?

    @Miron11
    Василий Банников,
    И что там на счёт Task.Run?
    Вполне возможно, что у тебя одновременно отрабатывает и сохранение и выгрузка.

    One can easily test it by running same request 2nd time, after a bit of wait, when all database queries finished. In this second attempt he should see the change.
    I was assuming the author of the question tried a few times, before coming to conclusion that the issue isn't concurrent access, but caching ( buffering ) of data on the front - end.
    Besides, EF buffering ( if you dig into theoretical side of platform ) is an expression of IQueriable, which has caching defined as part of standard implementation, as opposed to IEnumerable, which requires hard reading of values from the media for each request.
  • Может ли кэшироваться данные в EF Core?

    @Miron11
    Василий Банников,
    for security reasons I cannot install russian locale on this computer.
    So, I have to enter russian text using Yandex keyboard, a mouse click per letter, which isn't really working for most of posts. But in some cases, depending on context, I feel obligated to answer in Russian, particularly when I feel vis-a-vis isn't really tolerant of english.
    I try to be nice to people I speak to. But I have to think for myself, too.
    In the end I am trying to help.
    If you feel english isn't working, I will stop.
    If english is acceptable ( I will learn from your future answer, just under supposition ) I try to write very concise english, very clear, as close to impeccable as I can, in the hope it would teach my vis-a-vis ways to read and express things in English.
    In the end, pity it is, for the moment we don't have Russian speaking code. All of our code is English to some extent. So, I don't feel I am veering too far from the mark.
    It would be very nice, speaking of the future, to have Russian environment, including cyrillic based vocabulary and semantical programming interface, end - to - end.
    I'd work on it, if we start project like that.
    If I may suggest, this post is grossly off top for the question asked. So, if moderators could move it to something like "off top answers" and provide link, for the future discussion, it would be very nice.
  • Может ли кэшироваться данные в EF Core?

    @Miron11
    Here is the answer
    There is a reference to this blog post
    Entity Framework Cache Busting
    2016-02-19
    The DbContext in Entity Framework 6 automatically caches data that it retrieves from your database. This is useful, but sometimes data changes outside your context (perhaps by another user) and you end up with stale data.

    In my personal experience all of the above, plus whatever is in the blog, is a poor's man patch from водка и подорожник.
    One should strive, as much as possible, to extend DBContext with methods and call in those stored procedures, where concurrent access to data is accounted for using
    1 Query design
    2 Transaction Isolation Level
    3 Tuning options at all levels, including, query, database and server instance
    Entities solve a lot of troubles, so, you don't have to code every POCO. The framework simplifies for developer data layer and magically extracts it from some foreign "database engine" into the application, as an opaque architecture, from which one can design entire application.
    But beyond it, data access via some scaffolding based code, is utter nonsense.
    I just can't believe, that someone with Russian Computer Science or Math University diploma would ask questions like this.
    It's unbelievable!
  • Как в Selenium Java обойти UnhandledAlertException?

    @Miron11
    Olga_AQA,

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

    Спасибо
  • Как установить сертификат ssl для .net api?

    @Miron11
    Not sure regarding 6.0, but earlier WebAPI had some limits on mutual SSL authentication. In case it comes useful: https://blogs.iis.net/danielvl/how-to-allow-self-s...
  • Как в Selenium Java обойти UnhandledAlertException?

    @Miron11
    Olga_AQA,
    не поделитесь деталями по AutoIt?
    я может сам и дотёпаю, но многим здесь на форуме без вашей помощи не справиться, уж поделитесь, если будет время и настроение. очень рад, что у Вас все хорошо!
  • Как обстоят дела с модулями C++20 и Inline оптимизацией?

    @Miron11
    I apologies, I wasn't prepare to this degree of scrutiny of words.
    It really is somewhat deciphering document you have shared.
    The document, its scope, is directed at terms and meaning of each term.
    What @Dollarum is proposing slices across domain of this word stream.
    He is basically applying the verbatim of the document to specific use case.
    This use case isn't in the document.
    So, we have two choices.
    1. send request to United Nations ( as a figure of speech ) and wait until they down unto us with "строго однозначное понимание термина", which in human words is a rare commodity, and really, never ever happens, no pun intended, but a succinct summary of practice.
    or
    2. try to turn to "many meaning" and slowly narrow those down and agree on what we find out, following our own humble devices. Or, in simple terms, a constructive way to understand what we are dealing with.
    It would be really - really nice if you proposed your own interpretation. I personally would be delighted to read it :)
    ---
    This isn't first discussion of this sort I am seeing. The previous one has occurred about 15 years ago, and since time has passed enough to pull even official seal of secrecy, I will make a call on this informal FOYA and describe similar research in meaning of words.
    And engineer working at Microsoft fielded a question, regarding handling hardware exception in the event of communication link failure between an application and an database.
    He had received an extremely qualified response from a Russian engineer, apparently working at Intel, with reference to specific hardware register and error code number, to look for, when handling such an exception, that was apparently causing significant trouble to product's driver ( Microsoft SQL Server ).
    This discussion ensued at expert-exchange.com site, similarly facilitating exchange of knowledge between engineers.
    In the end the engineer, who received support never ever acknowledged significant help and contribution of his vis-a-vis, however, it had significant impact on product quality improvement.
    My proposal, we should be better than that engineer :)
  • Как решить 22 задание ЕГЭ по информатике?

    @Miron11
    рискуя навлечь на себя гнев многих людей, хотел напомнить, что подобные форумы, и вообще любые формы решений экзаменационных задач, кроме как личным старанием ученика, являются нарушением морально этического кода.
    Пожалуйста, старайтесь решать сами.
  • Как обстоят дела с модулями C++20 и Inline оптимизацией?

    @Miron11
    Евгений Шатунов,
    по идее в документе, который Вы указали, как авторитетный источник, его суть сводится к тому, что inline это translation unit bound semantical flag.
    То есть похоже, что Dolarun прав.
    Поскольку ( если мыслить логически ) линкер не имеет права на inference, только транслятор.
    Не путать с распараллеливанием процедур. Матричные оптимизации, развертка, происходит в памяти процессора ( что так же возможно требует переосмысления, ибо не очевидно ).