В публикациях на хабре, чаще всего самое интересное в коментариях. Вот и в моем прошлом посте "Сгенерировать web интерфейс из БД или объектной модели не стало проще даже 10 лет спустя" я собрал джекпот из SharePoint, Vaadin и нескольких библиотек либо устаревших, либо поддерживаемых "одним автором".

Что я пытался объяснить читателям, что на этапе проверки идей не нужна дополнительная сложность в виде фреймворков, серверной инфраструктуры только ради этой самой инфраструктуры и нового языка программирования/платформы. Именно поэтому мне не нужны сейчас Yii, Symfony и Laravel, не подойдет ADF. На любое обучение тратится драгоценное время и поэтому надо понимать зачем тратить время на что-то, что потом возможно навсегда придется забыть через неделю-другую игры с прототипом.

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

При этом все это время я не программировал backend совсем. Все что есть в приложении - это набор статических файлов на http сервере jetty, и PostgREST для превращения базы данных PostgreSQL в сервис Open API и несколько новых хранимых функций в базе данных для интерфейса.

И jetty по хорошему здесь был бы лишний, если бы не...

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

Чем я могу поделиться и что будет полезно вам

Что из моего опыта разработки "на коленке" может быть полезно бэкэндерам, кто хочет бытро показать табличные данные из PostgreSQL?

Пересмотрев несколько библиотек на GitHub для отображения таблиц я остановился для себя на Tabulator которая из коробки умеет отображать таблицы с сортировкой, фильтрацией и разбивкой на страницы, автоматически распознает типы колонок, с отличной документацией, доступной для понимания не фронтэндером. А также позволяет быстро делать прототип, разрешая настраивать типы отображения поля даже в режиме автоопределения.

Попытка программировать на Java 21

За несколько минут( не учитывая время чтения документации) я набросал прототип который позволяет просмотреть все таблицы в базе данных. И сделал это я на Java еще не выпущенной версии 21 и потом еще минут десять пытался его запустить, разыскивая где в новой для меня версии IntelliJ Idea CE и в старой версии maven место куда же добавить эксперементальный параметр JDK enablePreview.

Честное слово, это привычка на автоматизме разрабатывать с заделом на будущее, чтобы когда-нибудь разбирать метаданные PostgreSQL и комментарии к объектам БД, все то что не экспортирует PostgREST. Пошел заварить чай и подумал доберусь ли я до этих метаданных постгреса и когда, что все это тоже лишнее, не нужен тут overengineering и надо переписать на HTML+JavaScript.

Ощущения что с Servlet API 5.0 разработка стала проще и без web.xml, а String Templates из JDK 21 сделают код более читаемым и в простых случаях избавляют от надобности подключения внешнего движка шаблонизации Apache Velocity:

Для любопытных под спойлером новый Java сервлет который больше не понадобится:
package com.github.com.github.isuhorukov.postgrest.crud;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;


import static java.lang.StringTemplate.STR;

