• WAL в PostgreSQL: 2. Журнал предзаписи

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

      Журнал


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

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

      Чтобы это работало, журнальная запись в обязательном порядке должна попасть на диск до того, как туда попадет измененная страница. Отсюда и название: журнал предзаписи (write-ahead log).

      Если происходит сбой, данные на диске оказываются в рассогласованном состоянии: какие-то страницы были записаны раньше, какие-то — позже. Но остается и журнал, который можно прочитать и выполнить повторно те операции, которые уже были выполнены до сбоя, но результат которых не успел дойти до диска.
      Читать дальше →
      • +21
      • 1.9k
      • 2
    • WAL в PostgreSQL: 1. Буферный кеш

        Предыдущий цикл был посвящен изоляции и многоверсионности PostgreSQL, а сегодня мы начинаем новый — о механизме журналирования (write-ahead logging). Напомню, что материал основан на учебных курсах по администрированию, которые делаем мы с Павлом pluzanov, но не повторяет их дословно и предназначен для вдумчивого чтения и самостоятельного экспериментирования.

        Этот цикл будет состоять из четырех частей:

        • Буферный кеш (эта статья);
        • Журнал предзаписи — как устроен и как используется при восстановлении;
        • Контрольная точка и фоновая запись — зачем нужны и как настраиваются;
        • Настройка журнала — уровни и решаемые задачи, надежность и производительность.

        Зачем нужно журналирование?


        В процессе работы часть данных, с которыми имеет дело СУБД, хранится в оперативной памяти и записывается на диск (или на другой энергонезависимый носитель) отложенным образом. Чем реже это происходит, тем меньше ввод-вывод и тем быстрее работает система.

        Но что произойдет в случае сбоя, например, при выключении электропитания или при ошибке в коде СУБД или операционной системы? Все содержимое оперативной памяти будет потеряно, а останутся лишь данные, записанные на диск (при некоторых видах сбоев может пострадать и диск, но в этом случае поможет лишь резервная копия). В принципе можно организовать ввод-вывод таким образом, чтобы данные на диске всегда поддерживались в согласованном состоянии, но это сложно и не слишком эффективно (насколько я знаю, только Firebird пошел таким путем).

        Обычно же — в том числе и в PostgreSQL — данные, записанные на диск, оказываются несогласованными и при восстановлении после сбоя требуются специальные действия, чтобы согласованность восстановить. Журналирование — тот самый механизм, который делает это возможным.
        Читать дальше →
      • MVCC в PostgreSQL-8. Заморозка

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

          Затем мы рассмотрели разные виды очистки: внутристраничную (вместе с HOT-обновлениями), обычную и автоматическую.

          И добрались до последней темы этого цикла. Сегодня мы поговорим о проблеме переполнения счетчика транзакций (transaction id wraparound) и заморозке.
          Читать дальше →
          • +20
          • 2.4k
          • 2
        • Indexes in PostgreSQL — 10 (Bloom)

          • Translation
          In the previous articles we discussed PostgreSQL indexing engine and the interface of access methods, as well as hash indexes, B-trees, GiST, SP-GiST, GIN, RUM, and BRIN. But we still need to look at Bloom indexes.

          Bloom


          General concept


          A classical Bloom filter is a data structure that enables us to quickly check membership of an element in a set. The filter is highly compact, but allows false positives: it can mistakenly consider an element to be a member of a set (false positive), but it is not permitted to consider an element of a set not to be a member (false negative).

          The filter is an array of $m$ bits (also called a signature) that is initially filled with zeros. $k$ different hash functions are chosen that map any element of the set to $k$ bits of the signature. To add an element to the set, we need to set each of these bits in the signature to one. Consequently, if all the bits corresponding to an element are set to one, the element can be a member of the set, but if at least one bit equals zero, the element is not in the set for sure.

          In the case of a DBMS, we actually have $N$ separate filters built for each index row. As a rule, several fields are included in the index, and it's values of these fields that compose the set of elements for each row.

          By choosing the length of the signature $m$, we can find a trade-off between the index size and the probability of false positives. The application area for Bloom index is large, considerably «wide» tables to be queried using filters on each of the fields. This access method, like BRIN, can be regarded as an accelerator of sequential scan: all the matches found by the index must be rechecked with the table, but there is a chance to avoid considering most of the rows at all.
          Read more →
        • MVCC-7. Автоочистка

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

            Затем мы рассмотрели внутристраничную очистку (и HOT-обновления), обычную очистку, ну а сегодня посмотрим на автоматическую очистку.

            Автоочистка (autovacuum)


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

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

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

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

            Автоматическая очистка — как раз тот самый механизм, который позволяет запускать очистку в зависимости от активности изменений в таблицах.
            Читать дальше →
          • Indexes in PostgreSQL — 9 (BRIN)

            • Translation
            In the previous articles we discussed PostgreSQL indexing engine, the interface of access methods, and the following methods: hash indexes, B-trees, GiST, SP-GiST, GIN, and RUM. The topic of this article is BRIN indexes.

            BRIN


            General concept


            Unlike indexes with which we've already got acquainted, the idea of BRIN is to avoid looking through definitely unsuited rows rather than quickly find the matching ones. This is always an inaccurate index: it does not contain TIDs of table rows at all.

            Simplistically, BRIN works fine for columns where values correlate with their physical location in the table. In other words, if a query without ORDER BY clause returns the column values virtually in the increasing or decreasing order (and there are no indexes on that column).

            This access method was created in scope of Axle, the European project for extremely large analytical databases, with an eye on tables that are several terabyte or dozens of terabytes large. An important feature of BRIN that enables us to create indexes on such tables is a small size and minimal overhead costs of maintenance.

            This works as follows. The table is split into ranges that are several pages large (or several blocks large, which is the same) — hence the name: Block Range Index, BRIN. The index stores summary information on the data in each range. As a rule, this is the minimal and maximal values, but it happens to be different, as shown further. Assume that a query is performed that contains the condition for a column; if the sought values do not get into the interval, the whole range can be skipped; but if they do get, all rows in all blocks will have to be looked through to choose the matching ones among them.

            It will not be a mistake to treat BRIN not as an index, but as an accelerator of sequential scan. We can regard BRIN as an alternative to partitioning if we consider each range as a «virtual» partition.

            Now let's discuss the structure of the index in more detail.
            Read more →
          • MVCC-6. Очистка

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

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

              Обычная очистка (vacuum)


              Что делает очистка


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

              Основная, «обычная» очистка выполняется командой VACUUM и ее мы будем называть просто очисткой (а про автоочистку мы будем говорить отдельно).

              Итак, очистка обрабатывает таблицу полностью. Она вычищает не только ненужные версии строк, но и ссылки на них из всех индексов.

              Обработка происходит параллельно с другой активностью в системе. Таблица и индексы при этом могут использоваться обычным образом и для чтения, и для изменения (однако одновременное выполнение таких команд, как CREATE INDEX, ALTER TABLE и некоторых других будет невозможно).

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

              В процессе работы обновляется и карта свободного пространства, чтобы отразить появившееся свободное места в страницах.
              Читать дальше →
            • Indexes in PostgreSQL — 8 (RUM)

              • Translation
              We have already discussed PostgreSQL indexing engine, the interface of access methods, and main access methods, such as: hash indexes, B-trees, GiST, SP-GiST, and GIN. In this article, we will watch how gin turns into rum.

              RUM


              Although the authors claim that gin is a powerful genie, the theme of drinks has eventually won: next-generation GIN has been called RUM.

              This access method expands the concept that underlies GIN and enables us to perform full-text search even faster. In this series of articles, this is the only method that is not included in a standard PostgreSQL delivery and is an external extension. Several installation options are available for it:

              • Take «yum» or «apt» package from the PGDG repository. For example, if you installed PostgreSQL from «postgresql-10» package, also install «postgresql-10-rum».
              • Build from source code on github and install on your own (the instruction is there as well).
              • Use as a part of Postgres Pro Enterprise (or at least read the documentation from there).

              Limitations of GIN


              What limitations of GIN does RUM enable us to transcend?

              First, «tsvector» data type contains not only lexemes, but also information on their positions inside the document. As we observed last time, GIN index does not store this information. For this reason, operations to search for phrases, which appeared in version 9.6, are supported by GIN index inefficiently and have to access the original data for recheck.

              Second, search systems usually return the results sorted by relevance (whatever that means). We can use ranking functions «ts_rank» and «ts_rank_cd» to this end, but they have to be computed for each row of the result, which is certainly slow.

              To a first approximation, RUM access method can be considered as GIN that additionally stores position information and can return the results in a needed order (like GiST can return nearest neighbors). Let's move step by step.
              Read more →
            • MVCC-5. Внутристраничная очистка и HOT

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

                Сегодня займемся двумя довольно тесно связанными вопросами: внутристраничной очисткой и HOT-обновлениями. Оба механизма можно отнести к разряду оптимизаций; они важны, но в пользовательской документации практически не освещены.

                Внутристраничная очистка при обычных обновлениях


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

                1. Ранее выполненное на этой странице обновление (UPDATE) не обнаружило достаточно места, чтобы разместить новую версию строки на той же странице. Такая ситуация запоминается в заголовке страницы, и в следующий раз страница очищается.
                2. Страница заполнена больше, чем на fillfactor. При этом очистка происходит сразу, не откладывая на следующий раз.
                Читать дальше →
              • Indexes in PostgreSQL — 7 (GIN)

                • Translation
                We have already got acquainted with PostgreSQL indexing engine and the interface of access methods and discussed hash indexes, B-trees, as well as GiST and SP-GiST indexes. And this article will feature GIN index.

                GIN


                «Gin?.. Gin is, it seems, such an American liquor?..»
                «I'm not a drink, oh, inquisitive boy!» again the old man flared up, again he realized himself and again took himself in hand. «I am not a drink, but a powerful and undaunted spirit, and there is no such magic in the world that I would not be able to do.»

                — Lazar Lagin, «Old Khottabych».

                Gin stands for Generalized Inverted Index and should be considered as a genie, not a drink.
                README
                Read more →
              • MVCC-4. Снимки данных

                  Рассмотрев вопросы, связанные с изоляцией, и сделав отступление об организации данных на низком уровне, мы в прошлый раз подробно поговорили о версиях строк и проследили, как изменяется служебная информация в заголовке версии при различных операциях.

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

                  Что такое снимок данных


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

                  Изоляция в PostgreSQL строится на основе снимков данных (snapshot): каждая транзакция работает со своим снимком данных, который «содержит» данные, которые были зафиксированы до момента создания снимка, и не «содержит» еще не зафиксированные на этот момент данные. Мы уже видели, что изоляция при этом получается более строгая, чем требует стандарт, но не лишенная аномалий.
                  Читать дальше →
                • Indexes in PostgreSQL — 6 (SP-GiST)

                  • Translation
                  We've already discussed PostgreSQL indexing engine, the interface of access methods, and three methods: hash index, B-tree, and GiST. In this article, we will describe SP-GiST.

                  SP-GiST


                  First, a few words about this name. The «GiST» part alludes to some similarity with the same-name access method. The similarity does exist: both are generalized search trees that provide a framework for building various access methods.

                  «SP» stands for space partitioning. The space here is often just what we are used to call a space, for example, a two-dimensional plane. But we will see that any search space is meant, that is, actually any value domain.

                  SP-GiST is suitable for structures where the space can be recursively split into non-intersecting areas. This class comprises quadtrees, k-dimensional trees (k-D trees), and radix trees.
                  Read more →
                • MVCC-3. Версии строк

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

                    Заголовок


                    Как мы уже говорили, каждая строка может одновременно присутствовать в базе данных в нескольких версиях. Одну версию от другой надо как-то отличать С этой целью каждая версия имеет две отметки, определяющие «время» действия данной версии (xmin и xmax). В кавычках — потому, что используется не время как таковое, а специальный увеличивающийся счетчик. И этот счетчик — номер транзакции.

                    (Как обычно, на самом деле все сложнее: номер транзакций не может все время увеличиваться из-за ограниченной разрядности счетчика. Но эти детали мы рассмотрим подробно, когда дойдем до заморозки.)
                    Читать дальше →
                  • Indexes in PostgreSQL — 5 (GiST)

                    • Translation
                    In the previous articles, we discussed PostgreSQL indexing engine, the interface of access methods, and two access methods: hash index and B-tree. In this article, we will describe GiST indexes.

                    GiST


                    GiST is an abbreviation of «generalized search tree». This is a balanced search tree, just like «b-tree» discussed earlier.

                    What is the difference? «btree» index is strictly connected to the comparison semantics: support of «greater», «less», and «equal» operators is all it is capable of (but very capable!) However, modern databases store data types for which these operators just make no sense: geodata, text documents, images,…

                    GiST index method comes to our aid for these data types. It permits defining a rule to distribute data of an arbitrary type across a balanced tree and a method to use this representation for access by some operator. For example, GiST index can «accommodate» R-tree for spatial data with support of relative position operators (located on the left, on the right, contains, etc.) or RD-tree for sets with support of intersection or inclusion operators.

                    Thanks to extensibility, a totally new method can be created from scratch in PostgreSQL: to this end, an interface with the indexing engine must be implemented. But this requires premeditation of not only the indexing logic, but also mapping data structures to pages, efficient implementation of locks, and support of a write-ahead log. All this assumes high developer skills and a large human effort. GiST simplifies the task by taking over low-level problems and offering its own interface: several functions pertaining not to techniques, but to the application domain. In this sense, we can regard GiST as a framework for building new access methods.
                    Read more →
                  • MVCC-2. Слои, файлы, страницы

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

                      В этой статье мы посмотрим на то, как данные физически располагаются в файлах и страницах. Это уводит нас в сторону от темы изоляции, но такое отступление необходимо для понимания дальнейшего материала. Нам потребуется разобраться, как устроено хранение данных на низком уровне.

                      Отношения (relations)


                      Если заглянуть внутрь таблиц и индексов, то окажется, что они устроены схожим образом. И то, и другое — объекты базы, которые содержат некоторые данные, состоящие из строк.

                      То, что таблица состоит из строк, не вызывает сомнений; для индекса это менее очевидно. Тем не менее, представьте B-дерево: оно состоит из узлов, которые содержат индексированные значения и ссылки на другие узлы или на табличные строки. Вот эти узлы и можно считать индексными строками — фактически, так оно и есть.

                      На самом деле есть еще некоторое количество объектов, устроенных похожим образом: последовательности (по сути однострочные таблицы), материализованные представления (по сути таблицы, помнящие запрос). А еще есть обычные представления, которые сами по себе не хранят данные, но во всех остальных смыслах похожи на таблицы.

                      Все эти объекты в PostgreSQL называются общим словом отношение (по-английски relation). Слово крайне неудачное, потому что это термин из реляционной теории. Можно провести параллель между отношением и таблицей (представлением), но уж никак не между отношением и индексом. Но так уж сложилось: дают о себе знать академические корни PostgreSQL. Мне думается, что сначала так называли именно таблицы и представления, а остальное наросло со временем.
                      Читать дальше →
                    • Indexes in PostgreSQL — 4 (Btree)

                      • Translation
                      We've already discussed PostgreSQL indexing engine and interface of access methods, as well as hash index, one of access methods. We will now consider B-tree, the most traditional and widely used index. This article is large, so be patient.

                      Btree


                      Structure


                      B-tree index type, implemented as «btree» access method, is suitable for data that can be sorted. In other words, «greater», «greater or equal», «less», «less or equal», and «equal» operators must be defined for the data type. Note that the same data can sometimes be sorted differently, which takes us back to the concept of operator family.
                      Read more →
                    • MVCC-1. Изоляция

                        Привет, Хабр! Этой статьей я начинаю серию циклов (или цикл серий? в общем, задумка грандиозная) о внутреннем устройстве PostgreSQL.

                        Материал будет основан на учебных курсах по администрированию, которые делаем мы с Павлом pluzanov. Смотреть видео не все любят (я точно не люблю), а читать слайды, пусть даже с комментариями, — совсем «не то».

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

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

                        Вещи, о которых пойдет речь, не сильно меняются от версии к версии, но использовать я буду текущий, 11-й «ванильный» PostgreSQL.

                        Первый цикл посвящен вопросам, связанным с изоляцией и многоверсионностью, и план его таков:

                        1. Изоляция, как ее понимают стандарт и PostgreSQL (эта статья);
                        2. Слои, файлы, страницы — что творится на физическом уровне;
                        3. Версии строк, виртуальные и вложенные транзакции;
                        4. Снимки данных и видимость версий строк, горизонт событий;
                        5. Внутристраничная очистка и HOT-обновления;
                        6. Обычная очистка (vacuum);
                        7. Автоматическая очистка (autovacuum);
                        8. Переполнение счетчика транзакций и заморозка.

                        Ну, поехали.
                        Читать дальше →
                      • Indexes in PostgreSQL — 3 (Hash)

                        • Translation
                        The first article described PostgreSQL indexing engine, the second one dealt with the interface of access methods, and now we are ready to discuss specific types of indexes. Let's start with hash index.

                        Hash


                        Structure


                        General theory


                        Plenty of modern programming languages include hash tables as the base data type. On the outside, a hash table looks like a regular array that is indexed with any data type (for example, string) rather than with an integer number. Hash index in PostgreSQL is structured in a similar way. How does this work?

                        As a rule, data types have very large ranges of permissible values: how many different strings can we potentially envisage in a column of type «text»? At the same time, how many different values are actually stored in a text column of some table? Usually, not so many of them.

                        The idea of hashing is to associate a small number (from 0 to N−1, N values in total) with a value of any data type. Association like this is called a hash function. The number obtained can be used as an index of a regular array where references to table rows (TIDs) will be stored. Elements of this array are called hash table buckets — one bucket can store several TIDs if the same indexed value appears in different rows.

                        The more uniformly a hash function distributes source values by buckets, the better it is. But even a good hash function will sometimes produce equal results for different source values — this is called a collision. So, one bucket can store TIDs corresponding to different keys, and therefore, TIDs obtained from the index need to be rechecked.
                        Read more →
                      • Indexes in PostgreSQL — 2

                        • Translation

                        Interface


                        In the first article, we've mentioned that an access method must provide information about itself. Let's look into the structure of the access method interface.

                        Properties


                        All properties of access methods are stored in the «pg_am» table («am» stands for access method). We can also get a list of available methods from this same table:

                        postgres=# select amname from pg_am;
                        
                         amname
                        --------
                         btree
                         hash
                         gist
                         gin
                         spgist
                         brin
                        (6 rows)
                        

                        Although sequential scan can rightfully be referred to access methods, it is not on this list for historical reasons.

                        In PostgreSQL versions 9.5 and lower, each property was represented with a separate field of the «pg_am» table. Starting with version 9.6, properties are queried with special functions and are separated into several layers:

                        • Access method properties — «pg_indexam_has_property»
                        • Properties of a specific index — «pg_index_has_property»
                        • Properties of individual columns of the index — «pg_index_column_has_property»

                        The access method layer and index layer are separated with an eye towards the future: as of now, all indexes based on one access method will always have the same properties.
                        Read more →
                      • Indexes in PostgreSQL — 1

                        • Translation

                        Introduction


                        This series of articles is largely concerned with indexes in PostgreSQL.

                        Any subject can be considered from different perspectives. We will discuss matters that should interest an application developer who uses DBMS: what indexes are available, why there are so many different types of them, and how to use them to speed up queries. The topic can probably be covered in fewer words, but in secrecy we hope for a curious developer, who is also interested in details of the internals, especially since understanding of such details allows you to not only defer to other's judgement, but also make conclusions of your own.

                        Development of new types of indexes is outside the scope. This requires knowledge of the C programming language and pertains to the expertise of a system programmer rather than an application developer. For the same reason we almost won't discuss programming interfaces, but will focus only on what matters for working with ready-to-use indexes.

                        In this article we will discuss the distribution of responsibilities between the general indexing engine related to the DBMS core and individual index access methods, which PostgreSQL enables us to add as extensions. In the next article we will discuss the interface of the access method and critical concepts such as classes and operator families. After that long but necessary introduction we will consider details of the structure and application of different types of indexes: Hash, B-tree, GiST, SP-GiST, GIN and RUM, BRIN, and Bloom.

                        Before we start, I would like to thank Elena Indrupskaya for translating the articles to English.
                        Things have changed a bit since the original publication. My comments on the current state of affairs are indicated like this.
                        Read more →