[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-post-nl-\u002Fblog\u002Fduikersgids-ruimtelijk-zoeken-optimalisatie-\u002Fblog\u002Fduikersgids-ruimtelijk-zoeken-optimalisatie":3,"blog-post-surround-nl-\u002Fblog\u002Fduikersgids-ruimtelijk-zoeken-optimalisatie-\u002Fblog\u002Fduikersgids-ruimtelijk-zoeken-optimalisatie":1067,"related-posts-nl-\u002Fblog\u002Fduikersgids-ruimtelijk-zoeken-optimalisatie-\u002Fblog\u002Fduikersgids-ruimtelijk-zoeken-optimalisatie":1074},{"id":4,"title":5,"authors":6,"badge":13,"body":15,"categories":1048,"date":1050,"description":1051,"extension":1052,"image":1053,"meta":1055,"navigation":152,"path":1056,"readingTime":105,"seo":1057,"stem":1058,"tags":1059,"__hash__":1066},"posts_nl\u002Fblog\u002F5.duikersgids-ruimtelijk-zoeken-optimalisatie.md","Duikersgids: Hoe Ik Ruimtelijk Zoeken 55x Sneller Maakte",[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 bij UpstreamAds en Partner bij Ludulicious B.V. met meer dan 20 jaar ervaring in softwareontwikkeling, gespecialiseerd in .NET Core, ServiceStack, C# en database design.",{"label":14},"Ruimtelijke Data",{"type":16,"value":17,"toc":1021},"minimark",[18,23,27,33,57,62,115,126,130,133,164,169,183,187,192,195,215,220,249,255,259,262,287,291,308,314,318,321,346,350,367,372,376,380,383,408,412,415,489,493,510,515,519,523,526,566,570,573,636,640,663,668,672,775,779,783,794,798,809,813,824,828,839,843,854,858,861,936,940,943,946,949,969,974,982,1008,1011,1017],[19,20,22],"h2",{"id":21},"het-probleem-ruimtelijke-queries-vermoorden-gebruikerservaring","Het Probleem: Ruimtelijke Queries Vermoorden Gebruikerservaring",[24,25,26],"p",{},"In 2019 had Duikersgids.nl te maken met een kritiek performance probleem. Gebruikers die zochten naar duikstekken in de buurt van hun locatie wachtten 2.5 seconden op resultaten. Voor een locatie-gebaseerde applicatie was dit onacceptabel.",[24,28,29],{},[30,31,32],"strong",{},"De Uitdaging:",[34,35,36,40,51,54],"ul",{},[37,38,39],"li",{},"50.000+ duikstekken met geografische coördinaten",[37,41,42,43,47,48],{},"Complexe ruimtelijke queries met ",[44,45,46],"code",{},"ST_DWithin"," en ",[44,49,50],{},"ST_Distance",[37,52,53],{},"Gebruikers die instant locatie-gebaseerde resultaten verwachten",[37,55,56],{},"Server die het moeilijk heeft onder belasting",[24,58,59],{},[30,60,61],{},"De Cijfers:",[63,64,69],"pre",{"className":65,"code":66,"language":67,"meta":68,"style":68},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- Deze query deed er 2.5+ seconden over\nSELECT ds.name, ds.location, ds.difficulty_level\nFROM dive_sites ds\nWHERE ST_DWithin(ds.location, ST_Point(4.5, 52.0), 10000)\n  AND ds.status = 'active'\nORDER BY ST_Distance(ds.location, ST_Point(4.5, 52.0))\nLIMIT 20;\n","sql","",[44,70,71,79,85,91,97,103,109],{"__ignoreMap":68},[72,73,76],"span",{"class":74,"line":75},"line",1,[72,77,78],{},"-- Deze query deed er 2.5+ seconden over\n",[72,80,82],{"class":74,"line":81},2,[72,83,84],{},"SELECT ds.name, ds.location, ds.difficulty_level\n",[72,86,88],{"class":74,"line":87},3,[72,89,90],{},"FROM dive_sites ds\n",[72,92,94],{"class":74,"line":93},4,[72,95,96],{},"WHERE ST_DWithin(ds.location, ST_Point(4.5, 52.0), 10000)\n",[72,98,100],{"class":74,"line":99},5,[72,101,102],{},"  AND ds.status = 'active'\n",[72,104,106],{"class":74,"line":105},6,[72,107,108],{},"ORDER BY ST_Distance(ds.location, ST_Point(4.5, 52.0))\n",[72,110,112],{"class":74,"line":111},7,[72,113,114],{},"LIMIT 20;\n",[24,116,117],{},[118,119],"img",{"alt":120,"className":121,"height":123,"src":124,"width":125},"Duikersgids performance monitoring",[122],"rounded-lg",600,"https:\u002F\u002Fpicsum.photos\u002Fid\u002F7\u002F1000\u002F600",1000,[19,127,129],{"id":128},"de-oorzaak-ontbrekende-ruimtelijke-indexen","De Oorzaak: Ontbrekende Ruimtelijke Indexen",[24,131,132],{},"Het probleem was duidelijk uit het execution plan:",[63,134,136],{"className":65,"code":135,"language":67,"meta":68,"style":68},"EXPLAIN ANALYZE SELECT * FROM dive_sites \nWHERE ST_DWithin(location, ST_Point(4.5, 52.0), 10000);\n\n-- Resultaat: Seq Scan on dive_sites (cost=0.00..1250.00 rows=5000 width=64)\n-- Execution time: 2500.123 ms\n",[44,137,138,143,148,154,159],{"__ignoreMap":68},[72,139,140],{"class":74,"line":75},[72,141,142],{},"EXPLAIN ANALYZE SELECT * FROM dive_sites \n",[72,144,145],{"class":74,"line":81},[72,146,147],{},"WHERE ST_DWithin(location, ST_Point(4.5, 52.0), 10000);\n",[72,149,150],{"class":74,"line":87},[72,151,153],{"emptyLinePlaceholder":152},true,"\n",[72,155,156],{"class":74,"line":93},[72,157,158],{},"-- Resultaat: Seq Scan on dive_sites (cost=0.00..1250.00 rows=5000 width=64)\n",[72,160,161],{"class":74,"line":99},[72,162,163],{},"-- Execution time: 2500.123 ms\n",[24,165,166],{},[30,167,168],{},"Wat er gebeurde:",[34,170,171,174,177,180],{},[37,172,173],{},"PostgreSQL deed een volledige tabel scan op 50.000+ records",[37,175,176],{},"Er bestonden geen ruimtelijke indexen voor geografische queries",[37,178,179],{},"Elke ruimtelijke operatie werd vanaf nul berekend",[37,181,182],{},"Geen optimalisatie voor veelgebruikte query patronen",[19,184,186],{"id":185},"de-oplossing-strategische-ruimtelijke-indexing","De Oplossing: Strategische Ruimtelijke Indexing",[188,189,191],"h3",{"id":190},"stap-1-maak-gist-ruimtelijke-index","Stap 1: Maak GiST Ruimtelijke Index",[24,193,194],{},"De eerste doorbraak kwam met een juiste ruimtelijke index:",[63,196,198],{"className":65,"code":197,"language":67,"meta":68,"style":68},"-- De GiST index die alles veranderde\nCREATE INDEX CONCURRENTLY idx_dive_sites_location \nON dive_sites USING GIST (location);\n",[44,199,200,205,210],{"__ignoreMap":68},[72,201,202],{"class":74,"line":75},[72,203,204],{},"-- De GiST index die alles veranderde\n",[72,206,207],{"class":74,"line":81},[72,208,209],{},"CREATE INDEX CONCURRENTLY idx_dive_sites_location \n",[72,211,212],{"class":74,"line":87},[72,213,214],{},"ON dive_sites USING GIST (location);\n",[24,216,217],{},[30,218,219],{},"Waarom Dit Werkt:",[34,221,222,228,240,243],{},[37,223,224,227],{},[44,225,226],{},"GIST (location)",": GiST indexen zijn specifiek geoptimaliseerd voor geometrische datatypes",[37,229,230,231,233,234,236,237],{},"Maakt snelle ruimtelijke operaties mogelijk zoals ",[44,232,46],{},", ",[44,235,50],{},", en ",[44,238,239],{},"ST_Contains",[37,241,242],{},"Gebruikt R-tree structuur intern voor efficiënte ruimtelijke range queries",[37,244,245,248],{},[44,246,247],{},"CONCURRENTLY"," maakt index creatie mogelijk zonder writes te blokkeren",[24,250,251,254],{},[30,252,253],{},"Direct Resultaat:"," Query tijd daalde van 2.5 seconden naar 800ms (3x verbetering)",[188,256,258],{"id":257},"stap-2-voeg-partiële-index-toe-voor-actieve-sites","Stap 2: Voeg Partiële Index Toe voor Actieve Sites",[24,260,261],{},"De meeste queries hadden alleen actieve duikstekken nodig, dus maakten we een partiële index:",[63,263,265],{"className":65,"code":264,"language":67,"meta":68,"style":68},"-- Partiële index voor actieve sites (enorme win!)\nCREATE INDEX CONCURRENTLY idx_dive_sites_active \nON dive_sites (created_at) \nWHERE status = 'active';\n",[44,266,267,272,277,282],{"__ignoreMap":68},[72,268,269],{"class":74,"line":75},[72,270,271],{},"-- Partiële index voor actieve sites (enorme win!)\n",[72,273,274],{"class":74,"line":81},[72,275,276],{},"CREATE INDEX CONCURRENTLY idx_dive_sites_active \n",[72,278,279],{"class":74,"line":87},[72,280,281],{},"ON dive_sites (created_at) \n",[72,283,284],{"class":74,"line":93},[72,285,286],{},"WHERE status = 'active';\n",[24,288,289],{},[30,290,219],{},[34,292,293,299,302,305],{},[37,294,295,298],{},[44,296,297],{},"WHERE status = 'active'",": Indexeert alleen rijen die aan de conditie voldoen",[37,300,301],{},"Dramatisch kleinere index grootte (alleen ~40.000 actieve sites vs 50.000+ totaal)",[37,303,304],{},"Snellere index scans en betere cache benutting",[37,306,307],{},"De meeste queries filteren toch op actieve status",[24,309,310,313],{},[30,311,312],{},"Resultaat:"," Query tijd verbeterde naar 400ms (6x verbetering van origineel)",[188,315,317],{"id":316},"stap-3-maak-covering-index-voor-veelgebruikte-queries","Stap 3: Maak Covering Index voor Veelgebruikte Queries",[24,319,320],{},"Ons meest voorkomende query patroon had locatie, duiktype en moeilijkheidsgraad nodig:",[63,322,324],{"className":65,"code":323,"language":67,"meta":68,"style":68},"-- Samengestelde index voor ons meest voorkomende query patroon\nCREATE INDEX CONCURRENTLY idx_dive_sites_location_type \nON dive_sites USING GIST (location) \nINCLUDE (dive_type, difficulty_level);\n",[44,325,326,331,336,341],{"__ignoreMap":68},[72,327,328],{"class":74,"line":75},[72,329,330],{},"-- Samengestelde index voor ons meest voorkomende query patroon\n",[72,332,333],{"class":74,"line":81},[72,334,335],{},"CREATE INDEX CONCURRENTLY idx_dive_sites_location_type \n",[72,337,338],{"class":74,"line":87},[72,339,340],{},"ON dive_sites USING GIST (location) \n",[72,342,343],{"class":74,"line":93},[72,344,345],{},"INCLUDE (dive_type, difficulty_level);\n",[24,347,348],{},[30,349,219],{},[34,351,352,358,361,364],{},[37,353,354,357],{},[44,355,356],{},"INCLUDE (dive_type, difficulty_level)",": Covering index bevat extra kolommen",[37,359,360],{},"Elimineert tabel lookups - alle benodigde data komt uit de index",[37,362,363],{},"GiST index handelt ruimtelijke operaties af, included kolommen leveren extra data",[37,365,366],{},"Perfect voor queries die locatie + metadata nodig hebben",[24,368,369,371],{},[30,370,312],{}," Query tijd daalde naar 200ms (12x verbetering van origineel)",[19,373,375],{"id":374},"de-game-changer-postgresql-partitioning","De Game Changer: PostgreSQL Partitioning",[188,377,379],{"id":378},"het-probleem-tijd-gebaseerde-queries-nog-altijd-traag","Het Probleem: Tijd-Gebaseerde Queries Nog Altijd Traag",[24,381,382],{},"Zelfs met ruimtelijke indexen waren queries die filterden op datum nog steeds traag:",[63,384,386],{"className":65,"code":385,"language":67,"meta":68,"style":68},"-- Deze query scantte nog steeds alles\nSELECT * FROM dive_sites \nWHERE ST_DWithin(location, ST_Point(4.5, 52.0), 10000)\n  AND created_at > '2023-01-01';  -- Scantte nog steeds alle partitions!\n",[44,387,388,393,398,403],{"__ignoreMap":68},[72,389,390],{"class":74,"line":75},[72,391,392],{},"-- Deze query scantte nog steeds alles\n",[72,394,395],{"class":74,"line":81},[72,396,397],{},"SELECT * FROM dive_sites \n",[72,399,400],{"class":74,"line":87},[72,401,402],{},"WHERE ST_DWithin(location, ST_Point(4.5, 52.0), 10000)\n",[72,404,405],{"class":74,"line":93},[72,406,407],{},"  AND created_at > '2023-01-01';  -- Scantte nog steeds alle partitions!\n",[188,409,411],{"id":410},"de-oplossing-native-partitioning","De Oplossing: Native Partitioning",[24,413,414],{},"PostgreSQL 10+ native partitioning loste dit op:",[63,416,418],{"className":65,"code":417,"language":67,"meta":68,"style":68},"-- PostgreSQL 10+: Native partitioning\nCREATE TABLE dive_sites_partitioned (\n    id SERIAL,\n    name VARCHAR(255),\n    location GEOGRAPHY(POINT, 4326),\n    created_at TIMESTAMP WITH TIME ZONE\n) PARTITION BY RANGE (created_at);\n\nCREATE TABLE dive_sites_2023 PARTITION OF dive_sites_partitioned\nFOR VALUES FROM ('2023-01-01') TO ('2024-01-01');\n\nCREATE TABLE dive_sites_2024 PARTITION OF dive_sites_partitioned\nFOR VALUES FROM ('2024-01-01') TO ('2025-01-01');\n",[44,419,420,425,430,435,440,445,450,455,460,466,472,477,483],{"__ignoreMap":68},[72,421,422],{"class":74,"line":75},[72,423,424],{},"-- PostgreSQL 10+: Native partitioning\n",[72,426,427],{"class":74,"line":81},[72,428,429],{},"CREATE TABLE dive_sites_partitioned (\n",[72,431,432],{"class":74,"line":87},[72,433,434],{},"    id SERIAL,\n",[72,436,437],{"class":74,"line":93},[72,438,439],{},"    name VARCHAR(255),\n",[72,441,442],{"class":74,"line":99},[72,443,444],{},"    location GEOGRAPHY(POINT, 4326),\n",[72,446,447],{"class":74,"line":105},[72,448,449],{},"    created_at TIMESTAMP WITH TIME ZONE\n",[72,451,452],{"class":74,"line":111},[72,453,454],{},") PARTITION BY RANGE (created_at);\n",[72,456,458],{"class":74,"line":457},8,[72,459,153],{"emptyLinePlaceholder":152},[72,461,463],{"class":74,"line":462},9,[72,464,465],{},"CREATE TABLE dive_sites_2023 PARTITION OF dive_sites_partitioned\n",[72,467,469],{"class":74,"line":468},10,[72,470,471],{},"FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');\n",[72,473,475],{"class":74,"line":474},11,[72,476,153],{"emptyLinePlaceholder":152},[72,478,480],{"class":74,"line":479},12,[72,481,482],{},"CREATE TABLE dive_sites_2024 PARTITION OF dive_sites_partitioned\n",[72,484,486],{"class":74,"line":485},13,[72,487,488],{},"FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');\n",[24,490,491],{},[30,492,219],{},[34,494,495,501,504,507],{},[37,496,497,500],{},[44,498,499],{},"PARTITION BY RANGE (created_at)",": Splitst tabel op datum ranges",[37,502,503],{},"Elke partition is een aparte fysieke tabel",[37,505,506],{},"PostgreSQL bepaalt automatisch welke partitions te scannen",[37,508,509],{},"Queries met datum filters scannen alleen relevante partitions",[24,511,512,514],{},[30,513,312],{}," Tijd-gefilterde queries daalden naar 100ms (25x verbetering)",[19,516,518],{"id":517},"de-finale-optimalisatie-parallelle-verwerking","De Finale Optimalisatie: Parallelle Verwerking",[188,520,522],{"id":521},"het-probleem-complexe-ruimtelijke-joins-cpu-gebonden","Het Probleem: Complexe Ruimtelijke Joins CPU-Gebonden",[24,524,525],{},"Complexe queries die nabijgelegen duikstekken vonden gebruikten maar één CPU core:",[63,527,529],{"className":65,"code":528,"language":67,"meta":68,"style":68},"-- Deze query gebruikte maar één CPU core\nSELECT ds1.name, ds2.name, \n       ST_Distance(ds1.location, ds2.location) as distance\nFROM dive_sites ds1, dive_sites ds2 \nWHERE ds1.id \u003C ds2.id \n  AND ST_DWithin(ds1.location, ds2.location, 1000)\nORDER BY distance;\n",[44,530,531,536,541,546,551,556,561],{"__ignoreMap":68},[72,532,533],{"class":74,"line":75},[72,534,535],{},"-- Deze query gebruikte maar één CPU core\n",[72,537,538],{"class":74,"line":81},[72,539,540],{},"SELECT ds1.name, ds2.name, \n",[72,542,543],{"class":74,"line":87},[72,544,545],{},"       ST_Distance(ds1.location, ds2.location) as distance\n",[72,547,548],{"class":74,"line":93},[72,549,550],{},"FROM dive_sites ds1, dive_sites ds2 \n",[72,552,553],{"class":74,"line":99},[72,554,555],{},"WHERE ds1.id \u003C ds2.id \n",[72,557,558],{"class":74,"line":105},[72,559,560],{},"  AND ST_DWithin(ds1.location, ds2.location, 1000)\n",[72,562,563],{"class":74,"line":111},[72,564,565],{},"ORDER BY distance;\n",[188,567,569],{"id":568},"de-oplossing-verbeterde-parallelle-verwerking","De Oplossing: Verbeterde Parallelle Verwerking",[24,571,572],{},"PostgreSQL 16+ parallelle verwerking configuratie:",[63,574,576],{"className":65,"code":575,"language":67,"meta":68,"style":68},"-- PostgreSQL 16+: Betere parallelle uitvoering\nSET max_parallel_workers_per_gather = 8;\nSET parallel_leader_participation = off;\nSET parallel_tuple_cost = 0.05;  -- Lagere kostendrempel\n\n-- Dezelfde query gebruikt nu alle CPU cores\nEXPLAIN (ANALYZE, BUFFERS, VERBOSE)\nSELECT ds1.name, ds2.name, \n       ST_Distance(ds1.location, ds2.location) as distance\nFROM dive_sites ds1, dive_sites ds2 \nWHERE ds1.id \u003C ds2.id \n  AND ST_DWithin(ds1.location, ds2.location, 1000)\nORDER BY distance;\n",[44,577,578,583,588,593,598,602,607,612,616,620,624,628,632],{"__ignoreMap":68},[72,579,580],{"class":74,"line":75},[72,581,582],{},"-- PostgreSQL 16+: Betere parallelle uitvoering\n",[72,584,585],{"class":74,"line":81},[72,586,587],{},"SET max_parallel_workers_per_gather = 8;\n",[72,589,590],{"class":74,"line":87},[72,591,592],{},"SET parallel_leader_participation = off;\n",[72,594,595],{"class":74,"line":93},[72,596,597],{},"SET parallel_tuple_cost = 0.05;  -- Lagere kostendrempel\n",[72,599,600],{"class":74,"line":99},[72,601,153],{"emptyLinePlaceholder":152},[72,603,604],{"class":74,"line":105},[72,605,606],{},"-- Dezelfde query gebruikt nu alle CPU cores\n",[72,608,609],{"class":74,"line":111},[72,610,611],{},"EXPLAIN (ANALYZE, BUFFERS, VERBOSE)\n",[72,613,614],{"class":74,"line":457},[72,615,540],{},[72,617,618],{"class":74,"line":462},[72,619,545],{},[72,621,622],{"class":74,"line":468},[72,623,550],{},[72,625,626],{"class":74,"line":474},[72,627,555],{},[72,629,630],{"class":74,"line":479},[72,631,560],{},[72,633,634],{"class":74,"line":485},[72,635,565],{},[24,637,638],{},[30,639,219],{},[34,641,642,648,654,660],{},[37,643,644,647],{},[44,645,646],{},"max_parallel_workers_per_gather = 8",": Staat tot 8 worker processen toe",[37,649,650,653],{},[44,651,652],{},"parallel_leader_participation = off",": Vermindert coördinatie overhead",[37,655,656,659],{},[44,657,658],{},"parallel_tuple_cost = 0.05",": Maakt PostgreSQL meer geneigd parallelle plannen te kiezen",[37,661,662],{},"Complexe ruimtelijke joins worden gesplitst over meerdere CPU cores",[24,664,665,667],{},[30,666,312],{}," Complexe queries verbeterden naar 45ms (55x verbetering van origineel)",[19,669,671],{"id":670},"performance-resultaten-samenvatting","Performance Resultaten Samenvatting",[673,674,675,691],"table",{},[676,677,678],"thead",{},[679,680,681,685,688],"tr",{},[682,683,684],"th",{},"Optimalisatie Stap",[682,686,687],{},"Query Tijd",[682,689,690],{},"Verbetering",[692,693,694,708,721,734,747,760],"tbody",{},[679,695,696,702,705],{},[697,698,699],"td",{},[30,700,701],{},"Origineel (Geen Indexen)",[697,703,704],{},"2.500ms",[697,706,707],{},"Baseline",[679,709,710,715,718],{},[697,711,712],{},[30,713,714],{},"GiST Ruimtelijke Index",[697,716,717],{},"800ms",[697,719,720],{},"3x sneller",[679,722,723,728,731],{},[697,724,725],{},[30,726,727],{},"Partiële Index (Actieve Sites)",[697,729,730],{},"400ms",[697,732,733],{},"6x sneller",[679,735,736,741,744],{},[697,737,738],{},[30,739,740],{},"Covering Index",[697,742,743],{},"200ms",[697,745,746],{},"12x sneller",[679,748,749,754,757],{},[697,750,751],{},[30,752,753],{},"Native Partitioning",[697,755,756],{},"100ms",[697,758,759],{},"25x sneller",[679,761,762,767,770],{},[697,763,764],{},[30,765,766],{},"Parallelle Verwerking",[697,768,769],{},"45ms",[697,771,772],{},[30,773,774],{},"55x sneller",[19,776,778],{"id":777},"belangrijke-lessen-geleerd","Belangrijke Lessen Geleerd",[188,780,782],{"id":781},"_1-ruimtelijke-data-vereist-speciale-indexen","1. Ruimtelijke Data Vereist Speciale Indexen",[34,784,785,788,791],{},[37,786,787],{},"Gewone B-tree indexen werken niet voor geografische data",[37,789,790],{},"GiST indexen zijn essentieel voor ruimtelijke operaties",[37,792,793],{},"Overweeg de specifieke ruimtelijke operaties die je nodig hebt",[188,795,797],{"id":796},"_2-partiële-indexen-zijn-krachtig","2. Partiële Indexen Zijn Krachtig",[34,799,800,803,806],{},[37,801,802],{},"Indexeer alleen data die je daadwerkelijk queryt",[37,804,805],{},"Dramatisch kleinere index grootte en betere performance",[37,807,808],{},"Perfect voor gefilterde datasets",[188,810,812],{"id":811},"_3-covering-indexen-elimineren-tabel-lookups","3. Covering Indexen Elimineren Tabel Lookups",[34,814,815,818,821],{},[37,816,817],{},"Voeg veelgebruikte kolommen toe aan indexen",[37,819,820],{},"Vermindert I\u002FO operaties significant",[37,822,823],{},"Vooral waardevol voor read-heavy workloads",[188,825,827],{"id":826},"_4-partitioning-oplost-tijd-gebaseerde-queries","4. Partitioning Oplost Tijd-Gebaseerde Queries",[34,829,830,833,836],{},[37,831,832],{},"Native partitioning biedt automatische partition pruning",[37,834,835],{},"Essentieel voor time-series ruimtelijke data",[37,837,838],{},"Schaalt goed naarmate data groeit",[188,840,842],{"id":841},"_5-parallelle-verwerking-schaalt-complexe-operaties","5. Parallelle Verwerking Schaalt Complexe Operaties",[34,844,845,848,851],{},[37,846,847],{},"Moderne PostgreSQL versies excelleren in parallelle uitvoering",[37,849,850],{},"Configureer instellingen voor je hardware",[37,852,853],{},"Monitor CPU benutting om effectiviteit te verifiëren",[19,855,857],{"id":856},"implementatie-checklist","Implementatie Checklist",[24,859,860],{},"Als je vergelijkbare ruimtelijke query performance problemen hebt:",[34,862,865,882,891,900,909,918,927],{"className":863},[864],"contains-task-list",[37,866,869,873,874,877,878,881],{"className":867},[868],"task-list-item",[870,871],"input",{"disabled":152,"type":872},"checkbox"," ",[30,875,876],{},"Analyseer je queries",": Gebruik ",[44,879,880],{},"EXPLAIN ANALYZE"," om bottlenecks te identificeren",[37,883,885,873,887,890],{"className":884},[868],[870,886],{"disabled":152,"type":872},[30,888,889],{},"Maak GiST indexen",": Voor alle geografische kolommen",[37,892,894,873,896,899],{"className":893},[868],[870,895],{"disabled":152,"type":872},[30,897,898],{},"Voeg partiële indexen toe",": Voor veelgebruikte gefilterde data",[37,901,903,873,905,908],{"className":902},[868],[870,904],{"disabled":152,"type":872},[30,906,907],{},"Overweeg covering indexen",": Voor queries die extra kolommen nodig hebben",[37,910,912,873,914,917],{"className":911},[868],[870,913],{"disabled":152,"type":872},[30,915,916],{},"Implementeer partitioning",": Voor tijd-gebaseerde ruimtelijke data",[37,919,921,873,923,926],{"className":920},[868],[870,922],{"disabled":152,"type":872},[30,924,925],{},"Configureer parallelle verwerking",": Voor complexe ruimtelijke operaties",[37,928,930,873,932,935],{"className":929},[868],[870,931],{"disabled":152,"type":872},[30,933,934],{},"Monitor performance",": Volg query tijden en resource gebruik",[19,937,939],{"id":938},"samenvatting","Samenvatting",[24,941,942],{},"Het optimaliseren van ruimtelijke queries in PostgreSQL vereist een multi-layer aanpak. Door GiST indexen, partiële indexing, covering indexen, native partitioning en parallelle verwerking te combineren, behaalden we een 55x performance verbetering voor Duikersgids.nl.",[24,944,945],{},"De sleutel was het begrijpen dat ruimtelijke data unieke vereisten heeft en gespecialiseerde optimalisatie technieken vereist. Generieke database optimalisatie aanpakken werken niet voor geografische queries.",[24,947,948],{},"Als dit artikel je hielp ruimtelijke query optimalisatie te begrijpen, kunnen we je helpen deze technieken te implementeren in je eigen applicaties. Bij Ludulicious specialiseren we ons in:",[34,950,951,957,963],{},[37,952,953,956],{},[30,954,955],{},"Ruimtelijke Data Oplossingen",": Geografische queries en locatie-gebaseerde applicaties",[37,958,959,962],{},[30,960,961],{},"Database Performance Optimalisatie",": Van langzame queries tot indexing strategieën",[37,964,965,968],{},[30,966,967],{},"Custom Development",": Op maat gemaakte oplossingen voor je specifieke use case",[24,970,971],{},[30,972,973],{},"Klaar om je ruimtelijke queries te optimaliseren?",[24,975,976,981],{},[977,978,980],"a",{"href":979},"\u002Fcontact","Neem contact op"," voor een gratis consultatie, of bekijk onze andere optimalisatie gidsen:",[34,983,984,990,996,1002],{},[37,985,986],{},[977,987,989],{"href":988},"\u002Fblog\u002Fpostgresql-performance-strategy","PostgreSQL Performance Tuning: Strategische Lessen uit Productie",[37,991,992],{},[977,993,995],{"href":994},"\u002Fblog\u002Frijmwoordenboek-fonetische-zoekoptimalisatie","Rijmwoordenboek: Het 3-Seconden Fonetische Zoekprobleem Oplossen",[37,997,998],{},[977,999,1001],{"href":1000},"\u002Fblog\u002Fupstreamads-fulltext-zoekoptimalisatie","UpstreamAds: Van 1.2s naar 35ms Full-Text Zoeken",[37,1003,1004],{},[977,1005,1007],{"href":1006},"\u002Fblog\u002Fpostgresql-configuratie-optimalisatie","PostgreSQL Configuratie: De Instellingen Die Ertoe Doen",[1009,1010],"hr",{},[24,1012,1013],{},[1014,1015,1016],"em",{},"Deze optimalisatie case study is gebaseerd op echte productie ervaring met Duikersgids.nl. Alle performance nummers komen van echte productie systemen.",[1018,1019,1020],"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":68,"searchDepth":81,"depth":81,"links":1022},[1023,1024,1025,1030,1034,1038,1039,1046,1047],{"id":21,"depth":81,"text":22},{"id":128,"depth":81,"text":129},{"id":185,"depth":81,"text":186,"children":1026},[1027,1028,1029],{"id":190,"depth":87,"text":191},{"id":257,"depth":87,"text":258},{"id":316,"depth":87,"text":317},{"id":374,"depth":81,"text":375,"children":1031},[1032,1033],{"id":378,"depth":87,"text":379},{"id":410,"depth":87,"text":411},{"id":517,"depth":81,"text":518,"children":1035},[1036,1037],{"id":521,"depth":87,"text":522},{"id":568,"depth":87,"text":569},{"id":670,"depth":81,"text":671},{"id":777,"depth":81,"text":778,"children":1040},[1041,1042,1043,1044,1045],{"id":781,"depth":87,"text":782},{"id":796,"depth":87,"text":797},{"id":811,"depth":87,"text":812},{"id":826,"depth":87,"text":827},{"id":841,"depth":87,"text":842},{"id":856,"depth":81,"text":857},{"id":938,"depth":81,"text":939},[1049,14],"Database Optimalisatie","2025-01-17","Leer hoe we PostgreSQL ruimtelijke queries optimaliseerden voor Duikersgids.nl, waardoor zoektijden daalden van 2.5 seconden naar 45ms met GiST indexen, partitioning en parallelle verwerkingstechnieken.","md",{"src":1054},"https:\u002F\u002Fpicsum.photos\u002Fid\u002F7\u002F640\u002F360",{},"\u002Fblog\u002Fduikersgids-ruimtelijk-zoeken-optimalisatie",{"title":5,"description":1051},"blog\u002F5.duikersgids-ruimtelijk-zoeken-optimalisatie",[1060,1061,1062,1063,1064,1065],"PostgreSQL","Ruimtelijke Queries","GiST Indexen","Geografische Data","Performance Optimalisatie","Duikersgids","ZVoh8vGNvSmZI_zoYe7lzUOSEMJEofabSTNprTNnW9g",[1068,1071],{"title":989,"path":988,"stem":1069,"description":1070,"children":-1},"blog\u002F4.postgresql-performance-strategy","Leer PostgreSQL performance optimalisatie strategieën uit echte productie workloads. Van versie 9.6 tot 17, ontdek de technieken die onze database performance met 10-55x verbeterden.",{"title":995,"path":994,"stem":1072,"description":1073,"children":-1},"blog\u002F6.rijmwoordenboek-fonetische-zoekoptimalisatie","Leer hoe we PostgreSQL fonetische zoekopdrachten optimaliseerden voor Van Dale Rijmwoordenboek, waardoor zoektijden daalden van 3.2 seconden naar 85ms met multi-layer indexing strategieën en B-tree deduplicatie.",[]]