{"id":20499517,"url":"https://github.com/chobostar/postgres-handbook","last_synced_at":"2025-09-25T07:31:09.317Z","repository":{"id":165448300,"uuid":"295237333","full_name":"chobostar/postgres-handbook","owner":"chobostar","description":"Основано на боли разработчиков, и на куче потраченных денег бизнеса","archived":false,"fork":false,"pushed_at":"2025-06-21T03:04:50.000Z","size":13,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-21T04:18:55.827Z","etag":null,"topics":["handbook","postgres","postgresql","postgresql-handbook"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chobostar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-09-13T21:03:35.000Z","updated_at":"2025-06-21T03:04:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"50e6e69b-f2c0-4d0f-8a16-79c8b630acb4","html_url":"https://github.com/chobostar/postgres-handbook","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chobostar/postgres-handbook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chobostar%2Fpostgres-handbook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chobostar%2Fpostgres-handbook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chobostar%2Fpostgres-handbook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chobostar%2Fpostgres-handbook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chobostar","download_url":"https://codeload.github.com/chobostar/postgres-handbook/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chobostar%2Fpostgres-handbook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276881825,"owners_count":25721413,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-25T02:00:09.612Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["handbook","postgres","postgresql","postgresql-handbook"],"created_at":"2024-11-15T18:17:35.002Z","updated_at":"2025-09-25T07:31:09.310Z","avatar_url":"https://github.com/chobostar.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"[WIP] PostgreSQL Handbook\n=========================\nHandbook по эксплуатации PostgreSQL в production-e. Основано на реальных болях разработчиков.\n\nПрежде, чем вообще использовать БД помните:\n*\"Behavior is easy, state is hard\" (c)*\n\n- [Реально ли нужна база?](#реально-ли-нужна-база)\n- [Сделайте так, чтобы администратор к DB был не нужен](#сделайте-так-чтобы-администратор-к-DB-был-не-нужен)\n- [Обязательно ли делать запрос в базу?](#обязательно-ли-делать-запрос-в-базу)\n- [Capacity planning](#capacity-planning)\n- [Сколько делать размер пула коннектов](#сколько-делать-размер-пула-коннектов)\n- [Stateless масштабируется проще, чем Stateful](#stateless-масштабируется-проще-чем-stateful)\n- [Использование refresh materialized view, может приводит к connect timeout](#использование-refresh-materialized-view-может-приводит-к-connect-timeout)\n- [Влияние uuid vs bigint в качестве primary key на performance](#влияние-uuid-vs-bigint-в-качестве-primary-key-на-performance)\n- [Влияние synchronous_commit на TPS](#влияние-synchronous_commit-на-tps)\n- [Причины idle in transaction и почему это плохо](#причины-idle-in-transaction-и-почему-это-плохо)\n- [Не делайте базы коммуналки](#не-делайте-базы-коммуналки)\n- [Какие ресурсы нужны под базу](#какие-ресурсы-нужны-под-базу)\n- [Используейте минимальный необходимый уровень блокировки](#используейте-минимальный-необходимый-уровень-блокировки)\n- [Стремитесь разделять read-only запросы и read-write](#стремитесь-разделять-read-only-запросы-и-read-write)\n- [Советы как эффективно использовать JSONB](#советы-как-эффективно-использовать-jsonb)\n    - [Отдельная колонка для Primary key](#отдельная-колонка-для-primary-key)\n    - [Threshold деградации latency (TOAST Storage)](#threshold-деградации-latency--toast-storage-)\n    - [Избегайте слишком вложенные JSON-ы](#избегайте-слишком-вложенные-json-ы)\n    - [Вытаскивайте из JSON часто меняющиеся или читаемые поля](#вытаскивайте-из-json-часто-меняющиеся-или-читаемые-поля)\n    - [LZ4 компрессия](#lz4-компрессия)\n- [Грязные трюки (не использовать на продакшне)](#грязные-трюки-не-использовать-на-продакшне)\n    - [Запуск postgresql, если больше нет свободного места](#запуск-postgresql-если-больше-нет-свободного-места)\n    - [Потестировать отказоустойчивость](#потестировать-отказоустойчивость)\n    - [Non-Durable Settings](#non-durable-settings)\n- [Материалы](#материалы)\n\n### Реально ли нужна база?\nИспользуя базу сразу подписываетесь на дополнительную и весьма немалую ответственность.\n- так ли нужно самое точное и последнее значение?\n- обязательно ли хранить логи/историю/аудит и соблюдать целостность для них?\n- почему нельзя передавать state не через посредника (postgres), а напрямую? (JWT, подписанные параметры)\n- можно ли использовать message broker, там где используется shared state?\n- справочники можно хардкодить\n- данные, которые сохраняются, когда-нибудь читаются? при каких условиях их можно будет удалить?\n\n### Сделайте так, чтобы администратор к DB был не нужен\nЕсть проблемы коммуникации при эксплуатации:\n- Разработчик знает намерения кода, как оно должно работать, какие данные хранятся и характер их потребления\n- Администратор знает, как код работает по факту и что при этом происходит с инфраструктурой\n\nТо как должно работать != как работает по факту. Возникает конфликт. Можно учиться коммуницировать, а можно сделать так, чтобы этой коммуникации вообще не требовалось.\nНа этапе предоставления базы для разработчиков подумайте:\n- все ли метрики и дашборды есть, как понятнее их интерпретировать\n- предоставьте алгоритм действий, обучение, как проводить траблшутинг\n- упрощайте, иногда лучше не предоставлять что-то сложное клиенту, чем допустить, чтобы он выстрелил этим себе в ногу\n- понимает ли разработчик, как устроена инфраструктура и его технические ограничения\n\nКаждый раз после того, как потушили очередной пожар, думайте:\n- что не хватило разработчику, чтобы он самостоятельно разобрался в инциденте и решил ее\n- где можно улучшить процесс или ограничить функционал, чтобы это не повторялось\n\n### Обязательно ли делать запрос в базу?\n- часто пишем ненужную информацию в базу (или информацию, которая по своей природе быстро устаревает)\n- часто читаем ненужную информацию из базы\n- справочники можно кэшировать\n- разделять данные на immutable + часто читаемые, и mutable\n    - поля для поиска и часто изменяемые в простых типах\n    - отдельный json/поля для часто отображаемой информации\n    - отдельный json/таблица для редко используемой и не отображаемой информации\n\n### Capacity planning\nПустая строка занимает 24 байта\n```\npostgres=# select pg_column_size(row());\n pg_column_size \n----------------\n             24\n(1 row)\n```\nСчитать размеры строк можно так:\n```\npostgres=# select pg_column_size(row(0::bigint, 't'::boolean, 1::integer));\n pg_column_size \n----------------\n             40\n(1 row)\n```\nТут про [type alignment](https://www.2ndquadrant.com/en/blog/on-rocks-and-sand/)\n\nНе используйте uuid в виде текст, есть [тип uuid](https://stackoverflow.com/a/33837267) на 16 байт.\n\n### Сколько делать размер пула коннектов\n- [Закон Литтла](https://en.wikipedia.org/wiki/Little%27s_law)\n- [about pool sizing](https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing)\n\n`connections = ((core_count * 2) + effective_spindle_count)`\n\nЕсли все данные в кэше, то `effective_spindle_count = 0`.\n\nЧтобы тяжелые запросы не забивали весь пул, можно разделять пулы коннектов - для быстрых синхронных задач и медленных асинхронных.\n\n### Stateless масштабируется проще, чем Stateful\nПроцессор на stateless \"дешевле\", т.к. поднять такой же сервис рядом можно быстро, а поднять реплику от базы это дорого. Не пытайтесь всю работу отдавать на откуп базе:\n- при batch insert, если можно дедупликацию сделать на приложении, то лучше сделать это на приложении\n- обязательно ли надо возвращать отсортированные строки?\n- вычисление offset-ов внутри базы не бесплатно\n\n### Использование refresh materialized view, может приводит к connect timeout\nАктуально для драйверов, которые вычитываю pg_catalog:\n\n1) как работает matview:\nhttps://github.com/postgres/postgres/blob/REL_10_10/src/backend/commands/matview.c#L158-L166\n2) здесь pgx:\nhttps://github.com/jackc/pgx/blob/v3.6.0/conn.go#L607-L618\n\n2-ой не может прочитать каталог, если 1-ый держит эксклюзивный лок либо стоит в очереди на взятии лока \n\n### Влияние uuid vs bigint в качестве primary key на performance\nuuid занимает больше места плюс засчет рандомности трогает больше листьев b-tree, что приводит к большим объемам WAL:\nhttps://www.2ndquadrant.com/en/blog/on-the-impact-of-full-page-writes/ \n\n### Влияние synchronous_commit на TPS\n| synchronous_commit | TPS  |\n|---|---|\n| off | 3937  |\n| local | 1984  |\n| remote_write  | 1701  |\n| on  | 1373  |\n| remote_apply  | 1349  |\n\n[pgbench -c4 -j2 -N bench2 on Amazon EC2 VMs (m3.large, Ubuntu, all in same subnet, 1GB shared_buffers)](https://www.enterprisedb.com/blog/cheat-sheet-configuring-streaming-postgres-synchronous-replication)\n\n### Причины idle in transaction и почему это плохо\n1. Поход во внешний сервис при открытой транзакции\n    - Во-первых, открытая транзакция обходится не бесплатно     \n    - Во-вторых, попусту занимается коннект в пуле, пул-воркеров обычно гораздо больше, чем пул коннектов - зависающие транзакции приведут к истощению \n    - В-третьих, закладываемая логика все равно не будет работать честно, т.к. может произойти disconnect, failover - state или message для внешнего сервиса не откатится\n2. Вычисления на стороне приложения при открытой транзакции\n3. Транзакция ждет ответа пользователя\n4. Внутри транзакции происходит несколько round-trip до приложения и обратно\n\n### Не делайте базы коммуналки\n1 база == 1 postgres instance\n\n### Какие ресурсы нужны под базу\nИсходить например из:\n- планируемой нагрузки (`RPS`)\n- среднее время транзакции в секундах (`AvgTxTime`)\n- соотношение write/read\n- объем dataset-а\n- среднего объема запросов\n- можно ли разделить данные на горячие и холодные?\n\nСколько потоков нужно -`AvgTxTime * RPS` исходя из этого планируется количество vCPU.\n\nЕсли весь dataset горячий, то в диск ходить нежелательно - `RAM` \u003e объем dataset.\n\nlatency до локального SSD 150-300μs + ping + planning time + execution time - исходя из этого какую часть нагрузки допустимо пускать в диск, и сколько iops примерно нужно.\n\nколичество строк на запрос и средний объем запроса - исходить из худшего сценария, когда каждая нужная строка будет находится на отдельной странице.\nТ.е. `8kb` * `на количество строк` * `RPS` и прикинуть влезаем ли в лимиты iops + io bandwitch.\n\n### Используейте минимальный необходимый уровень блокировки\n- может достаточно `FOR SHARE` ?\n- если используются foreign keys может вместо `FOR UPDATE`, использовать `FOR NO KEY UPDATE` ?\n\n[подбирайте соответствующее](https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-ROWS)\n\n### Стремитесь разделять read-only запросы и read-write\n\nЭто история не только про масштабирование чтения, но и про повышение доступности.\n\nУ high-available кластера PostgreSQL может быть только 1 мастер (который обрабатывает read-write) и N реплик (которые обрабатывают read-only).\n\nОбеспечить доступность единственного хоста (мастера) сложнее, чем обеспечить доступность набора хостов (реплик). Не все API endpoints приложения выполняют операции записи.\nНаправляя read-only трафик на реплики, вы повышаете доступность кластера для операций чтения, а значит и общую доступность приложения.\n\nУчитывайте replication lag при проектировании логики приложения - данные на репликах могут отставать от мастера.\n\n### Советы как эффективно использовать JSONB\n#### Отдельная колонка для Primary key\n|Do|Don't|\n|---|---|\n|CREATE TABLE qq (jsonb)\u003cbr\u003e (id, {…}::jsonb)|CREATE TABLE qq (jsonb)\u003cbr\u003e ({id,…}::jsonb)|\n#### Threshold деградации latency (TOAST Storage)\nПо возможности держите размер tuple с JSON \u003c= 2000 bytes. Иначе оно будет [\"тоститься\"](https://www.postgresql.org/docs/current/storage-toast.html#STORAGE-TOAST-ONDISK)\nЭто дает значительный penalty по производительности.\n#### Избегайте слишком вложенные JSON-ы\nТак плохо: `{\"obj\": {\"obj\": {\"obj\": {\"obj\": {\"obj\": {\"key\": 14, \"long_str\": \"a\"}}}}}}`\n#### Вытаскивайте из JSON часто меняющиеся или читаемые поля\n| Do                                                                                                                                                                      |Don't|\n|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|\n| CREATE TABLE accounts\u003cbr\u003e(id, number, status, sum, {…}::jsonb)\u003cbr\u003e\u003cbr\u003eSELECT number FROM accounts WHERE id = 123;\u003cbr\u003e\u003cbr\u003eUPDATE accounts SET sum = 10000 WHERE id = 123 |CREATE TABLE customer (jsonb)\u003cbr\u003e(id, {number, status, sum…}::jsonb)\u003cbr\u003e\u003cbr\u003eSELECT js-\u003e\u003e'number' FROM accounts WHERE id = 123;\u003cbr\u003e\u003cbr\u003eUPDATE accounts SET js = jsonb_set(js, '{number}', '4444', true)\u003cbr\u003eWHERE id = 123|\n\n#### LZ4 компрессия\nLZ4 компрессия для TOAST данных обеспечивает [лучший баланс между скоростью компрессии/декомпрессии и размером](https://www.tigerdata.com/blog/optimizing-postgresql-performance-compression-pglz-vs-lz4).\nНастройка:\n```\ndefault_toast_compression = 'lz4'\n```\nили\n```sql\nCREATE TABLE lz4_example(id int, lz4_column text COMPRESSION lz4)\n```\n\n### Грязные трюки (не использовать на продакшне)\n#### Запуск postgresql, если больше нет свободного места\n```bash\n$ iptables -I INPUT -p tcp -m multiport --dport 5432,6432,6532\n$ /usr/lib/postgresql/12/bin/pg_controldata /var/lib/postgresql/12/main | grep checkpoint\nLatest checkpoint's REDO WAL file:    000000070000000400000014\n...\nTime of latest checkpoint:            Thu Aug 27 11:40:25 2020\n```\nудаляем из папки: `/var/lib/postgresql/12/main/pg_wal` все что старше.\n\nЗапускаем postgres, удаляем то, что просили удалить, возвращаем iptables.\n\n#### Потестировать отказоустойчивость\nПри некоторых настройках `vm.overcommit_memory` и `oom_score_adj` может сработать такое:\n\nоткрываем столько коннектов сколько сможем и выполняем:   \n```sql\npostgres=# set temp_buffers='1024GB';\nSET\npostgres=# set work_mem='1024GB';\nSET\npostgres=# explain analyze select a, max(b), min(c) from generate_series(1,1000000) as a, generate_series(1,100000) as b, generate_series(1,10) as c group by a;\n```\nдолжен сработать oom killer\n\n#### Non-Durable Settings\nкак ускорить postgres, если [данные в нём не нужны](https://www.postgresql.org/docs/current/non-durability.html)\n```\nfsync = off\nsynchronous_commit = off\nfull_page_writes = off\n```\nплюс [unlogged tables](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-UNLOGGED)\n\n### Что почитать?\n- https://www.interdb.jp/pg/\n- PostgreSQL 17 изнутри - https://postgrespro.ru/education/books/internals\n- Авторитетный и пополняемый контент:\n  - https://www.cybertec-postgresql.com/en/blog/\n  - https://www.depesz.com/\n  - https://www.crunchydata.com/blog\n\n### Материалы\n- [Как устроить хайлоад на ровном месте](https://www.highload.ru/moscow/2018/abstracts/41819)\n- [Борьба с нагрузкой в PostgreSQL, помогает ли репликация в этом?](https://www.highload.ru/spb/2019/abstracts/4890)\n- [Postgres Highload Checklist](https://www.highload.ru/spb/2019/abstracts/4433)\n- [Understanding JSONB Perforamance](http://www.sai.msu.su/~megera/postgres/talks/jsonb-pgconfnyc-2021.pdf)\n\n### Мои статьи на тему PG\n- 2022-02 - [Вредные советы для postgres](https://medium.com/@Kirill_P/%D1%8F-%D1%81%D0%BE%D0%B1%D0%B8%D1%80%D0%B0%D1%8E%D1%81%D1%8C-%D1%81%D0%BA%D0%BE%D1%80%D0%BE-%D1%83%D0%B2%D0%BE%D0%BB%D0%B8%D1%82%D1%8C%D1%81%D1%8F-%D0%BA%D0%B0%D0%BA-%D0%BC%D0%BD%D0%B5-%D0%BF%D0%BE%D0%B4%D0%B3%D0%B0%D0%B4%D0%B8%D1%82%D1%8C-%D1%81%D0%B2%D0%BE%D0%B8%D0%BC-%D0%BA%D0%BE%D0%BB%D0%BB%D0%B5%D0%B3%D0%B0%D0%BC-%D1%87%D0%B0%D1%81%D1%82%D1%8C-1-854b94223b0)\n- 2022-08 - [Про сеть и postgres](https://medium.com/@Kirill_P/network-issues-of-some-postgres-clients-48fcaeb2ce1d)\n- 2022-09 - [OOM и postgres](https://medium.com/@Kirill_P/oom-guard-for-containerized-postgres-79fec16b9ef0)\n- 2023-05 - [Про грабли Read Replicas](https://medium.com/@Kirill_P/postgresql-read-replicas-pitfalls-a361150d2564)\n- 2025-06 - [Про postgres-operator-ы](https://medium.com/@Kirill_P/%D0%BC%D0%BE%D0%B9-wish-list-%D0%BA-k8s-postgres-operator-%D0%B0%D0%BC-7a32bbcfcf49)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchobostar%2Fpostgres-handbook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchobostar%2Fpostgres-handbook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchobostar%2Fpostgres-handbook/lists"}