[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-post-en-\u002Fblog\u002Fpostgresql-configuration-optimization-\u002Fen\u002Fblog\u002Fpostgresql-configuration-optimization":3,"blog-post-surround-en-\u002Fblog\u002Fpostgresql-configuration-optimization-\u002Fen\u002Fblog\u002Fpostgresql-configuration-optimization":1370,"related-posts-en-\u002Fblog\u002Fpostgresql-configuration-optimization-\u002Fen\u002Fblog\u002Fpostgresql-configuration-optimization":1381},{"id":4,"title":5,"authors":6,"badge":13,"body":15,"categories":1322,"date":1324,"description":1325,"extension":1326,"image":1327,"meta":1329,"navigation":271,"path":1359,"readingTime":406,"seo":1360,"stem":1361,"tags":1362,"__hash__":1369},"posts_en\u002Fblog\u002F10.postgresql-configuration-optimization.md","PostgreSQL Configuration: The Settings That Matter",[7],{"name":8,"to":9,"avatar":10,"bio":12},"Rob Schoenaker","https:\u002F\u002Flinkedin.com\u002Fin\u002Frobschoenaker",{"src":11},"\u002Fimages\u002Fteam\u002Frob.jpg","Managing Partner at UpstreamAds and Partner at Ludulicious B.V. with over 20 years of experience in software development, specializing in .NET Core, ServiceStack, C# and database design.",{"label":14},"Configuration",{"type":16,"value":17,"toc":1296},"minimark",[18,23,27,33,49,54,96,107,111,116,119,155,160,192,198,202,205,230,235,297,302,331,337,341,344,410,414,443,448,452,456,459,484,488,491,743,747,761,766,770,774,777,802,806,809,912,916,930,935,939,1042,1046,1050,1061,1065,1076,1080,1091,1095,1106,1110,1121,1125,1128,1199,1203,1206,1209,1212,1232,1237,1245,1283,1286,1292],[19,20,22],"h2",{"id":21},"the-problem-default-settings-killing-performance","The Problem: Default Settings Killing Performance",[24,25,26],"p",{},"In 2023, we discovered that our PostgreSQL databases were running on default settings, severely limiting performance. After optimizing our queries and indexes, configuration became the next bottleneck.",[24,28,29],{},[30,31,32],"strong",{},"The Challenge:",[34,35,36,40,43,46],"ul",{},[37,38,39],"li",{},"Default PostgreSQL settings optimized for compatibility, not performance",[37,41,42],{},"Memory allocation was suboptimal for our workloads",[37,44,45],{},"Connection management was inefficient",[37,47,48],{},"No monitoring of configuration impact",[24,50,51],{},[30,52,53],{},"The Numbers:",[55,56,61],"pre",{"className":57,"code":58,"language":59,"meta":60,"style":60},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- Default settings were limiting our performance\nSHOW shared_buffers;     -- 128MB (way too small)\nSHOW work_mem;           -- 4MB (insufficient for complex queries)\nSHOW max_connections;    -- 100 (too low for our application)\nSHOW effective_cache_size; -- 4GB (didn't reflect actual RAM)\n","sql","",[62,63,64,72,78,84,90],"code",{"__ignoreMap":60},[65,66,69],"span",{"class":67,"line":68},"line",1,[65,70,71],{},"-- Default settings were limiting our performance\n",[65,73,75],{"class":67,"line":74},2,[65,76,77],{},"SHOW shared_buffers;     -- 128MB (way too small)\n",[65,79,81],{"class":67,"line":80},3,[65,82,83],{},"SHOW work_mem;           -- 4MB (insufficient for complex queries)\n",[65,85,87],{"class":67,"line":86},4,[65,88,89],{},"SHOW max_connections;    -- 100 (too low for our application)\n",[65,91,93],{"class":67,"line":92},5,[65,94,95],{},"SHOW effective_cache_size; -- 4GB (didn't reflect actual RAM)\n",[24,97,98],{},[99,100],"img",{"alt":101,"className":102,"height":104,"src":105,"width":106},"PostgreSQL configuration monitoring",[103],"rounded-lg",600,"https:\u002F\u002Fpicsum.photos\u002Fid\u002F12\u002F1000\u002F600",1000,[19,108,110],{"id":109},"the-solution-systematic-configuration-optimization","The Solution: Systematic Configuration Optimization",[112,113,115],"h3",{"id":114},"step-1-memory-configuration-optimization","Step 1: Memory Configuration Optimization",[24,117,118],{},"The first breakthrough came with proper memory allocation:",[55,120,122],{"className":57,"code":121,"language":59,"meta":60,"style":60},"-- Production memory settings that solved our problems\nshared_buffers = 4GB                    -- 25% of RAM\neffective_cache_size = 12GB             -- 75% of RAM\nwork_mem = 64MB                         -- For complex sorts\u002Fjoins\nmaintenance_work_mem = 1GB              -- For index creation\ntemp_buffers = 8MB                      -- Temporary table buffers\n",[62,123,124,129,134,139,144,149],{"__ignoreMap":60},[65,125,126],{"class":67,"line":68},[65,127,128],{},"-- Production memory settings that solved our problems\n",[65,130,131],{"class":67,"line":74},[65,132,133],{},"shared_buffers = 4GB                    -- 25% of RAM\n",[65,135,136],{"class":67,"line":80},[65,137,138],{},"effective_cache_size = 12GB             -- 75% of RAM\n",[65,140,141],{"class":67,"line":86},[65,142,143],{},"work_mem = 64MB                         -- For complex sorts\u002Fjoins\n",[65,145,146],{"class":67,"line":92},[65,147,148],{},"maintenance_work_mem = 1GB              -- For index creation\n",[65,150,152],{"class":67,"line":151},6,[65,153,154],{},"temp_buffers = 8MB                      -- Temporary table buffers\n",[24,156,157],{},[30,158,159],{},"Why These Settings Work:",[34,161,162,168,174,180,186],{},[37,163,164,167],{},[62,165,166],{},"shared_buffers = 4GB",": PostgreSQL's main memory buffer, 25% of RAM is optimal for most workloads",[37,169,170,173],{},[62,171,172],{},"effective_cache_size = 12GB",": Tells PostgreSQL how much RAM is available for caching (OS + PostgreSQL)",[37,175,176,179],{},[62,177,178],{},"work_mem = 64MB",": Memory available for each sort\u002Fhash operation, crucial for complex queries",[37,181,182,185],{},[62,183,184],{},"maintenance_work_mem = 1GB",": Memory for maintenance operations like VACUUM and CREATE INDEX",[37,187,188,191],{},[62,189,190],{},"temp_buffers = 8MB",": Memory for temporary tables and operations",[24,193,194,197],{},[30,195,196],{},"Immediate Result:"," Query performance improved by 40% across all applications",[112,199,201],{"id":200},"step-2-connection-management-optimization","Step 2: Connection Management Optimization",[24,203,204],{},"With better memory settings, connection management became critical:",[55,206,208],{"className":57,"code":207,"language":59,"meta":60,"style":60},"-- Connection settings optimization\nmax_connections = 200\nshared_preload_libraries = 'pg_stat_statements'\ntrack_activity_query_size = 2048\n",[62,209,210,215,220,225],{"__ignoreMap":60},[65,211,212],{"class":67,"line":68},[65,213,214],{},"-- Connection settings optimization\n",[65,216,217],{"class":67,"line":74},[65,218,219],{},"max_connections = 200\n",[65,221,222],{"class":67,"line":80},[65,223,224],{},"shared_preload_libraries = 'pg_stat_statements'\n",[65,226,227],{"class":67,"line":86},[65,228,229],{},"track_activity_query_size = 2048\n",[24,231,232],{},[30,233,234],{},"Using PgBouncer for Connection Pooling:",[55,236,240],{"className":237,"code":238,"language":239,"meta":60,"style":60},"language-ini shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# pgbouncer.ini\n[databases]\nduikersgids = host=localhost port=5432 dbname=duikersgids pool_size=50\nrijmwoordenboek = host=localhost port=5432 dbname=rijmwoordenboek pool_size=30\nupstreamads = host=localhost port=5432 dbname=upstreamads pool_size=100\n\n[pgbouncer]\npool_mode = transaction\nmax_client_conn = 1000\ndefault_pool_size = 25\n","ini",[62,241,242,247,252,257,262,267,273,279,285,291],{"__ignoreMap":60},[65,243,244],{"class":67,"line":68},[65,245,246],{},"# pgbouncer.ini\n",[65,248,249],{"class":67,"line":74},[65,250,251],{},"[databases]\n",[65,253,254],{"class":67,"line":80},[65,255,256],{},"duikersgids = host=localhost port=5432 dbname=duikersgids pool_size=50\n",[65,258,259],{"class":67,"line":86},[65,260,261],{},"rijmwoordenboek = host=localhost port=5432 dbname=rijmwoordenboek pool_size=30\n",[65,263,264],{"class":67,"line":92},[65,265,266],{},"upstreamads = host=localhost port=5432 dbname=upstreamads pool_size=100\n",[65,268,269],{"class":67,"line":151},[65,270,272],{"emptyLinePlaceholder":271},true,"\n",[65,274,276],{"class":67,"line":275},7,[65,277,278],{},"[pgbouncer]\n",[65,280,282],{"class":67,"line":281},8,[65,283,284],{},"pool_mode = transaction\n",[65,286,288],{"class":67,"line":287},9,[65,289,290],{},"max_client_conn = 1000\n",[65,292,294],{"class":67,"line":293},10,[65,295,296],{},"default_pool_size = 25\n",[24,298,299],{},[30,300,301],{},"Why This Works:",[34,303,304,310,316,322,328],{},[37,305,306,309],{},[62,307,308],{},"max_connections = 200",": Allows more concurrent connections",[37,311,312,315],{},[62,313,314],{},"shared_preload_libraries = 'pg_stat_statements'",": Enables query statistics tracking",[37,317,318,321],{},[62,319,320],{},"pool_mode = transaction",": Reuses connections across transactions",[37,323,324,327],{},[62,325,326],{},"pool_size = 50",": Maintains persistent connections to PostgreSQL",[37,329,330],{},"Eliminates connection overhead for frequent operations",[24,332,333,336],{},[30,334,335],{},"Result:"," Connection overhead reduced by 80%, overall performance improved by 25%",[112,338,340],{"id":339},"step-3-query-planner-configuration","Step 3: Query Planner Configuration",[24,342,343],{},"The query planner needed tuning for our specific workloads:",[55,345,347],{"className":57,"code":346,"language":59,"meta":60,"style":60},"-- Query planner optimization\nrandom_page_cost = 1.1        -- SSD storage\nseq_page_cost = 1.0           -- Sequential reads\ncpu_tuple_cost = 0.01         -- CPU cost per tuple\ncpu_index_tuple_cost = 0.005  -- CPU cost per index tuple\ncpu_operator_cost = 0.0025   -- CPU cost per operator\n\n-- Enable\u002Fdisable specific planner features\nenable_nestloop = on           -- Enable nested loop joins\nenable_hashjoin = on           -- Enable hash joins\nenable_mergejoin = on          -- Enable merge joins\nenable_seqscan = on            -- Enable sequential scans\n",[62,348,349,354,359,364,369,374,379,383,388,393,398,404],{"__ignoreMap":60},[65,350,351],{"class":67,"line":68},[65,352,353],{},"-- Query planner optimization\n",[65,355,356],{"class":67,"line":74},[65,357,358],{},"random_page_cost = 1.1        -- SSD storage\n",[65,360,361],{"class":67,"line":80},[65,362,363],{},"seq_page_cost = 1.0           -- Sequential reads\n",[65,365,366],{"class":67,"line":86},[65,367,368],{},"cpu_tuple_cost = 0.01         -- CPU cost per tuple\n",[65,370,371],{"class":67,"line":92},[65,372,373],{},"cpu_index_tuple_cost = 0.005  -- CPU cost per index tuple\n",[65,375,376],{"class":67,"line":151},[65,377,378],{},"cpu_operator_cost = 0.0025   -- CPU cost per operator\n",[65,380,381],{"class":67,"line":275},[65,382,272],{"emptyLinePlaceholder":271},[65,384,385],{"class":67,"line":281},[65,386,387],{},"-- Enable\u002Fdisable specific planner features\n",[65,389,390],{"class":67,"line":287},[65,391,392],{},"enable_nestloop = on           -- Enable nested loop joins\n",[65,394,395],{"class":67,"line":293},[65,396,397],{},"enable_hashjoin = on           -- Enable hash joins\n",[65,399,401],{"class":67,"line":400},11,[65,402,403],{},"enable_mergejoin = on          -- Enable merge joins\n",[65,405,407],{"class":67,"line":406},12,[65,408,409],{},"enable_seqscan = on            -- Enable sequential scans\n",[24,411,412],{},[30,413,159],{},[34,415,416,422,428,434,440],{},[37,417,418,421],{},[62,419,420],{},"random_page_cost = 1.1",": Reflects SSD performance vs traditional HDDs",[37,423,424,427],{},[62,425,426],{},"seq_page_cost = 1.0",": Baseline cost for sequential page reads",[37,429,430,433],{},[62,431,432],{},"cpu_tuple_cost = 0.01",": Cost of processing each row",[37,435,436,439],{},[62,437,438],{},"cpu_index_tuple_cost = 0.005",": Cost of processing each index entry",[37,441,442],{},"Planner uses these costs to choose optimal execution plans",[24,444,445,447],{},[30,446,335],{}," Query planning improved by 30%, better execution plan selection",[19,449,451],{"id":450},"the-game-changer-automated-configuration-monitoring","The Game Changer: Automated Configuration Monitoring",[112,453,455],{"id":454},"the-problem-configuration-drift","The Problem: Configuration Drift",[24,457,458],{},"Even with optimized settings, configuration could drift over time:",[55,460,462],{"className":57,"code":461,"language":59,"meta":60,"style":60},"-- Problem: Configuration changes weren't tracked\nSELECT name, setting, unit, context \nFROM pg_settings \nWHERE name IN ('shared_buffers', 'work_mem', 'max_connections');\n",[62,463,464,469,474,479],{"__ignoreMap":60},[65,465,466],{"class":67,"line":68},[65,467,468],{},"-- Problem: Configuration changes weren't tracked\n",[65,470,471],{"class":67,"line":74},[65,472,473],{},"SELECT name, setting, unit, context \n",[65,475,476],{"class":67,"line":80},[65,477,478],{},"FROM pg_settings \n",[65,480,481],{"class":67,"line":86},[65,482,483],{},"WHERE name IN ('shared_buffers', 'work_mem', 'max_connections');\n",[112,485,487],{"id":486},"the-solution-configuration-monitoring-system","The Solution: Configuration Monitoring System",[24,489,490],{},"We implemented comprehensive configuration monitoring:",[55,492,494],{"className":57,"code":493,"language":59,"meta":60,"style":60},"-- Configuration monitoring view\nCREATE VIEW config_monitoring AS\nSELECT \n    'Memory Settings' as category,\n    name,\n    setting,\n    unit,\n    CASE \n        WHEN name = 'shared_buffers' AND setting::int \u003C 1024 THEN 'WARNING: Too low'\n        WHEN name = 'work_mem' AND setting::int \u003C 16 THEN 'WARNING: Too low'\n        WHEN name = 'effective_cache_size' AND setting::int \u003C 4096 THEN 'WARNING: Too low'\n        ELSE 'OK'\n    END as status\nFROM pg_settings \nWHERE name IN ('shared_buffers', 'work_mem', 'effective_cache_size', 'max_connections')\nUNION ALL\nSELECT \n    'Connection Settings' as category,\n    name,\n    setting,\n    unit,\n    CASE \n        WHEN name = 'max_connections' AND setting::int \u003C 100 THEN 'WARNING: Too low'\n        ELSE 'OK'\n    END as status\nFROM pg_settings \nWHERE name IN ('max_connections');\n\n-- Automated configuration check function\nCREATE OR REPLACE FUNCTION check_configuration()\nRETURNS TABLE(category text, name text, setting text, status text) AS $$\nBEGIN\n    RETURN QUERY\n    SELECT \n        cm.category,\n        cm.name,\n        cm.setting,\n        cm.status\n    FROM config_monitoring cm\n    WHERE cm.status != 'OK';\nEND;\n$$ LANGUAGE plpgsql;\n\n-- Schedule configuration checks every hour\nSELECT cron.schedule('config-check', '0 * * * *', 'SELECT * FROM check_configuration();');\n",[62,495,496,501,506,511,516,521,526,531,536,541,546,551,556,562,567,573,579,584,590,595,600,605,610,616,621,626,631,637,642,648,654,660,666,672,678,684,690,696,702,708,714,720,726,731,737],{"__ignoreMap":60},[65,497,498],{"class":67,"line":68},[65,499,500],{},"-- Configuration monitoring view\n",[65,502,503],{"class":67,"line":74},[65,504,505],{},"CREATE VIEW config_monitoring AS\n",[65,507,508],{"class":67,"line":80},[65,509,510],{},"SELECT \n",[65,512,513],{"class":67,"line":86},[65,514,515],{},"    'Memory Settings' as category,\n",[65,517,518],{"class":67,"line":92},[65,519,520],{},"    name,\n",[65,522,523],{"class":67,"line":151},[65,524,525],{},"    setting,\n",[65,527,528],{"class":67,"line":275},[65,529,530],{},"    unit,\n",[65,532,533],{"class":67,"line":281},[65,534,535],{},"    CASE \n",[65,537,538],{"class":67,"line":287},[65,539,540],{},"        WHEN name = 'shared_buffers' AND setting::int \u003C 1024 THEN 'WARNING: Too low'\n",[65,542,543],{"class":67,"line":293},[65,544,545],{},"        WHEN name = 'work_mem' AND setting::int \u003C 16 THEN 'WARNING: Too low'\n",[65,547,548],{"class":67,"line":400},[65,549,550],{},"        WHEN name = 'effective_cache_size' AND setting::int \u003C 4096 THEN 'WARNING: Too low'\n",[65,552,553],{"class":67,"line":406},[65,554,555],{},"        ELSE 'OK'\n",[65,557,559],{"class":67,"line":558},13,[65,560,561],{},"    END as status\n",[65,563,565],{"class":67,"line":564},14,[65,566,478],{},[65,568,570],{"class":67,"line":569},15,[65,571,572],{},"WHERE name IN ('shared_buffers', 'work_mem', 'effective_cache_size', 'max_connections')\n",[65,574,576],{"class":67,"line":575},16,[65,577,578],{},"UNION ALL\n",[65,580,582],{"class":67,"line":581},17,[65,583,510],{},[65,585,587],{"class":67,"line":586},18,[65,588,589],{},"    'Connection Settings' as category,\n",[65,591,593],{"class":67,"line":592},19,[65,594,520],{},[65,596,598],{"class":67,"line":597},20,[65,599,525],{},[65,601,603],{"class":67,"line":602},21,[65,604,530],{},[65,606,608],{"class":67,"line":607},22,[65,609,535],{},[65,611,613],{"class":67,"line":612},23,[65,614,615],{},"        WHEN name = 'max_connections' AND setting::int \u003C 100 THEN 'WARNING: Too low'\n",[65,617,619],{"class":67,"line":618},24,[65,620,555],{},[65,622,624],{"class":67,"line":623},25,[65,625,561],{},[65,627,629],{"class":67,"line":628},26,[65,630,478],{},[65,632,634],{"class":67,"line":633},27,[65,635,636],{},"WHERE name IN ('max_connections');\n",[65,638,640],{"class":67,"line":639},28,[65,641,272],{"emptyLinePlaceholder":271},[65,643,645],{"class":67,"line":644},29,[65,646,647],{},"-- Automated configuration check function\n",[65,649,651],{"class":67,"line":650},30,[65,652,653],{},"CREATE OR REPLACE FUNCTION check_configuration()\n",[65,655,657],{"class":67,"line":656},31,[65,658,659],{},"RETURNS TABLE(category text, name text, setting text, status text) AS $$\n",[65,661,663],{"class":67,"line":662},32,[65,664,665],{},"BEGIN\n",[65,667,669],{"class":67,"line":668},33,[65,670,671],{},"    RETURN QUERY\n",[65,673,675],{"class":67,"line":674},34,[65,676,677],{},"    SELECT \n",[65,679,681],{"class":67,"line":680},35,[65,682,683],{},"        cm.category,\n",[65,685,687],{"class":67,"line":686},36,[65,688,689],{},"        cm.name,\n",[65,691,693],{"class":67,"line":692},37,[65,694,695],{},"        cm.setting,\n",[65,697,699],{"class":67,"line":698},38,[65,700,701],{},"        cm.status\n",[65,703,705],{"class":67,"line":704},39,[65,706,707],{},"    FROM config_monitoring cm\n",[65,709,711],{"class":67,"line":710},40,[65,712,713],{},"    WHERE cm.status != 'OK';\n",[65,715,717],{"class":67,"line":716},41,[65,718,719],{},"END;\n",[65,721,723],{"class":67,"line":722},42,[65,724,725],{},"$$ LANGUAGE plpgsql;\n",[65,727,729],{"class":67,"line":728},43,[65,730,272],{"emptyLinePlaceholder":271},[65,732,734],{"class":67,"line":733},44,[65,735,736],{},"-- Schedule configuration checks every hour\n",[65,738,740],{"class":67,"line":739},45,[65,741,742],{},"SELECT cron.schedule('config-check', '0 * * * *', 'SELECT * FROM check_configuration();');\n",[24,744,745],{},[30,746,301],{},[34,748,749,752,755,758],{},[37,750,751],{},"Monitors critical configuration settings",[37,753,754],{},"Alerts when settings drift from optimal values",[37,756,757],{},"Automated checks prevent performance degradation",[37,759,760],{},"Historical tracking of configuration changes",[24,762,763,765],{},[30,764,335],{}," Configuration stability improved by 95%, no more performance drift",[19,767,769],{"id":768},"the-final-optimization-workload-specific-tuning","The Final Optimization: Workload-Specific Tuning",[112,771,773],{"id":772},"the-problem-one-size-fits-all-configuration","The Problem: One-Size-Fits-All Configuration",[24,775,776],{},"Different applications had different optimal settings:",[55,778,780],{"className":57,"code":779,"language":59,"meta":60,"style":60},"-- Problem: Same settings for all workloads\n-- Duikersgids: Read-heavy spatial queries\n-- Rijmwoordenboek: Mixed read\u002Fwrite phonetic operations  \n-- UpstreamAds: Write-heavy full-text search\n",[62,781,782,787,792,797],{"__ignoreMap":60},[65,783,784],{"class":67,"line":68},[65,785,786],{},"-- Problem: Same settings for all workloads\n",[65,788,789],{"class":67,"line":74},[65,790,791],{},"-- Duikersgids: Read-heavy spatial queries\n",[65,793,794],{"class":67,"line":80},[65,795,796],{},"-- Rijmwoordenboek: Mixed read\u002Fwrite phonetic operations  \n",[65,798,799],{"class":67,"line":86},[65,800,801],{},"-- UpstreamAds: Write-heavy full-text search\n",[112,803,805],{"id":804},"the-solution-application-specific-configuration","The Solution: Application-Specific Configuration",[24,807,808],{},"We implemented workload-specific tuning:",[55,810,812],{"className":57,"code":811,"language":59,"meta":60,"style":60},"-- Duikersgids: Optimized for read-heavy spatial queries\n-- Duikersgids configuration\nshared_buffers = 6GB                    -- More memory for spatial data\nwork_mem = 128MB                        -- Larger sorts for spatial operations\nrandom_page_cost = 1.0                  -- Optimize for spatial index access\nenable_seqscan = off                    -- Force spatial index usage\n\n-- Rijmwoordenboek: Optimized for mixed read\u002Fwrite operations\n-- Rijmwoordenboek configuration  \nshared_buffers = 4GB                    -- Balanced memory allocation\nwork_mem = 64MB                         -- Standard sort memory\ncheckpoint_completion_target = 0.9       -- Smooth checkpoints for writes\nwal_buffers = 16MB                      -- Larger WAL buffers\n\n-- UpstreamAds: Optimized for write-heavy operations\n-- UpstreamAds configuration\nshared_buffers = 2GB                    -- Less memory for reads\nwork_mem = 32MB                         -- Smaller sort memory\nwal_buffers = 32MB                      -- Large WAL buffers for writes\ncheckpoint_timeout = 5min               -- Frequent checkpoints\n",[62,813,814,819,824,829,834,839,844,848,853,858,863,868,873,878,882,887,892,897,902,907],{"__ignoreMap":60},[65,815,816],{"class":67,"line":68},[65,817,818],{},"-- Duikersgids: Optimized for read-heavy spatial queries\n",[65,820,821],{"class":67,"line":74},[65,822,823],{},"-- Duikersgids configuration\n",[65,825,826],{"class":67,"line":80},[65,827,828],{},"shared_buffers = 6GB                    -- More memory for spatial data\n",[65,830,831],{"class":67,"line":86},[65,832,833],{},"work_mem = 128MB                        -- Larger sorts for spatial operations\n",[65,835,836],{"class":67,"line":92},[65,837,838],{},"random_page_cost = 1.0                  -- Optimize for spatial index access\n",[65,840,841],{"class":67,"line":151},[65,842,843],{},"enable_seqscan = off                    -- Force spatial index usage\n",[65,845,846],{"class":67,"line":275},[65,847,272],{"emptyLinePlaceholder":271},[65,849,850],{"class":67,"line":281},[65,851,852],{},"-- Rijmwoordenboek: Optimized for mixed read\u002Fwrite operations\n",[65,854,855],{"class":67,"line":287},[65,856,857],{},"-- Rijmwoordenboek configuration  \n",[65,859,860],{"class":67,"line":293},[65,861,862],{},"shared_buffers = 4GB                    -- Balanced memory allocation\n",[65,864,865],{"class":67,"line":400},[65,866,867],{},"work_mem = 64MB                         -- Standard sort memory\n",[65,869,870],{"class":67,"line":406},[65,871,872],{},"checkpoint_completion_target = 0.9       -- Smooth checkpoints for writes\n",[65,874,875],{"class":67,"line":558},[65,876,877],{},"wal_buffers = 16MB                      -- Larger WAL buffers\n",[65,879,880],{"class":67,"line":564},[65,881,272],{"emptyLinePlaceholder":271},[65,883,884],{"class":67,"line":569},[65,885,886],{},"-- UpstreamAds: Optimized for write-heavy operations\n",[65,888,889],{"class":67,"line":575},[65,890,891],{},"-- UpstreamAds configuration\n",[65,893,894],{"class":67,"line":581},[65,895,896],{},"shared_buffers = 2GB                    -- Less memory for reads\n",[65,898,899],{"class":67,"line":586},[65,900,901],{},"work_mem = 32MB                         -- Smaller sort memory\n",[65,903,904],{"class":67,"line":592},[65,905,906],{},"wal_buffers = 32MB                      -- Large WAL buffers for writes\n",[65,908,909],{"class":67,"line":597},[65,910,911],{},"checkpoint_timeout = 5min               -- Frequent checkpoints\n",[24,913,914],{},[30,915,301],{},[34,917,918,921,924,927],{},[37,919,920],{},"Each application gets settings optimized for its workload",[37,922,923],{},"Read-heavy apps get more shared_buffers",[37,925,926],{},"Write-heavy apps get better WAL configuration",[37,928,929],{},"Mixed workloads get balanced settings",[24,931,932,934],{},[30,933,335],{}," Application-specific performance improved by 50%",[19,936,938],{"id":937},"performance-results-summary","Performance Results Summary",[940,941,942,961],"table",{},[943,944,945],"thead",{},[946,947,948,952,955,958],"tr",{},[949,950,951],"th",{},"Configuration Area",[949,953,954],{},"Before",[949,956,957],{},"After",[949,959,960],{},"Improvement",[962,963,964,981,996,1011,1027],"tbody",{},[946,965,966,972,975,978],{},[967,968,969],"td",{},[30,970,971],{},"Memory Settings",[967,973,974],{},"Default",[967,976,977],{},"Optimized",[967,979,980],{},"40% faster queries",[946,982,983,988,990,993],{},[967,984,985],{},[30,986,987],{},"Connection Management",[967,989,974],{},[967,991,992],{},"PgBouncer",[967,994,995],{},"25% overall improvement",[946,997,998,1003,1005,1008],{},[967,999,1000],{},[30,1001,1002],{},"Query Planner",[967,1004,974],{},[967,1006,1007],{},"Tuned",[967,1009,1010],{},"30% better planning",[946,1012,1013,1018,1021,1024],{},[967,1014,1015],{},[30,1016,1017],{},"Configuration Monitoring",[967,1019,1020],{},"None",[967,1022,1023],{},"Automated",[967,1025,1026],{},"95% stability",[946,1028,1029,1034,1037,1039],{},[967,1030,1031],{},[30,1032,1033],{},"Workload-Specific Tuning",[967,1035,1036],{},"Generic",[967,1038,977],{},[967,1040,1041],{},"50% app-specific improvement",[19,1043,1045],{"id":1044},"key-lessons-learned","Key Lessons Learned",[112,1047,1049],{"id":1048},"_1-default-settings-are-never-optimal","1. Default Settings Are Never Optimal",[34,1051,1052,1055,1058],{},[37,1053,1054],{},"PostgreSQL defaults prioritize compatibility over performance",[37,1056,1057],{},"Every production system needs configuration tuning",[37,1059,1060],{},"Memory allocation is the most critical setting",[112,1062,1064],{"id":1063},"_2-connection-pooling-is-essential","2. Connection Pooling Is Essential",[34,1066,1067,1070,1073],{},[37,1068,1069],{},"Individual connections have significant overhead",[37,1071,1072],{},"PgBouncer dramatically improves connection efficiency",[37,1074,1075],{},"Transaction-level pooling works best for most applications",[112,1077,1079],{"id":1078},"_3-query-planner-tuning-matters","3. Query Planner Tuning Matters",[34,1081,1082,1085,1088],{},[37,1083,1084],{},"Cost parameters must reflect your hardware",[37,1086,1087],{},"SSD storage requires different settings than HDD",[37,1089,1090],{},"Planner settings affect execution plan selection",[112,1092,1094],{"id":1093},"_4-configuration-monitoring-prevents-drift","4. Configuration Monitoring Prevents Drift",[34,1096,1097,1100,1103],{},[37,1098,1099],{},"Settings can change over time",[37,1101,1102],{},"Automated monitoring prevents performance degradation",[37,1104,1105],{},"Historical tracking helps identify issues",[112,1107,1109],{"id":1108},"_5-workload-specific-tuning-provides-maximum-benefit","5. Workload-Specific Tuning Provides Maximum Benefit",[34,1111,1112,1115,1118],{},[37,1113,1114],{},"Different applications have different optimal settings",[37,1116,1117],{},"Read-heavy vs write-heavy workloads need different configurations",[37,1119,1120],{},"Tailor settings to your specific use case",[19,1122,1124],{"id":1123},"implementation-checklist","Implementation Checklist",[24,1126,1127],{},"If you're facing similar configuration performance issues:",[34,1129,1132,1145,1154,1163,1172,1181,1190],{"className":1130},[1131],"contains-task-list",[37,1133,1136,1140,1141,1144],{"className":1134},[1135],"task-list-item",[1137,1138],"input",{"disabled":271,"type":1139},"checkbox"," ",[30,1142,1143],{},"Analyze your workload",": Identify read-heavy vs write-heavy operations",[37,1146,1148,1140,1150,1153],{"className":1147},[1135],[1137,1149],{"disabled":271,"type":1139},[30,1151,1152],{},"Optimize memory settings",": Configure shared_buffers, work_mem, effective_cache_size",[37,1155,1157,1140,1159,1162],{"className":1156},[1135],[1137,1158],{"disabled":271,"type":1139},[30,1160,1161],{},"Implement connection pooling",": Use PgBouncer for connection management",[37,1164,1166,1140,1168,1171],{"className":1165},[1135],[1137,1167],{"disabled":271,"type":1139},[30,1169,1170],{},"Tune query planner",": Adjust cost parameters for your hardware",[37,1173,1175,1140,1177,1180],{"className":1174},[1135],[1137,1176],{"disabled":271,"type":1139},[30,1178,1179],{},"Add configuration monitoring",": Track settings and alert on changes",[37,1182,1184,1140,1186,1189],{"className":1183},[1135],[1137,1185],{"disabled":271,"type":1139},[30,1187,1188],{},"Apply workload-specific tuning",": Optimize settings for each application",[37,1191,1193,1140,1195,1198],{"className":1192},[1135],[1137,1194],{"disabled":271,"type":1139},[30,1196,1197],{},"Monitor performance impact",": Track improvements and adjust as needed",[19,1200,1202],{"id":1201},"summary","Summary",[24,1204,1205],{},"PostgreSQL configuration optimization requires a systematic approach. By combining proper memory allocation, connection pooling, query planner tuning, automated monitoring, and workload-specific optimization, we achieved significant performance improvements across all our applications.",[24,1207,1208],{},"The key was understanding that configuration isn't a one-time setup—it's an ongoing process that requires monitoring, tuning, and adaptation to changing workloads.",[24,1210,1211],{},"If this article helped you understand PostgreSQL configuration optimization, we can help you implement these techniques in your own applications. At Ludulicious, we specialize in:",[34,1213,1214,1220,1226],{},[37,1215,1216,1219],{},[30,1217,1218],{},"Database Configuration Optimization",": From memory tuning to connection pooling",[37,1221,1222,1225],{},[30,1223,1224],{},"Database Performance Optimization",": From slow queries to indexing strategies",[37,1227,1228,1231],{},[30,1229,1230],{},"Custom Development",": Tailored solutions for your specific use case",[24,1233,1234],{},[30,1235,1236],{},"Ready to optimize your PostgreSQL configuration?",[24,1238,1239,1244],{},[1240,1241,1243],"a",{"href":1242},"\u002Fcontact","Contact us"," for a free consultation, or check out our other optimization guides:",[34,1246,1247,1253,1259,1265,1271,1277],{},[37,1248,1249],{},[1240,1250,1252],{"href":1251},"\u002Fblog\u002Fpostgresql-performance-strategy","PostgreSQL Performance Tuning: Strategic Lessons from Production",[37,1254,1255],{},[1240,1256,1258],{"href":1257},"\u002Fblog\u002Fduikersgids-spatial-search-optimization","Duikersgids: How I Made Spatial Search 55x Faster",[37,1260,1261],{},[1240,1262,1264],{"href":1263},"\u002Fblog\u002Frijmwoordenboek-phonetic-search-optimization","Rijmwoordenboek: Solving the 3-Second Phonetic Search Problem",[37,1266,1267],{},[1240,1268,1270],{"href":1269},"\u002Fblog\u002Frijmwoordenboek-caching-optimization","Rijmwoordenboek: Serving Pages Under 15ms with Better Caching",[37,1272,1273],{},[1240,1274,1276],{"href":1275},"\u002Fblog\u002Fupstreamads-fulltext-search-optimization","UpstreamAds: From 1.2s to 35ms Full-Text Search",[37,1278,1279],{},[1240,1280,1282],{"href":1281},"\u002Fblog\u002Fupstreamads-wal-optimization","UpstreamAds: Fixing Write Performance with WAL Optimization",[1284,1285],"hr",{},[24,1287,1288],{},[1289,1290,1291],"em",{},"This configuration optimization guide is based on real production experience with PostgreSQL across multiple applications. All performance numbers are from actual production systems.",[1293,1294,1295],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":60,"searchDepth":74,"depth":74,"links":1297},[1298,1299,1304,1308,1312,1313,1320,1321],{"id":21,"depth":74,"text":22},{"id":109,"depth":74,"text":110,"children":1300},[1301,1302,1303],{"id":114,"depth":80,"text":115},{"id":200,"depth":80,"text":201},{"id":339,"depth":80,"text":340},{"id":450,"depth":74,"text":451,"children":1305},[1306,1307],{"id":454,"depth":80,"text":455},{"id":486,"depth":80,"text":487},{"id":768,"depth":74,"text":769,"children":1309},[1310,1311],{"id":772,"depth":80,"text":773},{"id":804,"depth":80,"text":805},{"id":937,"depth":74,"text":938},{"id":1044,"depth":74,"text":1045,"children":1314},[1315,1316,1317,1318,1319],{"id":1048,"depth":80,"text":1049},{"id":1063,"depth":80,"text":1064},{"id":1078,"depth":80,"text":1079},{"id":1093,"depth":80,"text":1094},{"id":1108,"depth":80,"text":1109},{"id":1123,"depth":74,"text":1124},{"id":1201,"depth":74,"text":1202},[1323,14],"Database Optimization","2025-01-17","Learn the essential PostgreSQL configuration settings that impact performance, from memory allocation to connection pooling. Based on real production experience optimizing databases for high-traffic applications.","md",{"src":1328},"https:\u002F\u002Fpicsum.photos\u002Fid\u002F12\u002F640\u002F360",{"schema":1330},{"type":1331,"name":5,"description":1325,"image":1328,"author":1332,"datePublished":1324,"dateModified":1324,"publisher":1333,"steps":1336,"totalTime":1355,"estimatedCost":1356},"HowTo",{"name":8,"url":9},{"name":1334,"url":1335},"Ludulicious B.V.","https:\u002F\u002Fludulicious.nl",[1337,1340,1343,1346,1349,1352],{"name":1338,"text":1339},"Assess Current Configuration","Analyze existing PostgreSQL settings and identify optimization opportunities",{"name":1341,"text":1342},"Optimize Memory Settings","Configure shared_buffers, work_mem, and effective_cache_size for optimal performance",{"name":1344,"text":1345},"Implement Connection Pooling","Set up PgBouncer for efficient connection management",{"name":1347,"text":1348},"Tune Query Planner","Adjust cost parameters for your specific hardware and workload",{"name":1350,"text":1351},"Add Configuration Monitoring","Implement automated monitoring to prevent configuration drift",{"name":1353,"text":1354},"Apply Workload-Specific Tuning","Customize settings for read-heavy vs write-heavy applications","PT3D",{"currency":1357,"value":1358},"EUR","7000","\u002Fblog\u002Fpostgresql-configuration-optimization",{"title":5,"description":1325},"blog\u002F10.postgresql-configuration-optimization",[1363,1364,1365,1366,1367,1368],"PostgreSQL","Database Configuration","Performance Tuning","Memory Management","Connection Pooling","Monitoring","Ig2kb8u2ExyYhPIk3lZlAt84KK4NBD0LqXw0ZvIhVhM",[1371,1376],{"title":1372,"path":1373,"stem":1374,"description":1375,"children":-1},"Customer Portal Development: From 6 Months to 6 Weeks","\u002Fblog\u002Fcustomer-portal","blog\u002F1.customer-portal","Learn how we streamlined customer portal development, reducing build time from 6 months to 6 weeks using our proven architecture patterns, authentication strategies, and performance optimization techniques.",{"title":1377,"path":1378,"stem":1379,"description":1380,"children":-1},"Domain Structure Challenges: When Clients Don't Know What They Want","\u002Fblog\u002Fdomain-structure-challenges","blog\u002F11.domain-structure-challenges","Learn how to navigate domain structure challenges when clients are uncertain about their requirements. Real-world strategies for gathering requirements, managing scope creep, and delivering successful projects despite unclear initial specifications.",[]]