@WebServlet("/table")
public class TableServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String endpoint = request.getParameter("endpoint");
        if(endpoint==null || endpoint.isBlank()){
            List<String> postgRestAvailablePaths = getPostgRestAvailablePaths();
            String options = postgRestAvailablePaths.stream().map(path ->
                    STR. """
                                    <option value="\{path}">\{path}</option>
                                """).collect(Collectors.joining("\n"));
            response.getWriter().append(STR."""
                    <!DOCTYPE html>
                    <html xmlns="http://www.w3.org/1999/html">
                        <head>
                        </head>
                        <body>
                            <form method="GET" action="/table">
                                <select name="endpoint">\{options}</select>
                                <input type="submit" value="Show">
                            </form>
                        </body>
                    </html>
                    """);
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        response.getWriter().append(STR."""
            <!DOCTYPE html>
            <html xmlns="http://www.w3.org/1999/html">
                <head>
                    <link href="https://unpkg.com/tabulator-tables@5.5.2/dist/css/tabulator.min.css" rel="stylesheet">
                    <script type="text/javascript" src="https://unpkg.com/tabulator-tables@5.5.2/dist/js/tabulator.min.js"></script>
                </head>
                <body>
                    <div id="table-result"></div>
                    <script>
                        var table = new Tabulator("#table-result", {
                            ajaxURL: '\{endpoint}',
                            pagination:true,
                            paginationMode:"remote",
                            dataSendParams:{
                                "size" : "limit" //rename page request parameter to "limit"
                             } ,
                             ajaxURLGenerator:function(url, config, params){
                                let sortParam = '';
                                if (params.sort && params.sort.length > 0) {
                                  sortParam = '&order='+params.sort.map(sorter => `${sorter.field}.${sorter.dir}`).join(',');
                                }
                                return url + (url.includes("?")?"&":"?") + "limit=" + params.limit+"&offset=" + params.limit*(params.page-1) + sortParam;
                             },
                             ajaxResponse: function (url, params, response) {
                                document.querySelector(".tabulator-footer .tabulator-page[data-page='last']").style.display = "none";
                                return {
                                  last_page: 10000000000,
                                  data: response
                                };
                              },
                              autoColumns:true,
                              paginationSize: 35,
                              sortMode: 'remote',
                              filterMode:'remote',
                              ajaxSorting: true,
                              ajaxFiltering: true
                        });
                    </script>
                </body>
            </html>""");
    }

    List<String> getPostgRestAvailablePaths() {
        String postgRestUrl = System.getenv("postgrest_url");
        if(postgRestUrl==null || postgRestUrl.isBlank()){
            throw new IllegalArgumentException("Please provide PostgREST / endpoint in environment variable: postgrest_url");
        }
        List<String> paths = new ArrayList<>();
        try (HttpClient httpClient = HttpClient.newHttpClient()){
            HttpRequest request = HttpRequest.newBuilder().uri(URI.create(postgRestUrl)).build();

            HttpResponse<String> postgRestResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            if (postgRestResponse.statusCode() == 200) {
                try {
                    HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

                    ObjectMapper objectMapper = new ObjectMapper();

                    if (response.statusCode() == 200) {
                        String responseBody = response.body();
                        JsonNode openApiSpec = objectMapper.readTree(responseBody);

                        JsonNode pathsNode = openApiSpec.get("paths");

                        if (pathsNode != null && pathsNode.isObject()) {
                            for (Iterator<String> it = pathsNode.fieldNames(); it.hasNext(); ) {
                                String path = it.next();
                               if (!"/".equals(path) && !path.startsWith("/rpc")) {
                                    paths.add(postgRestUrl + path);
                                }
                             }
                        } else {
                            throw new RuntimeException("No table paths found.");
                        }
                    } else {
                        throw new RuntimeException("PostgREST failed with status code: " + response.statusCode());
                    }
                } catch (IOException | InterruptedException e) {
                    throw new RuntimeException(e);
                }
            } else {
                throw new RuntimeException("PostgREST request failed with status code: " + postgRestResponse.statusCode());
            }
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
        Collections.sort(paths);
        return paths;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.igor-suhorukov</groupId>
    <artifactId>postgrest-crud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty.toolchain</groupId>
            <artifactId>jetty-jakarta-servlet-api</artifactId>
            <version>5.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <enablePreview>true</enablePreview>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>11.0.15</version>
                <configuration>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <connectors>
                        <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                            <port>8080</port>
                            <maxIdleTime>60000</maxIdleTime>
                        </connector>
                    </connectors>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Итак, я сделал все то что не хотел, добавил лишний компонент между PostgREST и браузером. Смело выбрасываем этот код/подход и движимся дальше!

Делаем все то же, но без сервера и только в браузере

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

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

Ну что ж, сделаем сами. Его параметр ajaxURL, который подгружает данные с сервера не сработает с PostgREST потому что в ответе он ожидает JSON объект c last_page - номером последней страницы и массивом data, который содержит сами данные.

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

К слову, API умеет возвращать сколько строк в результате по нескольким алгоритмам. Нужно лишь установить HTTP Header:

  • Prefer: count=exact

  • Prefer: count=planned

  • Prefer: count=estimated

Результат можно извлечь из Content-Range в Response после "/". Я же поступил гораздо проще - убрал кнопку перехода на последнюю страницу.

Продолжаю разбирать проблемы: Tabulator передает в параметры номер страницы, а мне это нужно перевести их в limit/offset для API. Делаю это в функции ajaxURLGenerator и с помощью конфигурации dataSendParams.

Преобразование в нужный для виджета формат данных с сервера делаю на клиенте в функции для ajaxResponse.

Итого, у моей страницы есть параметры:

  • postgrest_url указывает на PostgREST API. Если не указать, то по умолчанию будет "стучаться" на localhost:3000

  • endpoint - таблица или вьюшка в постгресе. Если ее не указать, то код запросит все пути endpoint из PostgREST, отфильтрует функции и метаданные, и выдаст вам форму с выбором endpoint. Укажите в поле select какую таблицу хотите посмотреть, после "Нажми на кнопку и получишь результат".

Для работы нужена ваша база данных, сервер для API PostgREST который "смотрит" на эту базу, любой веб сервер куда вы можете положить эту страничку ( запускаю с помощьюmvn jetty:run). Если бы браузеры не боролись с cross site scripting и безопасностью ваших локальных данных, как это было раньше, то и веб сервер не был бы нужен для раздачи статики.

Код PostgREST-Tabulator

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
    <link href="https://unpkg.com/tabulator-tables@5.5.2/dist/css/tabulator.min.css" rel="stylesheet">
    <script type="text/javascript" src="https://unpkg.com/tabulator-tables@5.5.2/dist/js/tabulator.min.js"></script>
</head>
<body>
<form id="endpoint_selector" method="GET" action="tables.html" style="display:none">
    <select id="endpoint" name="endpoint"></select>
    <input id="postgrest_url" name="postgrest_url" type="hidden" value="">
    <input type="submit" value="Show">
</form>
<div id="table-result"></div>
<script>

    const params = new URLSearchParams(window.location.search);
        
    const postgRestUrl = params.has('postgrest_url')?params.get('postgrest_url') :'http://localhost:3000';
    
    async function fetchAndExtractPaths(postgRestUrl) {
        try {
            const response = await fetch(postgRestUrl);
            if (!response.ok) {
                throw new Error(`Failed to fetch data from ${postgRestUrl}`);
            }
            const data = await response.json();
            const paths = [];
            if (data && typeof data.paths==='object') {
                    paths.push(...Object.keys(data.paths).filter(key => !key.startsWith('/rpc') && key!='/' ));
            }
            return paths;
        } catch (error) {
            alert('Error:', error);
            return [];
        }
    }
    

    if (params.has('endpoint')) {
        const endpoint = params.get('endpoint');
        var table = new Tabulator("#table-result", {
            ajaxURL: postgRestUrl+endpoint,
            pagination:true,
            paginationMode:"remote",
            dataSendParams:{
                "size" : "limit"
             } ,
             ajaxURLGenerator:function(url, config, params){
                let sortParam = '';
                if (params.sort && params.sort.length > 0) {
                  sortParam = '&order='+params.sort.map(sorter => `${sorter.field}.${sorter.dir}`).join(',');
                }
                return url + (url.includes("?")?"&":"?") + "limit=" + params.limit+"&offset=" + params.limit*(params.page-1) + sortParam;
             },
             ajaxResponse: function (url, params, response) {
                document.querySelector(".tabulator-footer .tabulator-page[data-page='last']").style.display = "none";
                return {
                  last_page: 10000000000,
                  data: response
                };
              },
              autoColumns:true,
              paginationSize: 35,
              sortMode: 'remote',
              filterMode:'remote',
              ajaxSorting: true,
              ajaxFiltering: true
        });
    } else {
        fetchAndExtractPaths(postgRestUrl)
        .then(paths => {
                const container = document.getElementById('endpoint');
                paths.forEach(path => {
                    const optionElement = document.createElement('option');
                    optionElement.setAttribute('option',path);
                    optionElement.textContent = path;
                    container.appendChild(optionElement);
                });
                document.getElementById("endpoint_selector").style.display = 'block';
                document.getElementById("postgrest_url").value = postgRestUrl;
        });
    }
</script>
</body>
</html>

PostgREST я запускаю локально в Docker:

docker run --name postgrest --net=host -e PGRST_DB_URI="postgres://USER:PASSWORD@127.0.0.1:5432/DATABASE" -e PGRST_DB_ANON_ROLE="YOUR_ROLE" postgrest/postgrest:v11.2.0

PostgreSQL с данными у меня тоже запущен в Docker образе, как я уже рассказывал на хабре.

Выбор endpoint (таблицы)
Выбор endpoint (таблицы)
И данные для этой таблицы
И данные для этой таблицы

Что еще можно сделать, пока не вышло универсально - в зависимости от типов данных менять отображение в колонках.

Хардкод, такой хардкод для отображения рейтинга и ссылок в таблице! Потому кода не будет в публикации
Хардкод, такой хардкод для отображения рейтинга и ссылок в таблице! Потому кода не будет в публикации

Результат

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

И самое главное, что в обсуждении о производительности мы фокусируемся на самих бизнес данных и методах доступа базы к ним, а не на тюнинге GC, оптимизации сериализации в приложении, не о кластеризации серверов и даже не о мониторинге кешей Hazelcast, Ingnite, Coherence, оркестрации микросервисов и много еще чего. То есть мы ближе к решению задачи, чем к зоопарку технологий в MVP. На начальном этапе достаточно грамотной работы с базой данных. И откладываем самые важные архитектурные решения и трудозатраты на разработку на потом, когда проверим что в решении задачи есть ценность.

У меня нет планов на создание или на развитие этого как low code/no code платформы. Свою задачу я решаю быстро. Возможно кому-нибудь еще, кто использует PostgreSQL и не фронтэнд разработчик, этот опыт поможет для проверки идеи или создания "админки" к базе для пользователей внутри компании.

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


  1. poxvuibr
    30.08.2023 23:45
    +1

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

    А как вам это удалось? Ведь судя по джаваскрипту используется конструкция limit ... offset . Неужели работает? Или там одна строка занимает мегабайты? Но на скриншоте строки маленькие.

    Интересно, узнать объём данных поточнее. Сколько там сотен гигабайт получилось? И сколько это строк. И на каком диске лежат данные?

    Индексы, наверное, по всем колонкам, да?


    1. igor_suhorukov Автор
      30.08.2023 23:45
      +1

      А как вам это удалось?

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

      Ведь судя по джаваскрипту используется конструкция limit ... offset .

      В простом случае да. Если хочется быстрее, то конечно надо переходить на ctid BETWEEN '($,0)'::tid AND '($,0)'::tid

      Неужели работает?

      Работает!

      Интересно, узнать объём данных поточнее. Сколько там сотен гигабайт
      получилось? И сколько это строк. И на каком диске лежат данные?

      Диск NVME:

      sudo smartctl -a /dev/nvme0
      
      Model Number:                       Samsung SSD 970 EVO Plus 2TB
      PCI Vendor/Subsystem ID:            0x144d
      IEEE OUI Identifier:                0x002538
      Total NVM Capacity:                 2 000 398 934 016 [2,00 TB]

      Общий объем базы данных 587GB. Число строк в самой большой таблице:

      select count(*) from ways;
         count   
      -----------
       871619356
      (1 row)
      Распределение объема данных по партициям
               Table          | Rows  |     Total Size     |     Table Size     |  Index(es) Size  |     TOAST Size     
      ------------------------+-------+--------------------+--------------------+------------------+--------------------
       *** TOTAL ***          | ~1B   | 587 GB (100.00%)   | 501 GB (100.00%)   | 54 GB (100.00%)  | 32 GB (100.00%)
                              |       |                    |                    |                  | 
       ways_051               | ~21M  | 12 GB (2.02%)      | 11 GB (2.14%)      | 1070 MB (1.94%)  | 73 MB (0.22%)
       relation_members       | ~113M | 9523 MB (1.58%)    | 6136 MB (1.20%)    | 3386 MB (6.15%)  | 8192 bytes (0.00%)
       ways_043               | ~14M  | 8102 MB (1.35%)    | 7334 MB (1.43%)    | 737 MB (1.34%)   | 31 MB (0.09%)
       ways_052               | ~14M  | 8013 MB (1.33%)    | 7200 MB (1.40%)    | 729 MB (1.32%)   | 84 MB (0.26%)
       ways_002               | ~17M  | 7923 MB (1.32%)    | 7013 MB (1.37%)    | 870 MB (1.58%)   | 40 MB (0.12%)
       ways_001               | ~15M  | 7360 MB (1.22%)    | 6485 MB (1.26%)    | 788 MB (1.43%)   | 87 MB (0.26%)
       ways_081               | ~14M  | 6963 MB (1.16%)    | 6127 MB (1.19%)    | 716 MB (1.30%)   | 120 MB (0.37%)
       ways_004               | ~13M  | 6886 MB (1.15%)    | 6108 MB (1.19%)    | 691 MB (1.25%)   | 87 MB (0.27%)
       ways_029               | ~11M  | 6824 MB (1.14%)    | 6197 MB (1.21%)    | 567 MB (1.03%)   | 60 MB (0.18%)
       ways_047               | ~11M  | 6750 MB (1.12%)    | 6101 MB (1.19%)    | 577 MB (1.05%)   | 71 MB (0.22%)
       ways_093               | ~13M  | 6522 MB (1.09%)    | 5749 MB (1.12%)    | 644 MB (1.17%)   | 128 MB (0.39%)
       ways_095               | ~13M  | 6451 MB (1.07%)    | 5689 MB (1.11%)    | 671 MB (1.22%)   | 91 MB (0.28%)
       ways_090               | ~12M  | 6405 MB (1.07%)    | 5708 MB (1.11%)    | 620 MB (1.13%)   | 77 MB (0.23%)
       ways_003               | ~12M  | 6401 MB (1.07%)    | 5553 MB (1.08%)    | 641 MB (1.16%)   | 206 MB (0.63%)
       multipolygon_32767     | ~187k | 6366 MB (1.06%)    | 338 MB (0.07%)     | 27 MB (0.05%)    | 6002 MB (18.32%)
       ways_005               | ~12M  | 6317 MB (1.05%)    | 5590 MB (1.09%)    | 601 MB (1.09%)   | 126 MB (0.38%)
       ways_092               | ~12M  | 6227 MB (1.04%)    | 5511 MB (1.07%)    | 610 MB (1.11%)   | 107 MB (0.33%)
       ways_074               | ~11M  | 6208 MB (1.03%)    | 5507 MB (1.07%)    | 587 MB (1.07%)   | 114 MB (0.35%)
       ways_091               | ~11M  | 6158 MB (1.02%)    | 5425 MB (1.06%)    | 578 MB (1.05%)   | 156 MB (0.47%)
       ways_098               | ~12M  | 6154 MB (1.02%)    | 5421 MB (1.06%)    | 595 MB (1.08%)   | 138 MB (0.42%)
       ways_089               | ~11M  | 6128 MB (1.02%)    | 5418 MB (1.06%)    | 576 MB (1.05%)   | 134 MB (0.41%)
       ways_080               | ~11M  | 5973 MB (0.99%)    | 5206 MB (1.01%)    | 549 MB (1.00%)   | 217 MB (0.66%)
       ways_097               | ~11M  | 5940 MB (0.99%)    | 5298 MB (1.03%)    | 547 MB (0.99%)   | 95 MB (0.29%)
       ways_045               | ~11M  | 5877 MB (0.98%)    | 5261 MB (1.03%)    | 546 MB (0.99%)   | 70 MB (0.21%)
       ways_053               | ~9M   | 5810 MB (0.97%)    | 5289 MB (1.03%)    | 487 MB (0.88%)   | 34 MB (0.10%)
       ways_019               | ~10M  | 5794 MB (0.96%)    | 5124 MB (1.00%)    | 517 MB (0.94%)   | 153 MB (0.47%)
       ways_006               | ~10M  | 5774 MB (0.96%)    | 5033 MB (0.98%)    | 510 MB (0.93%)   | 231 MB (0.70%)
       ways_046               | ~10M  | 5702 MB (0.95%)    | 5094 MB (0.99%)    | 505 MB (0.92%)   | 103 MB (0.31%)
       ways_042               | ~10M  | 5683 MB (0.95%)    | 5075 MB (0.99%)    | 496 MB (0.90%)   | 112 MB (0.34%)
       ways_049               | ~9M   | 5663 MB (0.94%)    | 5118 MB (1.00%)    | 456 MB (0.83%)   | 89 MB (0.27%)
       ways_037               | ~9M   | 5634 MB (0.94%)    | 5040 MB (0.98%)    | 447 MB (0.81%)   | 147 MB (0.45%)
       ways_054               | ~9M   | 5627 MB (0.94%)    | 4856 MB (0.95%)    | 485 MB (0.88%)   | 286 MB (0.87%)
       ways_017               | ~10M  | 5605 MB (0.93%)    | 4950 MB (0.97%)    | 507 MB (0.92%)   | 148 MB (0.45%)
       ways_075               | ~9M   | 5587 MB (0.93%)    | 4940 MB (0.96%)    | 488 MB (0.89%)   | 159 MB (0.49%)
       ways_026               | ~9M   | 5585 MB (0.93%)    | 5064 MB (0.99%)    | 482 MB (0.88%)   | 39 MB (0.12%)
       ways_048               | ~9M   | 5584 MB (0.93%)    | 4985 MB (0.97%)    | 484 MB (0.88%)   | 114 MB (0.35%)
       ways_082               | ~10M  | 5548 MB (0.92%)    | 4890 MB (0.95%)    | 500 MB (0.91%)   | 158 MB (0.48%)
       ways_000               | ~9M   | 5460 MB (0.91%)    | 4750 MB (0.93%)    | 461 MB (0.84%)   | 248 MB (0.76%)
       ways_094               | ~10M  | 5457 MB (0.91%)    | 4737 MB (0.92%)    | 493 MB (0.89%)   | 227 MB (0.69%)
       ways_033               | ~8M   | 5454 MB (0.91%)    | 4808 MB (0.94%)    | 403 MB (0.73%)   | 243 MB (0.74%)
       ways_087               | ~9M   | 5445 MB (0.91%)    | 4808 MB (0.94%)    | 463 MB (0.84%)   | 173 MB (0.53%)
       ways_010               | ~9M   | 5357 MB (0.89%)    | 4774 MB (0.93%)    | 440 MB (0.80%)   | 143 MB (0.44%)
       ways_040               | ~9M   | 5348 MB (0.89%)    | 4722 MB (0.92%)    | 467 MB (0.85%)   | 159 MB (0.48%)
       ways_088               | ~9M   | 5262 MB (0.88%)    | 4662 MB (0.91%)    | 455 MB (0.83%)   | 145 MB (0.44%)
       ways_060               | ~8M   | 5243 MB (0.87%)    | 4673 MB (0.91%)    | 420 MB (0.76%)   | 150 MB (0.46%)
       ways_096               | ~10M  | 5207 MB (0.87%)    | 4613 MB (0.90%)    | 495 MB (0.90%)   | 99 MB (0.30%)
       ways_024               | ~8M   | 5205 MB (0.87%)    | 4722 MB (0.92%)    | 431 MB (0.78%)   | 52 MB (0.16%)
       ways_071               | ~9M   | 5198 MB (0.87%)    | 4625 MB (0.90%)    | 469 MB (0.85%)   | 104 MB (0.32%)
       ways_038               | ~9M   | 5193 MB (0.86%)    | 4613 MB (0.90%)    | 463 MB (0.84%)   | 117 MB (0.36%)
       ways_057               | ~8M   | 5161 MB (0.86%)    | 4619 MB (0.90%)    | 415 MB (0.75%)   | 126 MB (0.39%)
       ways_070               | ~8M   | 5124 MB (0.85%)    | 4432 MB (0.86%)    | 424 MB (0.77%)   | 268 MB (0.82%)
       ways_031               | ~8M   | 5124 MB (0.85%)    | 4618 MB (0.90%)    | 435 MB (0.79%)   | 71 MB (0.22%)
       ways_013               | ~8M   | 5098 MB (0.85%)    | 4487 MB (0.87%)    | 396 MB (0.72%)   | 215 MB (0.66%)
       ways_073               | ~9M   | 5091 MB (0.85%)    | 4529 MB (0.88%)    | 479 MB (0.87%)   | 84 MB (0.25%)
       ways_079               | ~8M   | 5088 MB (0.85%)    | 4587 MB (0.89%)    | 404 MB (0.73%)   | 97 MB (0.30%)
       ways_083               | ~8M   | 5038 MB (0.84%)    | 4389 MB (0.86%)    | 402 MB (0.73%)   | 247 MB (0.75%)
       ways_044               | ~9M   | 5029 MB (0.84%)    | 4532 MB (0.88%)    | 461 MB (0.84%)   | 37 MB (0.11%)
       ways_018               | ~8M   | 5020 MB (0.84%)    | 4390 MB (0.86%)    | 415 MB (0.75%)   | 214 MB (0.65%)
       ways_021               | ~8M   | 5003 MB (0.83%)    | 4415 MB (0.86%)    | 414 MB (0.75%)   | 174 MB (0.53%)
       ways_077               | ~8M   | 4991 MB (0.83%)    | 4451 MB (0.87%)    | 416 MB (0.76%)   | 124 MB (0.38%)
       ways_067               | ~8M   | 4990 MB (0.83%)    | 4493 MB (0.88%)    | 425 MB (0.77%)   | 73 MB (0.22%)
       ways_099               | ~9M   | 4983 MB (0.83%)    | 4446 MB (0.87%)    | 475 MB (0.86%)   | 62 MB (0.19%)
       ways_009               | ~8M   | 4935 MB (0.82%)    | 4265 MB (0.83%)    | 390 MB (0.71%)   | 279 MB (0.85%)
       ways_055               | ~7M   | 4933 MB (0.82%)    | 4363 MB (0.85%)    | 380 MB (0.69%)   | 190 MB (0.58%)
       ways_028               | ~9M   | 4915 MB (0.82%)    | 4424 MB (0.86%)    | 445 MB (0.81%)   | 45 MB (0.14%)
       ways_069               | ~7M   | 4913 MB (0.82%)    | 4294 MB (0.84%)    | 378 MB (0.69%)   | 240 MB (0.73%)
       ways_086               | ~8M   | 4900 MB (0.82%)    | 4390 MB (0.86%)    | 393 MB (0.71%)   | 118 MB (0.36%)
       ways_007               | ~7M   | 4889 MB (0.81%)    | 4282 MB (0.83%)    | 375 MB (0.68%)   | 232 MB (0.71%)
       ways_015               | ~7M   | 4847 MB (0.81%)    | 4167 MB (0.81%)    | 354 MB (0.64%)   | 325 MB (0.99%)
       ways_041               | ~8M   | 4836 MB (0.80%)    | 4237 MB (0.83%)    | 392 MB (0.71%)   | 207 MB (0.63%)
       ways_066               | ~7M   | 4832 MB (0.80%)    | 4335 MB (0.85%)    | 376 MB (0.68%)   | 121 MB (0.37%)
       ways_084               | ~8M   | 4823 MB (0.80%)    | 4227 MB (0.82%)    | 411 MB (0.75%)   | 185 MB (0.56%)
       ways_056               | ~7M   | 4819 MB (0.80%)    | 4346 MB (0.85%)    | 379 MB (0.69%)   | 94 MB (0.29%)
       ways_064               | ~8M   | 4764 MB (0.79%)    | 4314 MB (0.84%)    | 404 MB (0.73%)   | 46 MB (0.14%)
       ways_035               | ~8M   | 4709 MB (0.78%)    | 4244 MB (0.83%)    | 421 MB (0.76%)   | 44 MB (0.13%)
       ways_011               | ~7M   | 4703 MB (0.78%)    | 3954 MB (0.77%)    | 348 MB (0.63%)   | 402 MB (1.23%)
       ways_078               | ~7M   | 4687 MB (0.78%)    | 4232 MB (0.82%)    | 377 MB (0.68%)   | 79 MB (0.24%)
       ways_062               | ~7M   | 4670 MB (0.78%)    | 4291 MB (0.84%)    | 358 MB (0.65%)   | 21 MB (0.06%)
       ways_050               | ~8M   | 4660 MB (0.78%)    | 4093 MB (0.80%)    | 388 MB (0.70%)   | 179 MB (0.55%)
       ways_008               | ~6M   | 4553 MB (0.76%)    | 3826 MB (0.75%)    | 311 MB (0.56%)   | 416 MB (1.27%)
       ways_058               | ~6M   | 4518 MB (0.75%)    | 3921 MB (0.76%)    | 321 MB (0.58%)   | 276 MB (0.84%)
       ways_085               | ~7M   | 4515 MB (0.75%)    | 4018 MB (0.78%)    | 368 MB (0.67%)   | 129 MB (0.39%)
       ways_072               | ~8M   | 4493 MB (0.75%)    | 3981 MB (0.78%)    | 419 MB (0.76%)   | 94 MB (0.29%)
       ways_076               | ~7M   | 4490 MB (0.75%)    | 3956 MB (0.77%)    | 365 MB (0.66%)   | 170 MB (0.52%)
       ways_020               | ~6M   | 4452 MB (0.74%)    | 3779 MB (0.74%)    | 290 MB (0.53%)   | 383 MB (1.17%)
       ways_059               | ~6M   | 4363 MB (0.73%)    | 3846 MB (0.75%)    | 318 MB (0.58%)   | 199 MB (0.61%)
       ways_036               | ~8M   | 4360 MB (0.73%)    | 3909 MB (0.76%)    | 398 MB (0.72%)   | 52 MB (0.16%)
       ways_016               | ~6M   | 4333 MB (0.72%)    | 3647 MB (0.71%)    | 292 MB (0.53%)   | 394 MB (1.20%)
       ways_32767             | ~2M   | 4321 MB (0.72%)    | 2618 MB (0.51%)    | 89 MB (0.16%)    | 1615 MB (4.93%)
       ways_068               | ~6M   | 4075 MB (0.68%)    | 3566 MB (0.70%)    | 296 MB (0.54%)   | 214 MB (0.65%)
       ways_025               | ~7M   | 4062 MB (0.68%)    | 3620 MB (0.71%)    | 362 MB (0.66%)   | 81 MB (0.25%)
       ways_039               | ~7M   | 4042 MB (0.67%)    | 3581 MB (0.70%)    | 354 MB (0.64%)   | 107 MB (0.33%)
       ways_012               | ~5M   | 3962 MB (0.66%)    | 3418 MB (0.67%)    | 244 MB (0.44%)   | 301 MB (0.92%)
       ways_065               | ~6M   | 3928 MB (0.65%)    | 3481 MB (0.68%)    | 317 MB (0.57%)   | 130 MB (0.40%)
       ways_023               | ~6M   | 3773 MB (0.63%)    | 3429 MB (0.67%)    | 315 MB (0.57%)   | 29 MB (0.09%)
       ways_014               | ~4M   | 3411 MB (0.57%)    | 3009 MB (0.59%)    | 230 MB (0.42%)   | 172 MB (0.53%)
       ways_061               | ~4M   | 2920 MB (0.49%)    | 2602 MB (0.51%)    | 225 MB (0.41%)   | 93 MB (0.28%)
       osm_file_block_content | ~32M  | 2834 MB (0.47%)    | 2834 MB (0.55%)    | 0 bytes (0.00%)  | 
       ways_030               | ~4M   | 2378 MB (0.40%)    | 2108 MB (0.41%)    | 190 MB (0.34%)   | 81 MB (0.25%)
       ways_022               | ~3M   | 1986 MB (0.33%)    | 1787 MB (0.35%)    | 159 MB (0.29%)   | 40 MB (0.12%)
       nodes_029              | ~8M   | 1959 MB (0.33%)    | 1725 MB (0.34%)    | 234 MB (0.42%)   | 64 kB (0.00%)
       ways_027               | ~3M   | 1863 MB (0.31%)    | 1676 MB (0.33%)    | 170 MB (0.31%)   | 18 MB (0.06%)
       ways_063               | ~3M   | 1782 MB (0.30%)    | 1593 MB (0.31%)    | 135 MB (0.25%)   | 53 MB (0.16%)
       relations              | ~10M  | 1480 MB (0.25%)    | 1477 MB (0.29%)    | 0 bytes (0.00%)  | 3360 kB (0.01%)
       nodes_082              | ~4M   | 1459 MB (0.24%)    | 1342 MB (0.26%)    | 117 MB (0.21%)   | 88 kB (0.00%)
       ways_034               | ~3M   | 1399 MB (0.23%)    | 1247 MB (0.24%)    | 130 MB (0.24%)   | 22 MB (0.07%)
       ways_032               | ~2M   | 1273 MB (0.21%)    | 1109 MB (0.22%)    | 106 MB (0.19%)   | 57 MB (0.18%)
       nodes_051              | ~6M   | 1174 MB (0.20%)    | 1007 MB (0.20%)    | 166 MB (0.30%)   | 168 kB (0.00%)
       nodes_011              | ~5M   | 1105 MB (0.18%)    | 946 MB (0.18%)     | 158 MB (0.29%)   | 96 kB (0.00%)
       nodes_045              | ~4M   | 986 MB (0.16%)     | 874 MB (0.17%)     | 112 MB (0.20%)   | 88 kB (0.00%)
       nodes_069              | ~4M   | 957 MB (0.16%)     | 829 MB (0.16%)     | 128 MB (0.23%)   | 8192 bytes (0.00%)
       multipolygon_012       | ~292k | 914 MB (0.15%)     | 385 MB (0.08%)     | 43 MB (0.08%)    | 486 MB (1.48%)
       nodes_043              | ~4M   | 831 MB (0.14%)     | 710 MB (0.14%)     | 120 MB (0.22%)   | 56 kB (0.00%)
       nodes_037              | ~4M   | 821 MB (0.14%)     | 712 MB (0.14%)     | 109 MB (0.20%)   | 56 kB (0.00%)
       nodes_042              | ~4M   | 800 MB (0.13%)     | 694 MB (0.14%)     | 106 MB (0.19%)   | 64 kB (0.00%)
       nodes_047              | ~4M   | 753 MB (0.13%)     | 633 MB (0.12%)     | 120 MB (0.22%)   | 56 kB (0.00%)
       nodes_064              | ~3M   | 739 MB (0.12%)     | 637 MB (0.12%)     | 102 MB (0.18%)   | 8192 bytes (0.00%)
       nodes_089              | ~3M   | 721 MB (0.12%)     | 634 MB (0.12%)     | 87 MB (0.16%)    | 200 kB (0.00%)
       multipolygon_014       | ~226k | 714 MB (0.12%)     | 301 MB (0.06%)     | 33 MB (0.06%)    | 380 MB (1.16%)
       nodes_053              | ~4M   | 701 MB (0.12%)     | 595 MB (0.12%)     | 106 MB (0.19%)   | 56 kB (0.00%)
       nodes_040              | ~4M   | 700 MB (0.12%)     | 590 MB (0.11%)     | 110 MB (0.20%)   | 64 kB (0.00%)
       nodes_031              | ~3M   | 675 MB (0.11%)     | 585 MB (0.11%)     | 90 MB (0.16%)    | 72 kB (0.00%)
       nodes_052              | ~3M   | 665 MB (0.11%)     | 561 MB (0.11%)     | 105 MB (0.19%)   | 56 kB (0.00%)
       nodes_044              | ~3M   | 634 MB (0.11%)     | 539 MB (0.11%)     | 94 MB (0.17%)    | 24 kB (0.00%)
       nodes_009              | ~3M   | 609 MB (0.10%)     | 505 MB (0.10%)     | 104 MB (0.19%)   | 104 kB (0.00%)
       multipolygon_016       | ~127k | 600 MB (0.10%)     | 196 MB (0.04%)     | 18 MB (0.03%)    | 386 MB (1.18%)
       nodes_048              | ~3M   | 595 MB (0.10%)     | 500 MB (0.10%)     | 94 MB (0.17%)    | 8192 bytes (0.00%)
       multipolygon_018       | ~265k | 578 MB (0.10%)     | 304 MB (0.06%)     | 39 MB (0.07%)    | 235 MB (0.72%)
       multipolygon_015       | ~206k | 576 MB (0.10%)     | 254 MB (0.05%)     | 30 MB (0.05%)    | 293 MB (0.89%)
       multipolygon_021       | ~123k | 568 MB (0.09%)     | 227 MB (0.04%)     | 20 MB (0.04%)    | 321 MB (0.98%)
       nodes_046              | ~3M   | 563 MB (0.09%)     | 479 MB (0.09%)     | 84 MB (0.15%)    | 64 kB (0.00%)
       multipolygon_033       | ~154k | 540 MB (0.09%)     | 274 MB (0.05%)     | 24 MB (0.04%)    | 242 MB (0.74%)
       nodes_035              | ~2M   | 532 MB (0.09%)     | 458 MB (0.09%)     | 74 MB (0.13%)    | 56 kB (0.00%)
       nodes_049              | ~3M   | 524 MB (0.09%)     | 442 MB (0.09%)     | 82 MB (0.15%)    | 56 kB (0.00%)
       nodes_077              | ~3M   | 519 MB (0.09%)     | 432 MB (0.08%)     | 87 MB (0.16%)    | 56 kB (0.00%)
       nodes_010              | ~3M   | 513 MB (0.09%)     | 421 MB (0.08%)     | 91 MB (0.17%)    | 88 kB (0.00%)
       nodes_050              | ~3M   | 468 MB (0.08%)     | 388 MB (0.08%)     | 80 MB (0.15%)    | 8192 bytes (0.00%)
       multipolygon_013       | ~142k | 466 MB (0.08%)     | 203 MB (0.04%)     | 21 MB (0.04%)    | 242 MB (0.74%)
       multipolygon_020       | ~60k  | 465 MB (0.08%)     | 122 MB (0.02%)     | 8296 kB (0.01%)  | 335 MB (1.02%)
       multipolygon_037       | ~133k | 461 MB (0.08%)     | 251 MB (0.05%)     | 20 MB (0.04%)    | 190 MB (0.58%)
       nodes_067              | ~3M   | 459 MB (0.08%)     | 372 MB (0.07%)     | 87 MB (0.16%)    | 24 kB (0.00%)
       nodes_055              | ~2M   | 436 MB (0.07%)     | 361 MB (0.07%)     | 75 MB (0.14%)    | 56 kB (0.00%)
       nodes_076              | ~2M   | 433 MB (0.07%)     | 361 MB (0.07%)     | 72 MB (0.13%)    | 80 kB (0.00%)
       nodes_060              | ~2M   | 433 MB (0.07%)     | 360 MB (0.07%)     | 73 MB (0.13%)    | 24 kB (0.00%)
       nodes_026              | ~2M   | 426 MB (0.07%)     | 359 MB (0.07%)     | 67 MB (0.12%)    | 56 kB (0.00%)
       nodes_078              | ~2M   | 423 MB (0.07%)     | 358 MB (0.07%)     | 65 MB (0.12%)    | 24 kB (0.00%)
       nodes_036              | ~2M   | 421 MB (0.07%)     | 360 MB (0.07%)     | 61 MB (0.11%)    | 8192 bytes (0.00%)
       nodes_024              | ~2M   | 418 MB (0.07%)     | 351 MB (0.07%)     | 67 MB (0.12%)    | 56 kB (0.00%)
       multipolygon_019       | ~249k | 418 MB (0.07%)     | 257 MB (0.05%)     | 37 MB (0.07%)    | 124 MB (0.38%)
       nodes_028              | ~2M   | 403 MB (0.07%)     | 338 MB (0.07%)     | 65 MB (0.12%)    | 56 kB (0.00%)
       nodes_068              | ~2M   | 400 MB (0.07%)     | 343 MB (0.07%)     | 57 MB (0.10%)    | 8192 bytes (0.00%)
       nodes_066              | ~2M   | 392 MB (0.07%)     | 335 MB (0.07%)     | 57 MB (0.10%)    | 56 kB (0.00%)
       multipolygon_011       | ~73k  | 383 MB (0.06%)     | 120 MB (0.02%)     | 10 MB (0.02%)    | 252 MB (0.77%)
       nodes_075              | ~2M   | 380 MB (0.06%)     | 313 MB (0.06%)     | 68 MB (0.12%)    | 88 kB (0.00%)
       nodes_062              | ~2M   | 374 MB (0.06%)     | 322 MB (0.06%)     | 52 MB (0.09%)    | 24 kB (0.00%)
       nodes_088              | ~2M   | 373 MB (0.06%)     | 307 MB (0.06%)     | 65 MB (0.12%)    | 24 kB (0.00%)
       nodes_015              | ~2M   | 371 MB (0.06%)     | 309 MB (0.06%)     | 62 MB (0.11%)    | 256 kB (0.00%)
       nodes_021              | ~2M   | 359 MB (0.06%)     | 295 MB (0.06%)     | 63 MB (0.12%)    | 56 kB (0.00%)
       multipolygon_069       | ~62k  | 355 MB (0.06%)     | 109 MB (0.02%)     | 9608 kB (0.02%)  | 237 MB (0.72%)
       nodes_025              | ~2M   | 351 MB (0.06%)     | 291 MB (0.06%)     | 60 MB (0.11%)    | 64 kB (0.00%)
       nodes_079              | ~2M   | 345 MB (0.06%)     | 291 MB (0.06%)     | 54 MB (0.10%)    | 24 kB (0.00%)
       nodes_033              | ~2M   | 341 MB (0.06%)     | 286 MB (0.06%)     | 55 MB (0.10%)    | 64 kB (0.00%)
       multipolygon_068       | ~50k  | 341 MB (0.06%)     | 90 MB (0.02%)      | 7656 kB (0.01%)  | 244 MB (0.74%)
       nodes_038              | ~2M   | 340 MB (0.06%)     | 274 MB (0.05%)     | 65 MB (0.12%)    | 72 kB (0.00%)
       nodes_087              | ~2M   | 333 MB (0.06%)     | 273 MB (0.05%)     | 59 MB (0.11%)    | 80 kB (0.00%)
       nodes_013              | ~2M   | 331 MB (0.06%)     | 271 MB (0.05%)     | 60 MB (0.11%)    | 128 kB (0.00%)
       multipolygon_070       | ~105k | 317 MB (0.05%)     | 105 MB (0.02%)     | 16 MB (0.03%)    | 196 MB (0.60%)
       nodes_000              | ~2M   | 312 MB (0.05%)     | 251 MB (0.05%)     | 61 MB (0.11%)    | 112 kB (0.00%)
       multipolygon_077       | ~148k | 309 MB (0.05%)     | 171 MB (0.03%)     | 23 MB (0.04%)    | 115 MB (0.35%)
       nodes_023              | ~2M   | 308 MB (0.05%)     | 262 MB (0.05%)     | 46 MB (0.08%)    | 64 kB (0.00%)
       nodes_019              | ~2M   | 308 MB (0.05%)     | 252 MB (0.05%)     | 56 MB (0.10%)    | 24 kB (0.00%)
       nodes_007              | ~2M   | 306 MB (0.05%)     | 247 MB (0.05%)     | 59 MB (0.11%)    | 64 kB (0.00%)
       nodes_041              | ~2M   | 305 MB (0.05%)     | 256 MB (0.05%)     | 50 MB (0.09%)    | 120 kB (0.00%)
       nodes_086              | ~2M   | 305 MB (0.05%)     | 254 MB (0.05%)     | 51 MB (0.09%)    | 24 kB (0.00%)
       nodes_085              | ~2M   | 297 MB (0.05%)     | 246 MB (0.05%)     | 50 MB (0.09%)    | 64 kB (0.00%)
       nodes_014              | ~2M   | 293 MB (0.05%)     | 246 MB (0.05%)     | 47 MB (0.09%)    | 496 kB (0.00%)
       multipolygon_008       | ~50k  | 288 MB (0.05%)     | 77 MB (0.02%)      | 8032 kB (0.01%)  | 204 MB (0.62%)
       nodes_056              | ~2M   | 286 MB (0.05%)     | 234 MB (0.05%)     | 52 MB (0.09%)    | 24 kB (0.00%)
       multipolygon_038       | ~156k | 286 MB (0.05%)     | 173 MB (0.03%)     | 24 MB (0.04%)    | 89 MB (0.27%)
       nodes_012              | ~2M   | 286 MB (0.05%)     | 232 MB (0.05%)     | 52 MB (0.10%)    | 736 kB (0.00%)
       nodes_039              | ~1M   | 275 MB (0.05%)     | 230 MB (0.04%)     | 45 MB (0.08%)    | 72 kB (0.00%)
       multipolygon_058       | ~46k  | 274 MB (0.05%)     | 83 MB (0.02%)      | 6536 kB (0.01%)  | 184 MB (0.56%)
       nodes_083              | ~2M   | 270 MB (0.05%)     | 218 MB (0.04%)     | 52 MB (0.10%)    | 104 kB (0.00%)
       nodes_018              | ~2M   | 270 MB (0.04%)     | 222 MB (0.04%)     | 48 MB (0.09%)    | 64 kB (0.00%)
       nodes_084              | ~2M   | 268 MB (0.04%)     | 217 MB (0.04%)     | 52 MB (0.09%)    | 120 kB (0.00%)
       multipolygon_045       | ~130k | 259 MB (0.04%)     | 155 MB (0.03%)     | 20 MB (0.04%)    | 83 MB (0.25%)
       multipolygon_050       | ~60k  | 255 MB (0.04%)     | 83 MB (0.02%)      | 9736 kB (0.02%)  | 163 MB (0.50%)
       multipolygon_031       | ~84k  | 246 MB (0.04%)     | 129 MB (0.03%)     | 14 MB (0.02%)    | 103 MB (0.31%)
       multipolygon_048       | ~58k  | 245 MB (0.04%)     | 91 MB (0.02%)      | 9520 kB (0.02%)  | 145 MB (0.44%)
       multipolygon_017       | ~102k | 242 MB (0.04%)     | 129 MB (0.03%)     | 15 MB (0.03%)    | 98 MB (0.30%)
       multipolygon_040       | ~64k  | 241 MB (0.04%)     | 83 MB (0.02%)      | 10200 kB (0.02%) | 148 MB (0.45%)
       multipolygon_039       | ~117k | 241 MB (0.04%)     | 122 MB (0.02%)     | 18 MB (0.03%)    | 101 MB (0.31%)
       multipolygon_091       | ~43k  | 237 MB (0.04%)     | 67 MB (0.01%)      | 6432 kB (0.01%)  | 164 MB (0.50%)
       multipolygon_097       | ~54k  | 237 MB (0.04%)     | 80 MB (0.02%)      | 7976 kB (0.01%)  | 149 MB (0.46%)
       nodes_074              | ~1M   | 236 MB (0.04%)     | 197 MB (0.04%)     | 39 MB (0.07%)    | 64 kB (0.00%)
       nodes_058              | ~1M   | 235 MB (0.04%)     | 198 MB (0.04%)     | 37 MB (0.07%)    | 64 kB (0.00%)
       multipolygon_000       | ~51k  | 234 MB (0.04%)     | 79 MB (0.02%)      | 7584 kB (0.01%)  | 147 MB (0.45%)
       nodes_057              | ~2M   | 234 MB (0.04%)     | 187 MB (0.04%)     | 47 MB (0.08%)    | 24 kB (0.00%)
       multipolygon_054       | ~51k  | 232 MB (0.04%)     | 79 MB (0.02%)      | 7760 kB (0.01%)  | 146 MB (0.45%)
       nodes_070              | ~1M   | 229 MB (0.04%)     | 187 MB (0.04%)     | 42 MB (0.08%)    | 120 kB (0.00%)
       multipolygon_041       | ~36k  | 229 MB (0.04%)     | 63 MB (0.01%)      | 5688 kB (0.01%)  | 160 MB (0.49%)
       multipolygon_071       | ~103k | 228 MB (0.04%)     | 122 MB (0.02%)     | 16 MB (0.03%)    | 91 MB (0.28%)
       multipolygon_046       | ~84k  | 226 MB (0.04%)     | 127 MB (0.02%)     | 13 MB (0.02%)    | 86 MB (0.26%)
       multipolygon_047       | ~63k  | 223 MB (0.04%)     | 102 MB (0.02%)     | 10152 kB (0.02%) | 112 MB (0.34%)
       multipolygon_075       | ~62k  | 218 MB (0.04%)     | 90 MB (0.02%)      | 9432 kB (0.02%)  | 119 MB (0.36%)
       nodes_071              | ~1M   | 216 MB (0.04%)     | 175 MB (0.03%)     | 40 MB (0.07%)    | 88 kB (0.00%)
       nodes_054              | ~2M   | 214 MB (0.04%)     | 168 MB (0.03%)     | 46 MB (0.08%)    | 120 kB (0.00%)
       multipolygon_042       | ~56k  | 213 MB (0.04%)     | 88 MB (0.02%)      | 8960 kB (0.02%)  | 116 MB (0.35%)
       nodes_092              | ~1M   | 209 MB (0.03%)     | 170 MB (0.03%)     | 40 MB (0.07%)    | 64 kB (0.00%)
       multipolygon_052       | ~57k  | 207 MB (0.03%)     | 87 MB (0.02%)      | 9288 kB (0.02%)  | 111 MB (0.34%)
       nodes_020              | ~1M   | 207 MB (0.03%)     | 168 MB (0.03%)     | 39 MB (0.07%)    | 24 kB (0.00%)
       nodes_073              | ~1M   | 206 MB (0.03%)     | 173 MB (0.03%)     | 33 MB (0.06%)    | 8192 bytes (0.00%)
       multipolygon_007       | ~64k  | 202 MB (0.03%)     | 81 MB (0.02%)      | 9720 kB (0.02%)  | 111 MB (0.34%)
       nodes_065              | ~1M   | 202 MB (0.03%)     | 171 MB (0.03%)     | 31 MB (0.06%)    | 24 kB (0.00%)
       multipolygon_051       | ~68k  | 202 MB (0.03%)     | 106 MB (0.02%)     | 11 MB (0.02%)    | 85 MB (0.26%)
       nodes_022              | ~987k | 201 MB (0.03%)     | 171 MB (0.03%)     | 30 MB (0.05%)    | 24 kB (0.00%)
       multipolygon_094       | ~32k  | 201 MB (0.03%)     | 46 MB (0.01%)      | 4952 kB (0.01%)  | 150 MB (0.46%)
       nodes_017              | ~1M   | 195 MB (0.03%)     | 158 MB (0.03%)     | 37 MB (0.07%)    | 24 kB (0.00%)
       nodes_094              | ~1M   | 194 MB (0.03%)     | 162 MB (0.03%)     | 32 MB (0.06%)    | 88 kB (0.00%)
       multipolygon_005       | ~36k  | 191 MB (0.03%)     | 50 MB (0.01%)      | 5496 kB (0.01%)  | 135 MB (0.41%)
       multipolygon_049       | ~33k  | 190 MB (0.03%)     | 56 MB (0.01%)      | 5472 kB (0.01%)  | 129 MB (0.39%)
       nodes_091              | ~1M   | 187 MB (0.03%)     | 154 MB (0.03%)     | 33 MB (0.06%)    | 152 kB (0.00%)
       multipolygon_059       | ~34k  | 186 MB (0.03%)     | 66 MB (0.01%)      | 4904 kB (0.01%)  | 115 MB (0.35%)
       multipolygon_083       | ~34k  | 186 MB (0.03%)     | 55 MB (0.01%)      | 4832 kB (0.01%)  | 125 MB (0.38%)
       nodes_061              | ~1M   | 179 MB (0.03%)     | 148 MB (0.03%)     | 32 MB (0.06%)    | 8192 bytes (0.00%)
       multipolygon_009       | ~43k  | 174 MB (0.03%)     | 56 MB (0.01%)      | 6440 kB (0.01%)  | 112 MB (0.34%)
       multipolygon_078       | ~28k  | 169 MB (0.03%)     | 56 MB (0.01%)      | 4664 kB (0.01%)  | 108 MB (0.33%)
       nodes_059              | ~1M   | 165 MB (0.03%)     | 133 MB (0.03%)     | 32 MB (0.06%)    | 24 kB (0.00%)
       multipolygon_085       | ~29k  | 164 MB (0.03%)     | 42 MB (0.01%)      | 4400 kB (0.01%)  | 117 MB (0.36%)
       nodes_008              | ~1M   | 163 MB (0.03%)     | 130 MB (0.03%)     | 33 MB (0.06%)    | 56 kB (0.00%)
       nodes_072              | ~842k | 162 MB (0.03%)     | 136 MB (0.03%)     | 25 MB (0.05%)    | 64 kB (0.00%)
       nodes_093              | ~956k | 158 MB (0.03%)     | 130 MB (0.03%)     | 29 MB (0.05%)    | 104 kB (0.00%)
       multipolygon_030       | ~43k  | 158 MB (0.03%)     | 74 MB (0.01%)      | 6304 kB (0.01%)  | 78 MB (0.24%)
       multipolygon_076       | ~54k  | 155 MB (0.03%)     | 66 MB (0.01%)      | 8576 kB (0.02%)  | 81 MB (0.25%)
       multipolygon_098       | ~49k  | 150 MB (0.02%)     | 56 MB (0.01%)      | 7112 kB (0.01%)  | 87 MB (0.27%)
       multipolygon_055       | ~42k  | 149 MB (0.02%)     | 64 MB (0.01%)      | 6552 kB (0.01%)  | 79 MB (0.24%)
       nodes_030              | ~884k | 149 MB (0.02%)     | 122 MB (0.02%)     | 27 MB (0.05%)    | 56 kB (0.00%)
       multipolygon_029       | ~54k  | 148 MB (0.02%)     | 84 MB (0.02%)      | 8616 kB (0.02%)  | 56 MB (0.17%)
       multipolygon_010       | ~33k  | 147 MB (0.02%)     | 52 MB (0.01%)      | 4928 kB (0.01%)  | 90 MB (0.27%)
       multipolygon_024       | ~23k  | 146 MB (0.02%)     | 33 MB (0.01%)      | 3808 kB (0.01%)  | 109 MB (0.33%)
       multipolygon_084       | ~43k  | 146 MB (0.02%)     | 48 MB (0.01%)      | 6240 kB (0.01%)  | 92 MB (0.28%)
       multipolygon_096       | ~25k  | 145 MB (0.02%)     | 41 MB (0.01%)      | 3824 kB (0.01%)  | 101 MB (0.31%)
       nodes_016              | ~1M   | 145 MB (0.02%)     | 114 MB (0.02%)     | 31 MB (0.06%)    | 24 kB (0.00%)
       multipolygon_044       | ~65k  | 145 MB (0.02%)     | 96 MB (0.02%)      | 10 MB (0.02%)    | 38 MB (0.12%)
       multipolygon_028       | ~44k  | 143 MB (0.02%)     | 57 MB (0.01%)      | 7024 kB (0.01%)  | 79 MB (0.24%)
       multipolygon_053       | ~44k  | 140 MB (0.02%)     | 65 MB (0.01%)      | 7192 kB (0.01%)  | 68 MB (0.21%)
       multipolygon_023       | ~21k  | 135 MB (0.02%)     | 28 MB (0.01%)      | 3720 kB (0.01%)  | 103 MB (0.31%)
       multipolygon_065       | ~30k  | 133 MB (0.02%)     | 50 MB (0.01%)      | 4760 kB (0.01%)  | 78 MB (0.24%)
       nodes_027              | ~770k | 131 MB (0.02%)     | 108 MB (0.02%)     | 23 MB (0.04%)    | 64 kB (0.00%)
       multipolygon_025       | ~29k  | 130 MB (0.02%)     | 44 MB (0.01%)      | 4784 kB (0.01%)  | 82 MB (0.25%)
       nodes_098              | ~763k | 129 MB (0.02%)     | 106 MB (0.02%)     | 23 MB (0.04%)    | 112 kB (0.00%)
       multipolygon_036       | ~30k  | 128 MB (0.02%)     | 67 MB (0.01%)      | 4856 kB (0.01%)  | 57 MB (0.17%)
       multipolygon_043       | ~56k  | 127 MB (0.02%)     | 83 MB (0.02%)      | 9184 kB (0.02%)  | 35 MB (0.11%)
       nodes_081              | ~855k | 124 MB (0.02%)     | 99 MB (0.02%)      | 26 MB (0.05%)    | 56 kB (0.00%)
       multipolygon_035       | ~37k  | 124 MB (0.02%)     | 73 MB (0.01%)      | 6200 kB (0.01%)  | 45 MB (0.14%)
       multipolygon_006       | ~19k  | 122 MB (0.02%)     | 37 MB (0.01%)      | 3016 kB (0.01%)  | 82 MB (0.25%)
       nodes_003              | ~653k | 121 MB (0.02%)     | 101 MB (0.02%)     | 20 MB (0.04%)    | 64 kB (0.00%)
       multipolygon_074       | ~26k  | 115 MB (0.02%)     | 33 MB (0.01%)      | 4184 kB (0.01%)  | 78 MB (0.24%)
       multipolygon_087       | ~30k  | 113 MB (0.02%)     | 43 MB (0.01%)      | 4672 kB (0.01%)  | 66 MB (0.20%)
       multipolygon_060       | ~37k  | 113 MB (0.02%)     | 50 MB (0.01%)      | 5656 kB (0.01%)  | 57 MB (0.17%)
       multipolygon_099       | ~36k  | 112 MB (0.02%)     | 36 MB (0.01%)      | 5784 kB (0.01%)  | 70 MB (0.21%)
       nodes_034              | ~638k | 112 MB (0.02%)     | 92 MB (0.02%)      | 19 MB (0.03%)    | 56 kB (0.00%)
       multipolygon_026       | ~34k  | 111 MB (0.02%)     | 48 MB (0.01%)      | 5680 kB (0.01%)  | 57 MB (0.17%)
       multipolygon_079       | ~32k  | 108 MB (0.02%)     | 43 MB (0.01%)      | 5360 kB (0.01%)  | 60 MB (0.18%)
       multipolygon_089       | ~39k  | 106 MB (0.02%)     | 48 MB (0.01%)      | 5808 kB (0.01%)  | 52 MB (0.16%)
       nodes_006              | ~666k | 105 MB (0.02%)     | 85 MB (0.02%)      | 20 MB (0.04%)    | 136 kB (0.00%)
       multipolygon_092       | ~44k  | 99 MB (0.02%)      | 56 MB (0.01%)      | 6800 kB (0.01%)  | 36 MB (0.11%)
       multipolygon_067       | ~23k  | 99 MB (0.02%)      | 40 MB (0.01%)      | 3688 kB (0.01%)  | 56 MB (0.17%)
       multipolygon_032       | ~25k  | 99 MB (0.02%)      | 39 MB (0.01%)      | 4256 kB (0.01%)  | 56 MB (0.17%)
       multipolygon_088       | ~20k  | 97 MB (0.02%)      | 29 MB (0.01%)      | 3120 kB (0.01%)  | 66 MB (0.20%)
       nodes_095              | ~554k | 95 MB (0.02%)      | 78 MB (0.02%)      | 17 MB (0.03%)    | 64 kB (0.00%)
       multipolygon_066       | ~22k  | 94 MB (0.02%)      | 39 MB (0.01%)      | 3424 kB (0.01%)  | 51 MB (0.16%)
       multipolygon_073       | ~18k  | 93 MB (0.02%)      | 23 MB (0.00%)      | 2856 kB (0.01%)  | 67 MB (0.20%)
       multipolygon_082       | ~37k  | 93 MB (0.02%)      | 40 MB (0.01%)      | 5832 kB (0.01%)  | 47 MB (0.14%)
       multipolygon_086       | ~21k  | 92 MB (0.02%)      | 32 MB (0.01%)      | 3296 kB (0.01%)  | 57 MB (0.17%)
       nodes_080              | ~626k | 92 MB (0.02%)      | 73 MB (0.01%)      | 19 MB (0.03%)    | 80 kB (0.00%)
       multipolygon_072       | ~20k  | 90 MB (0.02%)      | 26 MB (0.00%)      | 3272 kB (0.01%)  | 61 MB (0.19%)
       multipolygon_080       | ~18k  | 88 MB (0.01%)      | 37 MB (0.01%)      | 2880 kB (0.01%)  | 49 MB (0.15%)
       nodes_063              | ~508k | 87 MB (0.01%)      | 71 MB (0.01%)      | 15 MB (0.03%)    | 24 kB (0.00%)
       nodes_099              | ~521k | 85 MB (0.01%)      | 69 MB (0.01%)      | 16 MB (0.03%)    | 192 kB (0.00%)
       nodes_032              | ~447k | 85 MB (0.01%)      | 71 MB (0.01%)      | 13 MB (0.02%)    | 120 kB (0.00%)
       nodes_001              | ~537k | 80 MB (0.01%)      | 64 MB (0.01%)      | 16 MB (0.03%)    | 72 kB (0.00%)
       nodes_097              | ~450k | 80 MB (0.01%)      | 66 MB (0.01%)      | 14 MB (0.02%)    | 88 kB (0.00%)
       multipolygon_034       | ~46k  | 79 MB (0.01%)      | 50 MB (0.01%)      | 7440 kB (0.01%)  | 22 MB (0.07%)
       nodes_090              | ~502k | 77 MB (0.01%)      | 62 MB (0.01%)      | 15 MB (0.03%)    | 72 kB (0.00%)
       multipolygon_003       | ~18k  | 77 MB (0.01%)      | 25 MB (0.00%)      | 2704 kB (0.00%)  | 49 MB (0.15%)
       nodes_096              | ~464k | 76 MB (0.01%)      | 62 MB (0.01%)      | 14 MB (0.03%)    | 56 kB (0.00%)
       multipolygon_061       | ~17k  | 75 MB (0.01%)      | 29 MB (0.01%)      | 2712 kB (0.00%)  | 43 MB (0.13%)
       multipolygon_064       | ~22k  | 71 MB (0.01%)      | 36 MB (0.01%)      | 3704 kB (0.01%)  | 30 MB (0.09%)
       multipolygon_027       | ~16k  | 70 MB (0.01%)      | 29 MB (0.01%)      | 2704 kB (0.00%)  | 39 MB (0.12%)
       multipolygon_081       | ~45k  | 68 MB (0.01%)      | 37 MB (0.01%)      | 6576 kB (0.01%)  | 24 MB (0.07%)
       multipolygon_093       | ~19k  | 68 MB (0.01%)      | 26 MB (0.00%)      | 2848 kB (0.01%)  | 39 MB (0.12%)
       multipolygon_090       | ~19k  | 65 MB (0.01%)      | 23 MB (0.00%)      | 2880 kB (0.01%)  | 39 MB (0.12%)
       multipolygon_022       | ~14k  | 63 MB (0.01%)      | 24 MB (0.00%)      | 2272 kB (0.00%)  | 37 MB (0.11%)
       multipolygon_056       | ~21k  | 63 MB (0.01%)      | 34 MB (0.01%)      | 3064 kB (0.01%)  | 26 MB (0.08%)
       multipolygon_057       | ~15k  | 56 MB (0.01%)      | 23 MB (0.00%)      | 2384 kB (0.00%)  | 30 MB (0.09%)
       multipolygon_062       | ~40k  | 53 MB (0.01%)      | 41 MB (0.01%)      | 6128 kB (0.01%)  | 6216 kB (0.02%)
       multipolygon_001       | ~19k  | 48 MB (0.01%)      | 23 MB (0.00%)      | 3224 kB (0.01%)  | 22 MB (0.07%)
       multipolygon_095       | ~17k  | 47 MB (0.01%)      | 21 MB (0.00%)      | 2552 kB (0.00%)  | 24 MB (0.07%)
       nodes_005              | ~310k | 47 MB (0.01%)      | 37 MB (0.01%)      | 9576 kB (0.02%)  | 96 kB (0.00%)
       multipolygon_004       | ~20k  | 46 MB (0.01%)      | 17 MB (0.00%)      | 3080 kB (0.01%)  | 25 MB (0.08%)
       nodes_004              | ~191k | 38 MB (0.01%)      | 32 MB (0.01%)      | 5888 kB (0.01%)  | 56 kB (0.00%)
       nodes_002              | ~186k | 30 MB (0.01%)      | 25 MB (0.00%)      | 5736 kB (0.01%)  | 8192 bytes (0.00%)
       multipolygon_002       | ~9k   | 23 MB (0.00%)      | 13 MB (0.00%)      | 1480 kB (0.00%)  | 8760 kB (0.03%)
       multipolygon_063       | ~5k   | 21 MB (0.00%)      | 9096 kB (0.00%)    | 808 kB (0.00%)   | 11 MB (0.03%)
       h3_3_bounds_complex    | ~41k  | 17 MB (0.00%)      | 12 MB (0.00%)      | 4832 kB (0.01%)  | 8192 bytes (0.00%)
       osm_file_block         | ~135k | 15 MB (0.00%)      | 15 MB (0.00%)      | 0 bytes (0.00%)  | 
       spatial_ref_sys        | ~9k   | 7280 kB (0.00%)    | 6968 kB (0.00%)    | 304 kB (0.00%)   | 8192 bytes (0.00%)
       key_frequencies        | ~87k  | 4632 kB (0.00%)    | 4624 kB (0.00%)    | 0 bytes (0.00%)  | 8192 bytes (0.00%)
       key_stat_building      | ~23k  | 1112 kB (0.00%)    | 1104 kB (0.00%)    | 0 bytes (0.00%)  | 8192 bytes (0.00%)
       osm_stat_nodes_3_3     | ~14k  | 784 kB (0.00%)     | 776 kB (0.00%)     | 0 bytes (0.00%)  | 8192 bytes (0.00%)
       osm_stat_ways_3_3      | ~14k  | 776 kB (0.00%)     | 768 kB (0.00%)     | 0 bytes (0.00%)  | 8192 bytes (0.00%)
       rel                    | ~7k   | 352 kB (0.00%)     | 352 kB (0.00%)     | 0 bytes (0.00%)  | 
       size_stat              | ~313  | 48 kB (0.00%)      | 40 kB (0.00%)      | 0 bytes (0.00%)  | 8192 bytes (0.00%)
       osm_file_statistics    | ~1    | 8192 bytes (0.00%) | 8192 bytes (0.00%) | 0 bytes (0.00%)  | 

      Подробнее о схеме данных тут и тут.

      Индексы, наверное, по всем колонкам, да?

      Первичный ключ и гео столбцы.


      1. javalin
        30.08.2023 23:45

        Немного не понятно, как работает сортировка и партицирование тут..

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

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


        1. igor_suhorukov Автор
          30.08.2023 23:45

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

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

          Пример с удачным планом для запроса: select * from ways order by scale desc limit 35 offset 350;
          Time: 51,948 ms

          Его план использует обратный скан индексов
                                                                       QUERY PLAN                                                             
          ------------------------------------------------------------------------------------------------------------------------------------
           Limit  (cost=2232962.80..2233007.46 rows=35 width=552)
             ->  Merge Append  (cost=47.27..1112144267.67 rows=871619354 width=552)
                   Sort Key: ways.scale DESC
                   ->  Index Scan Backward using ways_000_scale_idx on ways_000 ways_1  (cost=0.43..10734002.76 rows=8962840 width=563)
                   ->  Index Scan Backward using ways_001_scale_idx on ways_001 ways_2  (cost=0.43..19179046.70 rows=15312682 width=436)
                   ->  Index Scan Backward using ways_002_scale_idx on ways_002 ways_3  (cost=0.44..21248802.48 rows=16906140 width=424)
                   ->  Index Scan Backward using ways_003_scale_idx on ways_003 ways_4  (cost=0.43..14207278.34 rows=12459795 width=464)
                   ->  Index Scan Backward using ways_004_scale_idx on ways_004 ways_5  (cost=0.43..16869097.60 rows=13419629 width=469)
                   ->  Index Scan Backward using ways_005_scale_idx on ways_005 ways_6  (cost=0.43..14634701.00 rows=11664735 width=492)
                   ->  Index Scan Backward using ways_006_scale_idx on ways_006 ways_7  (cost=0.43..12280027.78 rows=9914308 width=529)
                   ->  Index Scan Backward using ways_007_scale_idx on ways_007 ways_8  (cost=0.43..8712729.56 rows=7289608 width=624)
                   ->  Index Scan Backward using ways_008_scale_idx on ways_008 ways_9  (cost=0.43..7133623.17 rows=6034490 width=706)
                   ->  Index Scan Backward using ways_009_scale_idx on ways_009 ways_10  (cost=0.43..9378623.09 rows=7586060 width=603)
                   ->  Index Scan Backward using ways_010_scale_idx on ways_010 ways_11  (cost=0.43..10748305.23 rows=8550632 width=576)
                   ->  Index Scan Backward using ways_011_scale_idx on ways_011 ways_12  (cost=0.43..8398076.53 rows=6753388 width=636)
                   ->  Index Scan Backward using ways_012_scale_idx on ways_012 ways_13  (cost=0.43..5614494.70 rows=4740446 width=780)
                   ->  Index Scan Backward using ways_013_scale_idx on ways_013 ways_14  (cost=0.43..9584317.72 rows=7691123 width=617)
                   ->  Index Scan Backward using ways_014_scale_idx on ways_014 ways_15  (cost=0.43..5510768.28 rows=4464275 width=716)
                   ->  Index Scan Backward using ways_015_scale_idx on ways_015 ways_16  (cost=0.43..8243193.23 rows=6884838 width=653)
                   ->  Index Scan Backward using ways_016_scale_idx on ways_016 ways_17  (cost=0.43..6605525.35 rows=5677561 width=722)
                   ->  Index Scan Backward using ways_017_scale_idx on ways_017 ways_18  (cost=0.43..12340746.92 rows=9849491 width=525)
                   ->  Index Scan Backward using ways_018_scale_idx on ways_018 ways_19  (cost=0.43..10091330.41 rows=8070721 width=575)
                   ->  Index Scan Backward using ways_019_scale_idx on ways_019 ways_20  (cost=0.43..12619485.57 rows=10046626 width=529)
                   ->  Index Scan Backward using ways_020_scale_idx on ways_020 ways_21  (cost=0.43..6916614.30 rows=5628859 width=731)
                   ->  Index Scan Backward using ways_021_scale_idx on ways_021 ways_22  (cost=0.43..9145680.99 rows=8046433 width=582)
                   ->  Index Scan Backward using ways_022_scale_idx on ways_022 ways_23  (cost=0.43..3887688.37 rows=3093161 width=600)
                   ->  Index Scan Backward using ways_023_scale_idx on ways_023 ways_24  (cost=0.43..7682436.49 rows=6110087 width=575)
                   ->  Index Scan Backward using ways_024_scale_idx on ways_024 ways_25  (cost=0.43..10497581.90 rows=8378638 width=579)
                   ->  Index Scan Backward using ways_025_scale_idx on ways_025 ways_26  (cost=0.43..8835745.22 rows=7027353 width=534)
                   ->  Index Scan Backward using ways_026_scale_idx on ways_026 ways_27  (cost=0.43..11585319.96 rows=9369376 width=557)
                   ->  Index Scan Backward using ways_027_scale_idx on ways_027 ways_28  (cost=0.43..4100928.35 rows=3294022 width=526)
                   ->  Index Scan Backward using ways_028_scale_idx on ways_028 ways_29  (cost=0.43..10870554.26 rows=8647001 width=529)
                   ->  Index Scan Backward using ways_029_scale_idx on ways_029 ways_30  (cost=0.43..13852693.93 rows=11017560 width=581)
                   ->  Index Scan Backward using ways_030_scale_idx on ways_030 ways_31  (cost=0.43..4039497.37 rows=3684417 width=595)
                   ->  Index Scan Backward using ways_031_scale_idx on ways_031 ways_32  (cost=0.43..10610566.20 rows=8449412 width=570)
                   ->  Index Scan Backward using ways_032_scale_idx on ways_032 ways_33  (cost=0.43..2586771.55 rows=2058661 width=568)
                   ->  Index Scan Backward using ways_033_scale_idx on ways_033 ways_34  (cost=0.43..9823547.59 rows=7831531 width=656)
                   ->  Index Scan Backward using ways_034_scale_idx on ways_034 ways_35  (cost=0.43..3170543.68 rows=2521667 width=514)
                   ->  Index Scan Backward using ways_035_scale_idx on ways_035 ways_36  (cost=0.43..10272123.78 rows=8178766 width=537)
                   ->  Index Scan Backward using ways_036_scale_idx on ways_036 ways_37  (cost=0.43..9725077.21 rows=7735432 width=521)
                   ->  Index Scan Backward using ways_037_scale_idx on ways_037 ways_38  (cost=0.43..10920908.20 rows=8688039 width=610)
                   ->  Index Scan Backward using ways_038_scale_idx on ways_038 ways_39  (cost=0.43..11229155.87 rows=8999835 width=537)
                   ->  Index Scan Backward using ways_039_scale_idx on ways_039 ways_40  (cost=0.43..8574657.61 rows=6884006 width=544)
                   ->  Index Scan Backward using ways_040_scale_idx on ways_040 ways_41  (cost=0.43..11413728.35 rows=9078911 width=545)
                   ->  Index Scan Backward using ways_041_scale_idx on ways_041 ways_42  (cost=0.43..9372766.97 rows=7620146 width=587)
                   ->  Index Scan Backward using ways_042_scale_idx on ways_042 ways_43  (cost=0.43..12105935.99 rows=9629150 width=547)
                   ->  Index Scan Backward using ways_043_scale_idx on ways_043 ways_44  (cost=0.43..17979991.28 rows=14312375 width=530)
                   ->  Index Scan Backward using ways_044_scale_idx on ways_044 ways_45  (cost=0.43..11237868.26 rows=8952180 width=523)
                   ->  Index Scan Backward using ways_045_scale_idx on ways_045 ways_46  (cost=0.43..13321180.18 rows=10602507 width=513)
                   ->  Index Scan Backward using ways_046_scale_idx on ways_046 ways_47  (cost=0.43..12241542.13 rows=9814522 width=540)
                   ->  Index Scan Backward using ways_047_scale_idx on ways_047 ways_48  (cost=0.43..14093608.13 rows=11217847 width=566)
                   ->  Index Scan Backward using ways_048_scale_idx on ways_048 ways_49  (cost=0.43..11770767.77 rows=9398580 width=550)
                   ->  Index Scan Backward using ways_049_scale_idx on ways_049 ways_50  (cost=0.43..11082052.03 rows=8863425 width=598)
                   ->  Index Scan Backward using ways_050_scale_idx on ways_050 ways_51  (cost=0.43..9361022.84 rows=7534409 width=569)
                   ->  Index Scan Backward using ways_051_scale_idx on ways_051 ways_52  (cost=0.56..25932871.02 rows=20787168 width=543)
                   ->  Index Scan Backward using ways_052_scale_idx on ways_052 ways_53  (cost=0.43..17771993.12 rows=14157957 width=526)
                   ->  Index Scan Backward using ways_053_scale_idx on ways_053 ways_54  (cost=0.43..11884060.47 rows=9465129 width=571)
                   ->  Index Scan Backward using ways_054_scale_idx on ways_054 ways_55  (cost=0.43..11702717.53 rows=9415987 width=543)
                   ->  Index Scan Backward using ways_055_scale_idx on ways_055 ways_56  (cost=0.43..9286006.28 rows=7391109 width=618)
                   ->  Index Scan Backward using ways_056_scale_idx on ways_056 ways_57  (cost=0.43..9262029.47 rows=7367119 width=607)
                   ->  Index Scan Backward using ways_057_scale_idx on ways_057 ways_58  (cost=0.43..10123432.47 rows=8069411 width=592)
                   ->  Index Scan Backward using ways_058_scale_idx on ways_058 ways_59  (cost=0.43..7755095.67 rows=6234346 width=670)
                   ->  Index Scan Backward using ways_059_scale_idx on ways_059 ways_60  (cost=0.43..7281365.72 rows=6169332 width=661)
                   ->  Index Scan Backward using ways_060_scale_idx on ways_060 ways_61  (cost=0.43..10122877.37 rows=8162266 width=596)
                   ->  Index Scan Backward using ways_061_scale_idx on ways_061 ways_62  (cost=0.43..5430863.15 rows=4370853 width=624)
                   ->  Index Scan Backward using ways_062_scale_idx on ways_062 ways_63  (cost=0.43..8251841.72 rows=6963772 width=631)
                   ->  Index Scan Backward using ways_063_scale_idx on ways_063 ways_64  (cost=0.43..2891595.30 rows=2627670 width=633)
                   ->  Index Scan Backward using ways_064_scale_idx on ways_064 ways_65  (cost=0.43..9784960.02 rows=7838608 width=568)
                   ->  Index Scan Backward using ways_065_scale_idx on ways_065 ways_66  (cost=0.43..7539174.68 rows=6152933 width=596)
                   ->  Index Scan Backward using ways_066_scale_idx on ways_066 ways_67  (cost=0.43..9173091.27 rows=7297383 width=612)
                   ->  Index Scan Backward using ways_067_scale_idx on ways_067 ways_68  (cost=0.43..10246979.92 rows=8247090 width=563)
                   ->  Index Scan Backward using ways_068_scale_idx on ways_068 ways_69  (cost=0.43..7200272.87 rows=5745960 width=659)
                   ->  Index Scan Backward using ways_069_scale_idx on ways_069 ways_70  (cost=0.43..9138136.04 rows=7344976 width=619)
                   ->  Index Scan Backward using ways_070_scale_idx on ways_070 ways_71  (cost=0.43..10268320.27 rows=8229844 width=578)
                   ->  Index Scan Backward using ways_071_scale_idx on ways_071 ways_72  (cost=0.43..11453918.01 rows=9118260 width=527)
                   ->  Index Scan Backward using ways_072_scale_idx on ways_072 ways_73  (cost=0.43..9831262.79 rows=8141135 width=506)
                   ->  Index Scan Backward using ways_073_scale_idx on ways_073 ways_74  (cost=0.43..11683915.42 rows=9311915 width=501)
                   ->  Index Scan Backward using ways_074_scale_idx on ways_074 ways_75  (cost=0.43..13889599.06 rows=11399777 width=503)
                   ->  Index Scan Backward using ways_075_scale_idx on ways_075 ways_76  (cost=0.43..11851797.07 rows=9475185 width=545)
                   ->  Index Scan Backward using ways_076_scale_idx on ways_076 ways_77  (cost=0.43..8876670.05 rows=7083628 width=580)
                   ->  Index Scan Backward using ways_077_scale_idx on ways_077 ways_78  (cost=0.43..10057654.63 rows=8087229 width=572)
                   ->  Index Scan Backward using ways_078_scale_idx on ways_078 ways_79  (cost=0.43..9037339.05 rows=7315559 width=596)
                   ->  Index Scan Backward using ways_079_scale_idx on ways_079 ways_80  (cost=0.43..8871341.88 rows=7838637 width=604)
                   ->  Index Scan Backward using ways_080_scale_idx on ways_080 ways_81  (cost=0.43..13198230.43 rows=10672667 width=506)
                   ->  Index Scan Backward using ways_081_scale_idx on ways_081 ways_82  (cost=0.43..17327717.40 rows=13911841 width=453)
                   ->  Index Scan Backward using ways_082_scale_idx on ways_082 ways_83  (cost=0.43..11884679.58 rows=9715200 width=522)
                   ->  Index Scan Backward using ways_083_scale_idx on ways_083 ways_84  (cost=0.43..9718215.08 rows=7802347 width=589)
                   ->  Index Scan Backward using ways_084_scale_idx on ways_084 ways_85  (cost=0.43..9692886.61 rows=7981013 width=558)
                   ->  Index Scan Backward using ways_085_scale_idx on ways_085 ways_86  (cost=0.43..8687789.22 rows=7146330 width=584)
                   ->  Index Scan Backward using ways_086_scale_idx on ways_086 ways_87  (cost=0.43..9558622.99 rows=7626922 width=594)
                   ->  Index Scan Backward using ways_087_scale_idx on ways_087 ways_88  (cost=0.43..11189552.87 rows=9002111 width=558)
                   ->  Index Scan Backward using ways_088_scale_idx on ways_088 ways_89  (cost=0.43..11073334.61 rows=8838901 width=554)
                   ->  Index Scan Backward using ways_089_scale_idx on ways_089 ways_90  (cost=0.43..13255718.16 rows=11195281 width=498)
                   ->  Index Scan Backward using ways_090_scale_idx on ways_090 ways_91  (cost=0.43..15113771.53 rows=12045927 width=487)
                   ->  Index Scan Backward using ways_091_scale_idx on ways_091 ways_92  (cost=0.43..14039524.73 rows=11219574 width=502)
                   ->  Index Scan Backward using ways_092_scale_idx on ways_092 ways_93  (cost=0.43..14764558.05 rows=11840971 width=478)
                   ->  Index Scan Backward using ways_093_scale_idx on ways_093 ways_94  (cost=0.43..15604254.12 rows=12517653 width=474)
                   ->  Index Scan Backward using ways_094_scale_idx on ways_094 ways_95  (cost=0.43..11551367.57 rows=9571524 width=522)
                   ->  Index Scan Backward using ways_095_scale_idx on ways_095 ways_96  (cost=0.43..16365206.54 rows=13028483 width=452)
                   ->  Index Scan Backward using ways_096_scale_idx on ways_096 ways_97  (cost=0.43..12097474.19 rows=9623347 width=499)
                   ->  Index Scan Backward using ways_097_scale_idx on ways_097 ways_98  (cost=0.43..13315421.48 rows=10617219 width=514)
                   ->  Index Scan Backward using ways_098_scale_idx on ways_098 ways_99  (cost=0.43..14050141.10 rows=11553492 width=482)
                   ->  Index Scan Backward using ways_099_scale_idx on ways_099 ways_100  (cost=0.43..11106929.91 rows=9230831 width=498)
                   ->  Index Scan Backward using ways_32767_scale_idx on ways_32767 ways_101  (cost=0.43..2129725.90 rows=1719786 width=1921)
           JIT:
             Functions: 2
             Options: Inlining true, Optimization true, Expressions true, Deforming true
          (107 rows)

          Этот же план с человечным форматированием

          PostgreSQL не выдаст неверный результат, а вот "супер пупер производительная СУБД убийца PostgreSQL" вполне может поступить как вы описали. Или упасть в процессе выполнения с OOM/segfault или сказать что ваш запрос не верный "мы так не умеем как вы хотите", докупите еще GPGPU / RAM / блейдов / Infiniband и попробуйте еще раз.

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


          1. poxvuibr
            30.08.2023 23:45

            PostgreSQL не выдаст неверный результат, а вот "супер пупер производительная СУБД убийца PostgreSQL" вполне может поступить как вы описали

            Это ж будет баг в реализации sql. Я не слышал про СУБД с таким поведением. Они правда есть или это предположение?

            Пример с удачным планом для запроса:
            select * from ways order by scale desc limit 35 offset 350;
            Time: 51,948 ms

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


            1. igor_suhorukov Автор
              30.08.2023 23:45

              Это ж будет баг в реализации sql. Я не слышал про СУБД с таким поведением. Они правда есть или это предположение?

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

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

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


      1. poxvuibr
        30.08.2023 23:45

        В простом случае да [используется limit ... offset ] .

        Судя по коду у вас как раз такой случай. И я вас так понял, что всё работает с приемлемой скоростью. Или я неправильно понял?

        Если хочется быстрее, то конечно надо переходить на ctid

        А можно использовать ctid вместе с сортировкой по произвольной колонке?

        Первичный ключ и гео столбцы [по ним есть индексы]

        На скриншоте можно сортировать по любой колонке. Скорость получается приемлемой несмотря на отсутствие индексов?

        И ещё на скриншоте оффсеты совсем маленькие. 352, например. И они меняются на единицу каждый раз. Такое впечатление, что каждая строка загружается отдельным запросом с limit... offset . Страницы из середины или конца больших таблиц при этом загружаются быстро?


        1. igor_suhorukov Автор
          30.08.2023 23:45

          И я вас так понял, что всё работает с приемлемой скоростью.

          Укажите где я говорил, что произвольные сортировки с миллионными офсетами работают с приемлемой скоростью?

          И ещё на скриншоте оффсеты совсем маленькие. 352, например. И они меняются на единицу каждый раз.

          Есть и страница 1011, под спойлером "Работает!" в ответе вам. С таймингами в отладчике firefox.

          Это же открытый код. Вдруг есть идея как сделать так, чтобы выдавало за 1-2мс на произвольной сортировке и базе, то ваш вклад в код приветствуется!


          1. poxvuibr
            30.08.2023 23:45
            +2

            Укажите где я говорил, что произвольные сортировки с миллионными офсетами работают с приемлемой скоростью?

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

            Есть и страница 1011, под спойлером "Работает!" в ответе вам. С таймингами в отладчике firefox.

            Да, странно, что номер страницы 1011, а offset - 352 . Поэтому я и уточняю. Наверное отладчик firefox показывает загрузку какой-то другой страницы? Но вот то, что строки по одной загружаются я правильно понял?

            Это же открытый код.

            Я чего-то ссылки в статье не нахожу. Плохо ищу?

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

            Если сделать индексы по каждой колонке, сортировать только по одной колонке, не накладывать фильтры и убрать limit ... offset , то можно . А так - я не знаю как. Вот прочитал что вы написали про большие таблицы, посмотрел на возможность сортировки в интерфейсах и подумал - вдруг вы знаете.


            1. igor_suhorukov Автор
              30.08.2023 23:45

              Похоже дело в ваших догадках, "чтении" моих мыслей, да прочтении между строчек! На статье не указан хаб "Высокая производительность", теги "low latency" и уж тем более за время выполнения произвольных запросов с сортировкой по произвольному столбцу я не отвечаю. Датасет легко воспроизводим каждым: дампы планеты OSM доступны, как и утилита загрузки.

              У меня проблем с производительностью и не было. Объемы своих данных показал, мои задачи Tabulator успешно решает, таблицы из этой базы данных успешно открывает.

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

              Я чего-то ссылки в статье не нахожу. Плохо ищу?

              Добавил заголовок "Код PostgREST-Tabulator", теперь точно найдете. Исходники не по ссылке, а прямо в этой статье.

              Если сделать индексы по каждой колонке, сортировать только по одной
              колонке, не накладывать фильтры и убрать limit ... offset , то можно .

              Дерзайте! Исходники доступны.

              Да, странно, что номер страницы 1011, а offset - 352 . Поэтому я и уточняю.

              Видимо колонка в отладчике с адресом не до конца развернута.

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

              Основная тема публикации о создании интерфейса в браузере, а не оптимизации произвольного запроса в PostgreSQL.


  1. pin2t
    30.08.2023 23:45

    Я думал будет про то как ChatGPT генерирует таблицы за вас. Как-то не в тренде слвсем


    1. igor_suhorukov Автор
      30.08.2023 23:45

      - Внучек возьми вот скриптов домой.

      - Деда, да нас этих скриптов дома, как #$*на.

      - Так это все нейросети сгенерировали, а дед сам писал.

      Попробовал чтобы ChatGPT написал детекторы и настраивал форматтеры таблицы для ссылок и изображений. Уже 10 минут закидываю промпты и поправляю результаты...

      не осилила нейросетка поправить готовый компонент