[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-postgresql-18-obzor-uuidv7-virtualnye-kolonki-dvizhok-vvoda-vyvoda":3},{"article":4,"author":51},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":16,"meta_description":17,"focus_keyword":18,"og_image":19,"canonical_url":19,"robots_meta":20,"created_at":15,"updated_at":15,"tags":21,"category_name":31,"related_articles":32},"df000000-0000-0000-0000-000000000011","a0000000-0000-0000-0000-000000000016","PostgreSQL 18: полный обзор — uuidv7, виртуальные колонки и новый движок ввода-вывода","postgresql-18-obzor-uuidv7-virtualnye-kolonki-dvizhok-vvoda-vyvoda","PostgreSQL 18 вышел в сентябре 2025 года с революционными возможностями: новый асинхронный движок ввода-вывода увеличивает пропускную способность чтения до 3 раз, нативная функция uuidv7() для генерации идентификаторов с временной меткой, виртуальные вычисляемые колонки, аутентификация через OAuth и темпоральные ограничения.","## Краткий ответ\n\nPostgreSQL 18 — самый значительный релиз со времён PostgreSQL 12, который представил подключаемые методы доступа к таблицам. Ключевые возможности — переписанная асинхронная подсистема ввода-вывода, нативная генерация uuidv7(), виртуальные вычисляемые колонки и темпоральные ограничения — закрывают давние пробелы, которые ранее требовали расширений, обходных решений или совершенно других баз данных. Если вы используете PostgreSQL 17 в продакшене, начинайте планировать обновление прямо сейчас. Путь миграции прост, а прирост производительности от нового движка ввода-вывода оправдывает усилия.\n\n## Контекст релиза\n\nPostgreSQL 18 вышел 18 сентября 2025 года в рамках ежегодного цикла релизов. Цикл разработки был заметно длиннее обычного из-за переписывания подсистемы ввода-вывода, потребовавшего одновременных изменений в менеджере буферов, писателе WAL и подсистеме вакуума. Более 380 контрибьюторов внесли код в этот релиз — рекордное количество в истории PostgreSQL.\n\nРелиз выходит в момент, когда PostgreSQL стал выбором по умолчанию для новых проектов. По данным Stack Overflow Developer Survey 2025, PostgreSQL занял первое место как наиболее используемая база данных третий год подряд с долей 49,1%, опережая MySQL (40,2%) и SQLite (32,6%).\n\n## Новая асинхронная подсистема ввода-вывода\n\nСамое значительное изменение в PostgreSQL 18 — переписанная подсистема ввода-вывода. В предыдущих версиях PostgreSQL использовал синхронный однопоточный ввод-вывод для чтения страниц данных с диска. Новая подсистема реализует настоящий асинхронный ввод-вывод через io_uring на Linux и kqueue на macOS\u002FBSD, с фолбэком на воркер-потоки на других платформах.\n\n### Как это работает\n\nТрадиционный путь ввода-вывода PostgreSQL был прост: когда запросу требовалась страница, отсутствующая в shared_buffers, бэкенд-процесс выполнял синхронный вызов read() и блокировался до получения данных от ядра. Это означало, что последовательное сканирование таблицы на 100 ГБ было ограничено однопоточным вводом-выводом, независимо от количества NVMe-дисков.\n\nНовая подсистема группирует запросы ввода-вывода. Когда исполнитель определяет, что ему потребуются страницы 1, 5, 12 и 47 (например, при bitmap heap scan), он отправляет все четыре запроса на чтение в ядро одновременно через io_uring. Ядро обрабатывает их параллельно через несколько очередей NVMe, и результаты поступают асинхронно.\n\n### Влияние на производительность\n\nБенчмарки на стандартной конфигурации NVMe SSD (4x NVMe в RAID-0):\n\n| Нагрузка | PG 17 | PG 18 | Улучшение |\n|----------|-------|-------|----------|\n| Последовательное сканирование (холодный кэш) | 1,2 ГБ\u002Fс | 3,4 ГБ\u002Fс | 2,8x |\n| Bitmap heap scan | 890 МБ\u002Fс | 2,6 ГБ\u002Fс | 2,9x |\n| VACUUM (большая таблица) | 45 мин | 18 мин | 2,5x |\n| Параллельное построение индекса | 12 мин | 5,5 мин | 2,2x |\n| Пропускная способность записи WAL | 1,8 ГБ\u002Fс | 3,1 ГБ\u002Fс | 1,7x |\n\nУлучшение наиболее заметно для нагрузок, ограниченных вводом-выводом, на современных NVMe-накопителях. Если ваша база данных целиком помещается в shared_buffers, вы увидите минимальные изменения. Если рабочий набор превышает объём RAM — что типично для аналитических нагрузок, данных временных рядов и больших JSONB-хранилищ — прирост трансформирующий.\n\n### Конфигурация\n\nНовая подсистема ввода-вывода включена по умолчанию. Два новых параметра GUC управляют её поведением:\n\n```sql\n-- Максимальное количество параллельных запросов ввода-вывода на бэкенд (по умолчанию: 128)\nSET io_max_concurrency = 128;\n\n-- Метод ввода-вывода: 'io_uring', 'kqueue', 'worker' (определяется автоматически)\nSET io_method = 'io_uring';\n```\n\nДля большинства установок значения по умолчанию оптимальны. Увеличьте `io_max_concurrency`, если у вас массивы NVMe высокого класса (8+ дисков) и нагрузки с очень большими последовательными сканированиями.\n\n## uuidv7(): UUID с временной меткой нативно\n\nPostgreSQL 18 добавляет функцию `uuidv7()`, генерирующую UUID версии 7 по RFC 9562. Эту возможность сообщество запрашивало годами — ранее требовались расширения `pgcrypto` или `uuid-ossp` в сочетании с пользовательскими функциями.\n\n### Почему uuidv7 важен\n\nUUIDv4 (случайный) — наиболее распространённая версия UUID в качестве первичного ключа. У него есть критический недостаток для производительности баз данных: случайные UUID создают случайные паттерны ввода-вывода на B-tree индексах. При вставке новой строки с первичным ключом UUIDv4 листовая страница индекса, куда она попадёт, по сути случайна, что вызывает промахи кэша и усиление записи.\n\nUUIDv7 кодирует временную метку Unix в первых 48 битах, за которыми следуют случайные биты для уникальности. Это означает, что значения UUIDv7 монотонно возрастают со временем, как BIGSERIAL, но при этом глобально уникальны без координации.\n\n```sql\n-- Генерация UUIDv7\nSELECT uuidv7();\n-- Результат: 019271a4-5b00-7123-8456-789abcdef012\n\n-- Извлечение временной метки из UUIDv7\nSELECT uuid_extract_timestamp('019271a4-5b00-7123-8456-789abcdef012');\n-- Результат: 2025-09-18 14:30:00+00\n\n-- Использование в качестве первичного ключа по умолчанию\nCREATE TABLE events (\n    id UUID PRIMARY KEY DEFAULT uuidv7(),\n    event_type TEXT NOT NULL,\n    payload JSONB,\n    created_at TIMESTAMPTZ DEFAULT now()\n);\n```\n\n### Сравнение производительности\n\nНа таблице со 100 миллионами строк:\n\n| Метрика | UUIDv4 PK | UUIDv7 PK | BIGSERIAL PK |\n|---------|-----------|-----------|---------------|\n| Скорость вставки (строк\u002Fсек) | 45 000 | 112 000 | 125 000 |\n| Размер индекса | 4,2 ГБ | 4,2 ГБ | 2,1 ГБ |\n| Cache hit ratio индекса | 67% | 94% | 96% |\n| Задержка точечного поиска (p99) | 2,1 мс | 0,4 мс | 0,3 мс |\n\nUUIDv7 достигает производительности вставки, близкой к BIGSERIAL, сохраняя при этом глобальную уникальность. Для распределённых систем, микросервисов и любых архитектур, где идентификаторы генерируются на стороне приложения без координации с базой данных, uuidv7 теперь является очевидным выбором по умолчанию.\n\n## Виртуальные вычисляемые колонки\n\nPostgreSQL поддерживает хранимые вычисляемые колонки с версии 12. PostgreSQL 18 добавляет виртуальные вычисляемые колонки — вычисляемые при чтении, без хранения на диске.\n\n```sql\nCREATE TABLE products (\n    id UUID PRIMARY KEY DEFAULT uuidv7(),\n    name TEXT NOT NULL,\n    price_cents INTEGER NOT NULL,\n    tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.11,\n    -- Виртуальная: вычисляется при чтении, нулевая стоимость хранения\n    price_with_tax NUMERIC GENERATED ALWAYS AS (price_cents * (1 + tax_rate)) VIRTUAL,\n    -- Хранимая: вычисляется при записи, занимает место на диске\n    search_vector TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', name)) STORED\n);\n```\n\n### Когда использовать VIRTUAL, а когда STORED\n\n**Используйте VIRTUAL, когда:**\n- Вычисление дешёвое (арифметика, конкатенация строк, приведение типов)\n- Вы хотите нулевых накладных расходов на хранение\n- Колонка запрашивается редко или только вместе со строкой\n- Вы хотите, чтобы значение всегда отражало текущие данные\n\n**Используйте STORED, когда:**\n- Вычисление дорогое (векторы полнотекстового поиска, сложная экстракция JSON)\n- Вам нужно индексировать вычисляемую колонку\n- Колонка часто используется в WHERE или JOIN\n\nВиртуальные колонки нельзя индексировать напрямую, так как на диске нет данных для индексации. Если вам нужно часто фильтровать или сортировать по вычисляемому значению, используйте STORED.\n\n## Поддержка OAuth-аутентификации\n\nPostgreSQL 18 добавляет OAuth 2.0 \u002F OpenID Connect как нативный метод аутентификации в pg_hba.conf. Это позволяет пользователям аутентифицироваться через провайдеры идентификации, такие как Okta, Auth0, Azure AD или Keycloak, без пользовательских PAM-модулей или проксирования через LDAP.\n\n```\n# pg_hba.conf\nhost    all    all    0.0.0.0\u002F0    oauth issuer=\"https:\u002F\u002Fauth.company.com\" client_id=\"pg-prod\"\n```\n\nПроцесс работает следующим образом:\n\n1. Клиент подключается к PostgreSQL и получает OAuth-вызов\n2. Клиент получает JWT access-токен от настроенного провайдера идентификации\n3. Клиент отправляет токен PostgreSQL\n4. PostgreSQL проверяет подпись токена, издателя, аудиторию и срок действия\n5. Claim `sub` (subject) маппится на роль PostgreSQL\n\nЭто особенно ценно для организаций, стандартизировавших OAuth\u002FOIDC для всей аутентификации сервисов. Доступом к базе данных теперь можно управлять через тот же провайдер идентификации, что и для приложений, с теми же политиками MFA, длительностью сессий и журналами аудита.\n\n## Темпоральные ограничения\n\nPostgreSQL 18 представляет темпоральные PRIMARY KEY, UNIQUE и FOREIGN KEY ограничения для таблиц с периодическими колонками. Это привносит возможности SQL:2011 для работы с темпоральными данными, позволяя моделировать битемпоральные данные без принудительного контроля на уровне приложения.\n\n```sql\nCREATE TABLE employee_departments (\n    employee_id INTEGER NOT NULL,\n    department_id INTEGER NOT NULL,\n    valid_from DATE NOT NULL,\n    valid_to DATE NOT NULL,\n    PERIOD FOR valid_period (valid_from, valid_to),\n    -- Темпоральный PK: запрет пересекающихся периодов для одного сотрудника\n    PRIMARY KEY (employee_id, valid_period WITHOUT OVERLAPS)\n);\n\nCREATE TABLE salary_history (\n    employee_id INTEGER NOT NULL,\n    salary NUMERIC NOT NULL,\n    valid_from DATE NOT NULL,\n    valid_to DATE NOT NULL,\n    PERIOD FOR valid_period (valid_from, valid_to),\n    -- Темпоральный FK: записи о зарплате должны ссылаться на валидное назначение в отдел\n    FOREIGN KEY (employee_id, PERIOD valid_period)\n        REFERENCES employee_departments (employee_id, PERIOD valid_period)\n);\n```\n\nТемпоральные ограничения предотвращают пересечение периодов для одной и той же сущности — распространённый источник ошибок в приложениях, управляющих данными с временными диапазонами (подписки, ценовые уровни, назначения ролей, резервирование).\n\n## OLD\u002FNEW в RETURNING\n\nPostgreSQL 18 позволяет обращаться к значениям OLD и NEW в RETURNING-выражениях UPDATE и DELETE. Это устраняет необходимость в CTE или отдельных запросах, когда нужно и предыдущее, и новое состояние изменённых строк.\n\n```sql\n-- Обновить цены и вернуть старые и новые значения\nUPDATE products\nSET price_cents = price_cents * 1.1\nWHERE category = 'electronics'\nRETURNING\n    id,\n    OLD.price_cents AS previous_price,\n    NEW.price_cents AS updated_price,\n    name;\n```\n\nЭто незаменимо для журналирования изменений, захвата изменений данных (CDC) и любых процессов, где необходимо знать, что именно изменилось.\n\n## Skip-Scan для составных B-tree индексов\n\nPostgreSQL 18 вводит оптимизацию skip-scan для составных B-tree индексов. Это позволяет планировщику эффективно использовать составной индекс, даже когда ведущая колонка отсутствует в WHERE.\n\n```sql\n-- Индекс по (country, city, population)\nCREATE INDEX idx_locations ON locations (country, city, population);\n\n-- PG 17: полное сканирование индекса (не может эффективно использовать индекс без 'country')\n-- PG 18: skip-scan (перескакивает между уникальными значениями 'country')\nSELECT * FROM locations WHERE city = 'Jakarta';\n```\n\nSkip-scan работает путём определения уникальных значений в ведущих колонках и выполнения серии целевых поисков для каждого значения. Для колонок с низкой кардинальностью (country, status, type) это значительно быстрее полного сканирования индекса.\n\nSkip-scan устраняет множество случаев, когда ранее требовался отдельный одноколоночный индекс, снижая накладные расходы на обслуживание индексов и хранение.\n\n## Руководство по миграции: PostgreSQL 17 → 18\n\n### Чек-лист перед обновлением\n\n1. **Проверьте совместимость расширений.** Запустите `SELECT * FROM pg_available_extensions;` на тестовом экземпляре PG 18. Большинство популярных расширений (PostGIS, pgvector, pg_stat_statements) получили PG 18-совместимые релизы в течение 2 недель после выпуска.\n\n2. **Проверьте pg_hba.conf.** Новый метод OAuth аддитивен — существующие конфигурации продолжают работать без изменений.\n\n3. **Протестируйте производительность ввода-вывода.** Новая асинхронная подсистема включена по умолчанию. Запустите стандартный набор бенчмарков на тестовом экземпляре.\n\n4. **Проверьте вычисляемые колонки.** Если планируете конвертировать хранимые вычисляемые колонки в виртуальные, убедитесь, что от них не зависят индексы.\n\n5. **Протестируйте запросы приложения.** Оптимизация skip-scan может изменить планы запросов. Проверьте `EXPLAIN ANALYZE` для критических запросов.\n\n### Методы обновления\n\n**pg_upgrade (рекомендуется для большинства):**\n```bash\n# Остановить старый сервер\npg_ctl -D \u002Fvar\u002Flib\u002Fpostgresql\u002F17\u002Fdata stop\n\n# Выполнить обновление\npg_upgrade \\\n  --old-datadir=\u002Fvar\u002Flib\u002Fpostgresql\u002F17\u002Fdata \\\n  --new-datadir=\u002Fvar\u002Flib\u002Fpostgresql\u002F18\u002Fdata \\\n  --old-bindir=\u002Fusr\u002Flib\u002Fpostgresql\u002F17\u002Fbin \\\n  --new-bindir=\u002Fusr\u002Flib\u002Fpostgresql\u002F18\u002Fbin \\\n  --link  # Жёсткие ссылки для скорости\n\n# Запустить новый сервер\npg_ctl -D \u002Fvar\u002Flib\u002Fpostgresql\u002F18\u002Fdata start\n\n# Обновить статистику\nvacuumdb --all --analyze-in-stages\n```\n\n**Логическая репликация (для нулевого простоя):**\nНастройте логическую репликацию с PG 17 на PG 18, дождитесь синхронизации, затем переключите строку подключения приложения. Этот подход сложнее, но позволяет откат переключением обратно на PG 17.\n\n**Управляемые сервисы:** AWS RDS, Google Cloud SQL, Azure Database и Neon поддерживают обновление мажорных версий с минимальным простоем.\n\n### Задачи после обновления\n\n1. Запустите `ANALYZE` на всех таблицах для обновления статистики планировщика\n2. Проверьте `pg_stat_io` для подтверждения активности асинхронного ввода-вывода\n3. Замените генераторы UUIDv4 на uuidv7() где уместно\n4. Оцените возможность конвертации хранимых колонок в VIRTUAL\n5. Мониторьте планы запросов первую неделю — skip-scan может изменить планы\n\n## FAQ\n\n### Готов ли PostgreSQL 18 к продакшену?\n\nДа. PostgreSQL следует строгому процессу релиза с несколькими фазами бета- и RC-версий. Релиз .0 является продакшен-качеством. Тем не менее, ожидание патч-релиза .1 (обычно через 2-3 месяца после .0) — распространённая и разумная стратегия для организаций с низкой терпимостью к рискам.\n\n### Стоит ли переходить с UUIDv4 на UUIDv7 для существующих таблиц?\n\nДля новых таблиц используйте uuidv7() по умолчанию. Для существующих таблиц с первичными ключами UUIDv4 стоимость миграции (перезапись всей таблицы и всех ссылающихся внешних ключей) редко оправдывает выгоду, если только у вас нет измеримых проблем с раздуванием индекса или промахами кэша.\n\n### Требует ли новый движок ввода-вывода изменений в ядре?\n\nПоддержка io_uring требует ядра Linux 5.10 или новее (выпущено в декабре 2020). Если ваше ядро старше, PostgreSQL 18 использует фолбэк на воркер-потоки, который всё равно обеспечивает улучшения по сравнению с синхронным вводом-выводом PG 17, но менее значительные.\n\n### Можно ли использовать виртуальные колонки с pgvector?\n\nНе напрямую. Эмбеддинги pgvector обычно хранятся, а не вычисляются, так как генерация эмбеддингов требует вызова внешней модели. Однако можно использовать виртуальную колонку для производных метрик вроде `vector_dims(embedding)` или `l2_distance(embedding, reference_vector)`.\n\n### Как темпоральные ограничения работают с партиционированием?\n\nТемпоральные ограничения работают с декларативным партиционированием. Можно партиционировать таблицу по диапазону периодической колонки и применять темпоральные PRIMARY KEY. Проверка ограничений учитывает партиции — пересечения проверяются по всем партициям.\n\n### Что с улучшениями MERGE?\n\nPostgreSQL 18 расширяет оператор MERGE поддержкой RETURNING, завершая набор возможностей, представленный в PG 15. Теперь можно использовать `MERGE ... RETURNING *` для получения затронутых строк, аналогично INSERT\u002FUPDATE\u002FDELETE RETURNING.","\u003Ch2 id=\"\">Краткий ответ\u003C\u002Fh2>\n\u003Cp>PostgreSQL 18 — самый значительный релиз со времён PostgreSQL 12, который представил подключаемые методы доступа к таблицам. Ключевые возможности — переписанная асинхронная подсистема ввода-вывода, нативная генерация uuidv7(), виртуальные вычисляемые колонки и темпоральные ограничения — закрывают давние пробелы, которые ранее требовали расширений, обходных решений или совершенно других баз данных. Если вы используете PostgreSQL 17 в продакшене, начинайте планировать обновление прямо сейчас. Путь миграции прост, а прирост производительности от нового движка ввода-вывода оправдывает усилия.\u003C\u002Fp>\n\u003Ch2 id=\"\">Контекст релиза\u003C\u002Fh2>\n\u003Cp>PostgreSQL 18 вышел 18 сентября 2025 года в рамках ежегодного цикла релизов. Цикл разработки был заметно длиннее обычного из-за переписывания подсистемы ввода-вывода, потребовавшего одновременных изменений в менеджере буферов, писателе WAL и подсистеме вакуума. Более 380 контрибьюторов внесли код в этот релиз — рекордное количество в истории PostgreSQL.\u003C\u002Fp>\n\u003Cp>Релиз выходит в момент, когда PostgreSQL стал выбором по умолчанию для новых проектов. По данным Stack Overflow Developer Survey 2025, PostgreSQL занял первое место как наиболее используемая база данных третий год подряд с долей 49,1%, опережая MySQL (40,2%) и SQLite (32,6%).\u003C\u002Fp>\n\u003Ch2 id=\"\">Новая асинхронная подсистема ввода-вывода\u003C\u002Fh2>\n\u003Cp>Самое значительное изменение в PostgreSQL 18 — переписанная подсистема ввода-вывода. В предыдущих версиях PostgreSQL использовал синхронный однопоточный ввод-вывод для чтения страниц данных с диска. Новая подсистема реализует настоящий асинхронный ввод-вывод через io_uring на Linux и kqueue на macOS\u002FBSD, с фолбэком на воркер-потоки на других платформах.\u003C\u002Fp>\n\u003Ch3>Как это работает\u003C\u002Fh3>\n\u003Cp>Традиционный путь ввода-вывода PostgreSQL был прост: когда запросу требовалась страница, отсутствующая в shared_buffers, бэкенд-процесс выполнял синхронный вызов read() и блокировался до получения данных от ядра. Это означало, что последовательное сканирование таблицы на 100 ГБ было ограничено однопоточным вводом-выводом, независимо от количества NVMe-дисков.\u003C\u002Fp>\n\u003Cp>Новая подсистема группирует запросы ввода-вывода. Когда исполнитель определяет, что ему потребуются страницы 1, 5, 12 и 47 (например, при bitmap heap scan), он отправляет все четыре запроса на чтение в ядро одновременно через io_uring. Ядро обрабатывает их параллельно через несколько очередей NVMe, и результаты поступают асинхронно.\u003C\u002Fp>\n\u003Ch3>Влияние на производительность\u003C\u002Fh3>\n\u003Cp>Бенчмарки на стандартной конфигурации NVMe SSD (4x NVMe в RAID-0):\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Нагрузка\u003C\u002Fth>\u003Cth>PG 17\u003C\u002Fth>\u003Cth>PG 18\u003C\u002Fth>\u003Cth>Улучшение\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>Последовательное сканирование (холодный кэш)\u003C\u002Ftd>\u003Ctd>1,2 ГБ\u002Fс\u003C\u002Ftd>\u003Ctd>3,4 ГБ\u002Fс\u003C\u002Ftd>\u003Ctd>2,8x\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Bitmap heap scan\u003C\u002Ftd>\u003Ctd>890 МБ\u002Fс\u003C\u002Ftd>\u003Ctd>2,6 ГБ\u002Fс\u003C\u002Ftd>\u003Ctd>2,9x\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>VACUUM (большая таблица)\u003C\u002Ftd>\u003Ctd>45 мин\u003C\u002Ftd>\u003Ctd>18 мин\u003C\u002Ftd>\u003Ctd>2,5x\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Параллельное построение индекса\u003C\u002Ftd>\u003Ctd>12 мин\u003C\u002Ftd>\u003Ctd>5,5 мин\u003C\u002Ftd>\u003Ctd>2,2x\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Пропускная способность записи WAL\u003C\u002Ftd>\u003Ctd>1,8 ГБ\u002Fс\u003C\u002Ftd>\u003Ctd>3,1 ГБ\u002Fс\u003C\u002Ftd>\u003Ctd>1,7x\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>Улучшение наиболее заметно для нагрузок, ограниченных вводом-выводом, на современных NVMe-накопителях. Если ваша база данных целиком помещается в shared_buffers, вы увидите минимальные изменения. Если рабочий набор превышает объём RAM — что типично для аналитических нагрузок, данных временных рядов и больших JSONB-хранилищ — прирост трансформирующий.\u003C\u002Fp>\n\u003Ch3>Конфигурация\u003C\u002Fh3>\n\u003Cp>Новая подсистема ввода-вывода включена по умолчанию. Два новых параметра GUC управляют её поведением:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">-- Максимальное количество параллельных запросов ввода-вывода на бэкенд (по умолчанию: 128)\nSET io_max_concurrency = 128;\n\n-- Метод ввода-вывода: 'io_uring', 'kqueue', 'worker' (определяется автоматически)\nSET io_method = 'io_uring';\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Для большинства установок значения по умолчанию оптимальны. Увеличьте \u003Ccode>io_max_concurrency\u003C\u002Fcode>, если у вас массивы NVMe высокого класса (8+ дисков) и нагрузки с очень большими последовательными сканированиями.\u003C\u002Fp>\n\u003Ch2 id=\"uuidv7-uuid\">uuidv7(): UUID с временной меткой нативно\u003C\u002Fh2>\n\u003Cp>PostgreSQL 18 добавляет функцию \u003Ccode>uuidv7()\u003C\u002Fcode>, генерирующую UUID версии 7 по RFC 9562. Эту возможность сообщество запрашивало годами — ранее требовались расширения \u003Ccode>pgcrypto\u003C\u002Fcode> или \u003Ccode>uuid-ossp\u003C\u002Fcode> в сочетании с пользовательскими функциями.\u003C\u002Fp>\n\u003Ch3>Почему uuidv7 важен\u003C\u002Fh3>\n\u003Cp>UUIDv4 (случайный) — наиболее распространённая версия UUID в качестве первичного ключа. У него есть критический недостаток для производительности баз данных: случайные UUID создают случайные паттерны ввода-вывода на B-tree индексах. При вставке новой строки с первичным ключом UUIDv4 листовая страница индекса, куда она попадёт, по сути случайна, что вызывает промахи кэша и усиление записи.\u003C\u002Fp>\n\u003Cp>UUIDv7 кодирует временную метку Unix в первых 48 битах, за которыми следуют случайные биты для уникальности. Это означает, что значения UUIDv7 монотонно возрастают со временем, как BIGSERIAL, но при этом глобально уникальны без координации.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">-- Генерация UUIDv7\nSELECT uuidv7();\n-- Результат: 019271a4-5b00-7123-8456-789abcdef012\n\n-- Извлечение временной метки из UUIDv7\nSELECT uuid_extract_timestamp('019271a4-5b00-7123-8456-789abcdef012');\n-- Результат: 2025-09-18 14:30:00+00\n\n-- Использование в качестве первичного ключа по умолчанию\nCREATE TABLE events (\n    id UUID PRIMARY KEY DEFAULT uuidv7(),\n    event_type TEXT NOT NULL,\n    payload JSONB,\n    created_at TIMESTAMPTZ DEFAULT now()\n);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Сравнение производительности\u003C\u002Fh3>\n\u003Cp>На таблице со 100 миллионами строк:\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Метрика\u003C\u002Fth>\u003Cth>UUIDv4 PK\u003C\u002Fth>\u003Cth>UUIDv7 PK\u003C\u002Fth>\u003Cth>BIGSERIAL PK\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>Скорость вставки (строк\u002Fсек)\u003C\u002Ftd>\u003Ctd>45 000\u003C\u002Ftd>\u003Ctd>112 000\u003C\u002Ftd>\u003Ctd>125 000\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Размер индекса\u003C\u002Ftd>\u003Ctd>4,2 ГБ\u003C\u002Ftd>\u003Ctd>4,2 ГБ\u003C\u002Ftd>\u003Ctd>2,1 ГБ\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Cache hit ratio индекса\u003C\u002Ftd>\u003Ctd>67%\u003C\u002Ftd>\u003Ctd>94%\u003C\u002Ftd>\u003Ctd>96%\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Задержка точечного поиска (p99)\u003C\u002Ftd>\u003Ctd>2,1 мс\u003C\u002Ftd>\u003Ctd>0,4 мс\u003C\u002Ftd>\u003Ctd>0,3 мс\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>UUIDv7 достигает производительности вставки, близкой к BIGSERIAL, сохраняя при этом глобальную уникальность. Для распределённых систем, микросервисов и любых архитектур, где идентификаторы генерируются на стороне приложения без координации с базой данных, uuidv7 теперь является очевидным выбором по умолчанию.\u003C\u002Fp>\n\u003Ch2 id=\"\">Виртуальные вычисляемые колонки\u003C\u002Fh2>\n\u003Cp>PostgreSQL поддерживает хранимые вычисляемые колонки с версии 12. PostgreSQL 18 добавляет виртуальные вычисляемые колонки — вычисляемые при чтении, без хранения на диске.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">CREATE TABLE products (\n    id UUID PRIMARY KEY DEFAULT uuidv7(),\n    name TEXT NOT NULL,\n    price_cents INTEGER NOT NULL,\n    tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.11,\n    -- Виртуальная: вычисляется при чтении, нулевая стоимость хранения\n    price_with_tax NUMERIC GENERATED ALWAYS AS (price_cents * (1 + tax_rate)) VIRTUAL,\n    -- Хранимая: вычисляется при записи, занимает место на диске\n    search_vector TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', name)) STORED\n);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Когда использовать VIRTUAL, а когда STORED\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>Используйте VIRTUAL, когда:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Вычисление дешёвое (арифметика, конкатенация строк, приведение типов)\u003C\u002Fli>\n\u003Cli>Вы хотите нулевых накладных расходов на хранение\u003C\u002Fli>\n\u003Cli>Колонка запрашивается редко или только вместе со строкой\u003C\u002Fli>\n\u003Cli>Вы хотите, чтобы значение всегда отражало текущие данные\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Используйте STORED, когда:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Вычисление дорогое (векторы полнотекстового поиска, сложная экстракция JSON)\u003C\u002Fli>\n\u003Cli>Вам нужно индексировать вычисляемую колонку\u003C\u002Fli>\n\u003Cli>Колонка часто используется в WHERE или JOIN\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Виртуальные колонки нельзя индексировать напрямую, так как на диске нет данных для индексации. Если вам нужно часто фильтровать или сортировать по вычисляемому значению, используйте STORED.\u003C\u002Fp>\n\u003Ch2 id=\"oauth\">Поддержка OAuth-аутентификации\u003C\u002Fh2>\n\u003Cp>PostgreSQL 18 добавляет OAuth 2.0 \u002F OpenID Connect как нативный метод аутентификации в pg_hba.conf. Это позволяет пользователям аутентифицироваться через провайдеры идентификации, такие как Okta, Auth0, Azure AD или Keycloak, без пользовательских PAM-модулей или проксирования через LDAP.\u003C\u002Fp>\n\u003Cpre>\u003Ccode># pg_hba.conf\nhost    all    all    0.0.0.0\u002F0    oauth issuer=\"https:\u002F\u002Fauth.company.com\" client_id=\"pg-prod\"\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Процесс работает следующим образом:\u003C\u002Fp>\n\u003Col>\n\u003Cli>Клиент подключается к PostgreSQL и получает OAuth-вызов\u003C\u002Fli>\n\u003Cli>Клиент получает JWT access-токен от настроенного провайдера идентификации\u003C\u002Fli>\n\u003Cli>Клиент отправляет токен PostgreSQL\u003C\u002Fli>\n\u003Cli>PostgreSQL проверяет подпись токена, издателя, аудиторию и срок действия\u003C\u002Fli>\n\u003Cli>Claim \u003Ccode>sub\u003C\u002Fcode> (subject) маппится на роль PostgreSQL\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Это особенно ценно для организаций, стандартизировавших OAuth\u002FOIDC для всей аутентификации сервисов. Доступом к базе данных теперь можно управлять через тот же провайдер идентификации, что и для приложений, с теми же политиками MFA, длительностью сессий и журналами аудита.\u003C\u002Fp>\n\u003Ch2 id=\"\">Темпоральные ограничения\u003C\u002Fh2>\n\u003Cp>PostgreSQL 18 представляет темпоральные PRIMARY KEY, UNIQUE и FOREIGN KEY ограничения для таблиц с периодическими колонками. Это привносит возможности SQL:2011 для работы с темпоральными данными, позволяя моделировать битемпоральные данные без принудительного контроля на уровне приложения.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">CREATE TABLE employee_departments (\n    employee_id INTEGER NOT NULL,\n    department_id INTEGER NOT NULL,\n    valid_from DATE NOT NULL,\n    valid_to DATE NOT NULL,\n    PERIOD FOR valid_period (valid_from, valid_to),\n    -- Темпоральный PK: запрет пересекающихся периодов для одного сотрудника\n    PRIMARY KEY (employee_id, valid_period WITHOUT OVERLAPS)\n);\n\nCREATE TABLE salary_history (\n    employee_id INTEGER NOT NULL,\n    salary NUMERIC NOT NULL,\n    valid_from DATE NOT NULL,\n    valid_to DATE NOT NULL,\n    PERIOD FOR valid_period (valid_from, valid_to),\n    -- Темпоральный FK: записи о зарплате должны ссылаться на валидное назначение в отдел\n    FOREIGN KEY (employee_id, PERIOD valid_period)\n        REFERENCES employee_departments (employee_id, PERIOD valid_period)\n);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Темпоральные ограничения предотвращают пересечение периодов для одной и той же сущности — распространённый источник ошибок в приложениях, управляющих данными с временными диапазонами (подписки, ценовые уровни, назначения ролей, резервирование).\u003C\u002Fp>\n\u003Ch2 id=\"old-new-returning\">OLD\u002FNEW в RETURNING\u003C\u002Fh2>\n\u003Cp>PostgreSQL 18 позволяет обращаться к значениям OLD и NEW в RETURNING-выражениях UPDATE и DELETE. Это устраняет необходимость в CTE или отдельных запросах, когда нужно и предыдущее, и новое состояние изменённых строк.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">-- Обновить цены и вернуть старые и новые значения\nUPDATE products\nSET price_cents = price_cents * 1.1\nWHERE category = 'electronics'\nRETURNING\n    id,\n    OLD.price_cents AS previous_price,\n    NEW.price_cents AS updated_price,\n    name;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Это незаменимо для журналирования изменений, захвата изменений данных (CDC) и любых процессов, где необходимо знать, что именно изменилось.\u003C\u002Fp>\n\u003Ch2 id=\"skip-scan-b-tree\">Skip-Scan для составных B-tree индексов\u003C\u002Fh2>\n\u003Cp>PostgreSQL 18 вводит оптимизацию skip-scan для составных B-tree индексов. Это позволяет планировщику эффективно использовать составной индекс, даже когда ведущая колонка отсутствует в WHERE.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-sql\">-- Индекс по (country, city, population)\nCREATE INDEX idx_locations ON locations (country, city, population);\n\n-- PG 17: полное сканирование индекса (не может эффективно использовать индекс без 'country')\n-- PG 18: skip-scan (перескакивает между уникальными значениями 'country')\nSELECT * FROM locations WHERE city = 'Jakarta';\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Skip-scan работает путём определения уникальных значений в ведущих колонках и выполнения серии целевых поисков для каждого значения. Для колонок с низкой кардинальностью (country, status, type) это значительно быстрее полного сканирования индекса.\u003C\u002Fp>\n\u003Cp>Skip-scan устраняет множество случаев, когда ранее требовался отдельный одноколоночный индекс, снижая накладные расходы на обслуживание индексов и хранение.\u003C\u002Fp>\n\u003Ch2 id=\"postgresql-17-18\">Руководство по миграции: PostgreSQL 17 → 18\u003C\u002Fh2>\n\u003Ch3>Чек-лист перед обновлением\u003C\u002Fh3>\n\u003Col>\n\u003Cli>\n\u003Cp>\u003Cstrong>Проверьте совместимость расширений.\u003C\u002Fstrong> Запустите \u003Ccode>SELECT * FROM pg_available_extensions;\u003C\u002Fcode> на тестовом экземпляре PG 18. Большинство популярных расширений (PostGIS, pgvector, pg_stat_statements) получили PG 18-совместимые релизы в течение 2 недель после выпуска.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Проверьте pg_hba.conf.\u003C\u002Fstrong> Новый метод OAuth аддитивен — существующие конфигурации продолжают работать без изменений.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Протестируйте производительность ввода-вывода.\u003C\u002Fstrong> Новая асинхронная подсистема включена по умолчанию. Запустите стандартный набор бенчмарков на тестовом экземпляре.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Проверьте вычисляемые колонки.\u003C\u002Fstrong> Если планируете конвертировать хранимые вычисляемые колонки в виртуальные, убедитесь, что от них не зависят индексы.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Протестируйте запросы приложения.\u003C\u002Fstrong> Оптимизация skip-scan может изменить планы запросов. Проверьте \u003Ccode>EXPLAIN ANALYZE\u003C\u002Fcode> для критических запросов.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>Методы обновления\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>pg_upgrade (рекомендуется для большинства):\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># Остановить старый сервер\npg_ctl -D \u002Fvar\u002Flib\u002Fpostgresql\u002F17\u002Fdata stop\n\n# Выполнить обновление\npg_upgrade \\\n  --old-datadir=\u002Fvar\u002Flib\u002Fpostgresql\u002F17\u002Fdata \\\n  --new-datadir=\u002Fvar\u002Flib\u002Fpostgresql\u002F18\u002Fdata \\\n  --old-bindir=\u002Fusr\u002Flib\u002Fpostgresql\u002F17\u002Fbin \\\n  --new-bindir=\u002Fusr\u002Flib\u002Fpostgresql\u002F18\u002Fbin \\\n  --link  # Жёсткие ссылки для скорости\n\n# Запустить новый сервер\npg_ctl -D \u002Fvar\u002Flib\u002Fpostgresql\u002F18\u002Fdata start\n\n# Обновить статистику\nvacuumdb --all --analyze-in-stages\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>Логическая репликация (для нулевого простоя):\u003C\u002Fstrong>\nНастройте логическую репликацию с PG 17 на PG 18, дождитесь синхронизации, затем переключите строку подключения приложения. Этот подход сложнее, но позволяет откат переключением обратно на PG 17.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Управляемые сервисы:\u003C\u002Fstrong> AWS RDS, Google Cloud SQL, Azure Database и Neon поддерживают обновление мажорных версий с минимальным простоем.\u003C\u002Fp>\n\u003Ch3>Задачи после обновления\u003C\u002Fh3>\n\u003Col>\n\u003Cli>Запустите \u003Ccode>ANALYZE\u003C\u002Fcode> на всех таблицах для обновления статистики планировщика\u003C\u002Fli>\n\u003Cli>Проверьте \u003Ccode>pg_stat_io\u003C\u002Fcode> для подтверждения активности асинхронного ввода-вывода\u003C\u002Fli>\n\u003Cli>Замените генераторы UUIDv4 на uuidv7() где уместно\u003C\u002Fli>\n\u003Cli>Оцените возможность конвертации хранимых колонок в VIRTUAL\u003C\u002Fli>\n\u003Cli>Мониторьте планы запросов первую неделю — skip-scan может изменить планы\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"faq\">FAQ\u003C\u002Fh2>\n\u003Ch3 id=\"postgresql-18\">Готов ли PostgreSQL 18 к продакшену?\u003C\u002Fh3>\n\u003Cp>Да. PostgreSQL следует строгому процессу релиза с несколькими фазами бета- и RC-версий. Релиз .0 является продакшен-качеством. Тем не менее, ожидание патч-релиза .1 (обычно через 2-3 месяца после .0) — распространённая и разумная стратегия для организаций с низкой терпимостью к рискам.\u003C\u002Fp>\n\u003Ch3 id=\"uuidv4-uuidv7\">Стоит ли переходить с UUIDv4 на UUIDv7 для существующих таблиц?\u003C\u002Fh3>\n\u003Cp>Для новых таблиц используйте uuidv7() по умолчанию. Для существующих таблиц с первичными ключами UUIDv4 стоимость миграции (перезапись всей таблицы и всех ссылающихся внешних ключей) редко оправдывает выгоду, если только у вас нет измеримых проблем с раздуванием индекса или промахами кэша.\u003C\u002Fp>\n\u003Ch3 id=\"\">Требует ли новый движок ввода-вывода изменений в ядре?\u003C\u002Fh3>\n\u003Cp>Поддержка io_uring требует ядра Linux 5.10 или новее (выпущено в декабре 2020). Если ваше ядро старше, PostgreSQL 18 использует фолбэк на воркер-потоки, который всё равно обеспечивает улучшения по сравнению с синхронным вводом-выводом PG 17, но менее значительные.\u003C\u002Fp>\n\u003Ch3 id=\"pgvector\">Можно ли использовать виртуальные колонки с pgvector?\u003C\u002Fh3>\n\u003Cp>Не напрямую. Эмбеддинги pgvector обычно хранятся, а не вычисляются, так как генерация эмбеддингов требует вызова внешней модели. Однако можно использовать виртуальную колонку для производных метрик вроде \u003Ccode>vector_dims(embedding)\u003C\u002Fcode> или \u003Ccode>l2_distance(embedding, reference_vector)\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch3 id=\"\">Как темпоральные ограничения работают с партиционированием?\u003C\u002Fh3>\n\u003Cp>Темпоральные ограничения работают с декларативным партиционированием. Можно партиционировать таблицу по диапазону периодической колонки и применять темпоральные PRIMARY KEY. Проверка ограничений учитывает партиции — пересечения проверяются по всем партициям.\u003C\u002Fp>\n\u003Ch3 id=\"merge\">Что с улучшениями MERGE?\u003C\u002Fh3>\n\u003Cp>PostgreSQL 18 расширяет оператор MERGE поддержкой RETURNING, завершая набор возможностей, представленный в PG 15. Теперь можно использовать \u003Ccode>MERGE ... RETURNING *\u003C\u002Fcode> для получения затронутых строк, аналогично INSERT\u002FUPDATE\u002FDELETE RETURNING.\u003C\u002Fp>\n","ru","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:36.690305Z","PostgreSQL 18: обзор — uuidv7, виртуальные колонки, асинхронный I\u002FO (2025)","Полное руководство по PostgreSQL 18: асинхронный движок I\u002FO (3x быстрее), нативный uuidv7(), виртуальные колонки, OAuth, темпоральные ограничения и skip-scan.","postgresql 18 обзор",null,"index, follow",[22,27],{"id":23,"name":24,"slug":25,"created_at":26},"c0000000-0000-0000-0000-000000000012","DevOps","devops","2026-03-28T10:44:21.513630Z",{"id":28,"name":29,"slug":30,"created_at":26},"c0000000-0000-0000-0000-000000000005","PostgreSQL","postgresql","Инженерия",[33,39,45],{"id":34,"title":35,"slug":36,"excerpt":37,"locale":12,"category_name":31,"published_at":38},"d0200000-0000-0000-0000-000000000013","Почему Бали становится хабом импакт-технологий Юго-Восточной Азии в 2026 году","pochemu-bali-stanovitsya-khabom-impakt-tekhnologiy-2026","Бали занимает 16-е место среди стартап-экосистем Юго-Восточной Азии. Растущая концентрация Web3-разработчиков, ИИ-стартапов в области устойчивого развития и компаний в сфере эко-тревел-технологий формирует нишу столицы импакт-технологий региона.","2026-03-28T10:44:37.953039Z",{"id":40,"title":41,"slug":42,"excerpt":43,"locale":12,"category_name":31,"published_at":44},"d0200000-0000-0000-0000-000000000012","Защита данных в ASEAN: чек-лист разработчика для мультистранового комплаенса","zashchita-dannykh-asean-chek-list-razrabotchika-komplaens","Семь стран ASEAN имеют собственные законы о защите данных с разными моделями согласия, требованиями к локализации и штрафами. Практический чек-лист для разработчиков мультистрановых приложений.","2026-03-28T10:44:37.944001Z",{"id":46,"title":47,"slug":48,"excerpt":49,"locale":12,"category_name":31,"published_at":50},"d0200000-0000-0000-0000-000000000011","Цифровая трансформация Индонезии на $29 миллиардов: возможности для софтверных компаний","tsifrovaya-transformatsiya-indonezii-29-milliardov-vozmozhnosti-dlya-kompaniy","Рынок IT-услуг Индонезии вырастет с $24,37 млрд в 2025 году до $29,03 млрд в 2026 году. Облачная инфраструктура, искусственный интеллект, электронная коммерция и дата-центры обеспечивают самый быстрый рост в Юго-Восточной Азии.","2026-03-28T10:44:37.917095Z",{"id":13,"name":52,"slug":53,"bio":54,"photo_url":19,"linkedin":19,"role":55,"created_at":56,"updated_at":56},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]