DEV Community

Kanahiro Iguchi
Kanahiro Iguchi

Posted on

Comparing sync/async PostgreSQL18(Amazon RDS)

Introduction

PostgreSQL 18 was released in September 2025.

https://www.postgresql.org/about/news/postgresql-18-released-3142/

Some folks are excited about the native support for UUID v7, which is indeed awesome — but personally, I think the introduction of async I/O is the real game changer.

In this article, as a humble member of the GIS cluster world, I’m going to benchmark PostGIS to see how much performance improvement we can actually expect from async in PostgreSQL 18.

Test Environment

To reduce variability in conditions, I picked a cloud environment. I often use AWS, Supabase, or Neon, but at the time of writing, PostgreSQL 18 is available only on Amazon RDS — so AWS wins this time. Default configuration:

  • Instance type: db.m7g.large
  • Storage: gp3

Enabling async I/O by tweaking the parameter group

Screenshot 2025-12-03 at 18.25.15.pngCreate paramter group

Screenshot 2025-12-03 at 18.26.56.pngset io_method to worker. io_uring isn't supported yet.

Screenshot 2025-12-03 at 18.28.04.pngAttach to RDS

We’ll prepare two RDS instances — one with async enabled and one without — execute the exact same query, and compare the execution plans.

Data & Query

Load Overture Maps Buildings dataset (Hokkaido region, ~3M polygons)

Implement a Web API that serves MVT tiles from this table

Issue tile requests and observe the behavior

This is not meant to be a super-strict benchmark — just trying to capture the overall trend (yes, that’s an excuse)

Data Import

ogr2ogr -f PostgreSQL postgresql://username:password@pg17.<suppressed>.ap-northeast-1.rds.amazonaws.com:5432/postgres buildings.parquet -progress
Enter fullscreen mode Exit fullscreen mode

Query

Create MVT tiles using ST_AsMVT:

// Using drizzle, so written in JavaScript syntax
const _sql = sql`
    WITH bounds AS (
      SELECT ST_TileEnvelope(${z}::int, ${x}::int, ${y}::int) AS geom
    ),
    mvtgeom AS (
      SELECT
        ST_AsMVTGeom(
          ST_Transform(buildings.geometry, 3857),
          bounds.geom,
          4096,
          256,
          true
        ) AS geom,
        buildings.ogc_fid
      FROM public.buildings, bounds
      WHERE 
        buildings.geometry && ST_Transform(bounds.geom, 4326)
        AND ST_Intersects(buildings.geometry, ST_Transform(bounds.geom, 4326))
    )
    SELECT ST_AsMVT(mvtgeom.*, 'layer', 4096, 'geom') FROM mvtgeom;
  `;
Enter fullscreen mode Exit fullscreen mode

Then I reviewed the execution plans. Below are results from the same query executed on PostgreSQL 18 with sync vs async.

many rows

{
  pg18Sync: [
    'Aggregate  (cost=2850.42..2850.43 rows=1 width=32) (actual time=2908.535..2908.536 rows=1.00 loops=1)',
    '  Buffers: shared hit=1913',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..1557.29 rows=94 width=146) (actual time=0.159..528.852 rows=20564.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000000C161404F90BC55CB4645400000000000C161408C938850D18845400000000080D761408C938850D18845400000000080D761404F90BC55CB4645400000000000C161404F90BC55CB464540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000000C161404F90BC55CB4645400000000000C161408C938850D18845400000000080D761408C938850D18845400000000080D761404F90BC55CB4645400000000000C161404F90BC55CB464540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000000C161404F90BC55CB4645400000000000C161408C938850D18845400000000080D761408C938850D18845400000000080D761404F90BC55CB4645400000000000C161404F90BC55CB464540'::geometry)",
    '        Rows Removed by Filter: 3',
    '        Index Searches: 1',
    '        Buffers: shared hit=1907',
    'Planning:',
    '  Buffers: shared hit=284',
    'Planning Time: 57.249 ms',
    'Execution Time: 2908.974 ms'
  ],
  pg18Async: [
    'Aggregate  (cost=4212.57..4212.58 rows=1 width=32) (actual time=382.600..382.601 rows=1.00 loops=1)',
    '  Buffers: shared hit=1940',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..2300.69 rows=139 width=146) (actual time=0.166..39.303 rows=20564.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000000C161404F90BC55CB4645400000000000C161408C938850D18845400000000080D761408C938850D18845400000000080D761404F90BC55CB4645400000000000C161404F90BC55CB464540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000000C161404F90BC55CB4645400000000000C161408C938850D18845400000000080D761408C938850D18845400000000080D761404F90BC55CB4645400000000000C161404F90BC55CB464540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000000C161404F90BC55CB4645400000000000C161408C938850D18845400000000080D761408C938850D18845400000000080D761404F90BC55CB4645400000000000C161404F90BC55CB464540'::geometry)",
    '        Rows Removed by Filter: 3',
    '        Index Searches: 1',
    '        Buffers: shared hit=1934',
    'Planning:',
    '  Buffers: shared hit=285',
    'Planning Time: 14.621 ms',
    'Execution Time: 383.089 ms'
  ]
}
{
  pg18Sync: [
    'Aggregate  (cost=35.31..35.32 rows=1 width=32) (actual time=0.017..0.018 rows=1.00 loops=1)',
    '  Buffers: shared hit=1',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..20.93 rows=1 width=146) (actual time=0.015..0.015 rows=0.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000000946140D8DD5F3F4ACA4540000000000094614053F777FC350B46400000000080AA614053F777FC350B46400000000080AA6140D8DD5F3F4ACA45400000000000946140D8DD5F3F4ACA4540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000000946140D8DD5F3F4ACA4540000000000094614053F777FC350B46400000000080AA614053F777FC350B46400000000080AA6140D8DD5F3F4ACA45400000000000946140D8DD5F3F4ACA4540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000000946140D8DD5F3F4ACA4540000000000094614053F777FC350B46400000000080AA614053F777FC350B46400000000080AA6140D8DD5F3F4ACA45400000000000946140D8DD5F3F4ACA4540'::geometry)",
    '        Index Searches: 1',
    '        Buffers: shared hit=1',
    'Planning Time: 0.228 ms',
    'Execution Time: 0.044 ms'
  ],
  pg18Async: [
    'Aggregate  (cost=35.31..35.32 rows=1 width=32) (actual time=0.015..0.016 rows=1.00 loops=1)',
    '  Buffers: shared hit=1',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..20.93 rows=1 width=146) (actual time=0.013..0.013 rows=0.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000000946140D8DD5F3F4ACA4540000000000094614053F777FC350B46400000000080AA614053F777FC350B46400000000080AA6140D8DD5F3F4ACA45400000000000946140D8DD5F3F4ACA4540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000000946140D8DD5F3F4ACA4540000000000094614053F777FC350B46400000000080AA614053F777FC350B46400000000080AA6140D8DD5F3F4ACA45400000000000946140D8DD5F3F4ACA4540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000000946140D8DD5F3F4ACA4540000000000094614053F777FC350B46400000000080AA614053F777FC350B46400000000080AA6140D8DD5F3F4ACA45400000000000946140D8DD5F3F4ACA4540'::geometry)",
    '        Index Searches: 1',
    '        Buffers: shared hit=1',
    'Planning Time: 0.238 ms',
    'Execution Time: 0.042 ms'
  ]
}
Enter fullscreen mode Exit fullscreen mode

relatively few rows

{
  pg18Sync: [
    'Aggregate  (cost=216.93..216.94 rows=1 width=32) (actual time=424.191..424.192 rows=1.00 loops=1)',
    '  Buffers: shared hit=341',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..120.05 rows=7 width=146) (actual time=0.133..6.610 rows=2663.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000060BB6140D5C3C9336FA145400000000060BB6140BB7A166C9FA945400000000030BE6140BB7A166C9FA945400000000030BE6140D5C3C9336FA145400000000060BB6140D5C3C9336FA14540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000060BB6140D5C3C9336FA145400000000060BB6140BB7A166C9FA945400000000030BE6140BB7A166C9FA945400000000030BE6140D5C3C9336FA145400000000060BB6140D5C3C9336FA14540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000060BB6140D5C3C9336FA145400000000060BB6140BB7A166C9FA945400000000030BE6140BB7A166C9FA945400000000030BE6140D5C3C9336FA145400000000060BB6140D5C3C9336FA14540'::geometry)",
    '        Rows Removed by Filter: 2',
    '        Index Searches: 1',
    '        Buffers: shared hit=335',
    'Planning:',
    '  Buffers: shared hit=284',
    'Planning Time: 34.613 ms',
    'Execution Time: 424.417 ms'
  ],
  pg18Async: [
    'Aggregate  (cost=186.66..186.67 rows=1 width=32) (actual time=105.514..105.515 rows=1.00 loops=1)',
    '  Buffers: shared hit=325',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..103.53 rows=6 width=146) (actual time=0.141..6.406 rows=2663.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000060BB6140D5C3C9336FA145400000000060BB6140BB7A166C9FA945400000000030BE6140BB7A166C9FA945400000000030BE6140D5C3C9336FA145400000000060BB6140D5C3C9336FA14540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000060BB6140D5C3C9336FA145400000000060BB6140BB7A166C9FA945400000000030BE6140BB7A166C9FA945400000000030BE6140D5C3C9336FA145400000000060BB6140D5C3C9336FA14540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000060BB6140D5C3C9336FA145400000000060BB6140BB7A166C9FA945400000000030BE6140BB7A166C9FA945400000000030BE6140D5C3C9336FA145400000000060BB6140D5C3C9336FA14540'::geometry)",
    '        Rows Removed by Filter: 2',
    '        Index Searches: 1',
    '        Buffers: shared hit=319',
    'Planning:',
    '  Buffers: shared hit=285',
    'Planning Time: 14.389 ms',
    'Execution Time: 105.727 ms'
  ]
}
{
  pg18Sync: [
    'Aggregate  (cost=35.31..35.32 rows=1 width=32) (actual time=380.362..380.363 rows=1.00 loops=1)',
    '  Buffers: shared hit=257',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..20.93 rows=1 width=146) (actual time=0.134..15.859 rows=2315.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E6100000010000000500000000000000C0B56140D5C3C9336FA1454000000000C0B56140BB7A166C9FA945400000000090B86140BB7A166C9FA945400000000090B86140D5C3C9336FA1454000000000C0B56140D5C3C9336FA14540'::geometry) AND (geometry && '0103000020E6100000010000000500000000000000C0B56140D5C3C9336FA1454000000000C0B56140BB7A166C9FA945400000000090B86140BB7A166C9FA945400000000090B86140D5C3C9336FA1454000000000C0B56140D5C3C9336FA14540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E6100000010000000500000000000000C0B56140D5C3C9336FA1454000000000C0B56140BB7A166C9FA945400000000090B86140BB7A166C9FA945400000000090B86140D5C3C9336FA1454000000000C0B56140D5C3C9336FA14540'::geometry)",
    '        Rows Removed by Filter: 1',
    '        Index Searches: 1',
    '        Buffers: shared hit=251',
    'Planning:',
    '  Buffers: shared hit=284',
    'Planning Time: 52.362 ms',
    'Execution Time: 380.598 ms'
  ],
  pg18Async: [
    'Aggregate  (cost=35.31..35.32 rows=1 width=32) (actual time=130.899..130.900 rows=1.00 loops=1)',
    '  Buffers: shared hit=254',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..20.93 rows=1 width=146) (actual time=0.125..5.462 rows=2315.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E6100000010000000500000000000000C0B56140D5C3C9336FA1454000000000C0B56140BB7A166C9FA945400000000090B86140BB7A166C9FA945400000000090B86140D5C3C9336FA1454000000000C0B56140D5C3C9336FA14540'::geometry) AND (geometry && '0103000020E6100000010000000500000000000000C0B56140D5C3C9336FA1454000000000C0B56140BB7A166C9FA945400000000090B86140BB7A166C9FA945400000000090B86140D5C3C9336FA1454000000000C0B56140D5C3C9336FA14540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E6100000010000000500000000000000C0B56140D5C3C9336FA1454000000000C0B56140BB7A166C9FA945400000000090B86140BB7A166C9FA945400000000090B86140D5C3C9336FA1454000000000C0B56140D5C3C9336FA14540'::geometry)",
    '        Rows Removed by Filter: 1',
    '        Index Searches: 1',
    '        Buffers: shared hit=248',
    'Planning:',
    '  Buffers: shared hit=285',
    'Planning Time: 23.726 ms',
    'Execution Time: 131.086 ms'
  ]
}
{
  pg18Sync: [
    'Aggregate  (cost=95.85..95.86 rows=1 width=32) (actual time=431.336..431.337 rows=1.00 loops=1)',
    '  Buffers: shared hit=336',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..53.97 rows=3 width=146) (actual time=0.130..7.223 rows=2919.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000090B86140D5C3C9336FA145400000000090B86140BB7A166C9FA945400000000060BB6140BB7A166C9FA945400000000060BB6140D5C3C9336FA145400000000090B86140D5C3C9336FA14540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000090B86140D5C3C9336FA145400000000090B86140BB7A166C9FA945400000000060BB6140BB7A166C9FA945400000000060BB6140D5C3C9336FA145400000000090B86140D5C3C9336FA14540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000090B86140D5C3C9336FA145400000000090B86140BB7A166C9FA945400000000060BB6140BB7A166C9FA945400000000060BB6140D5C3C9336FA145400000000090B86140D5C3C9336FA14540'::geometry)",
    '        Rows Removed by Filter: 4',
    '        Index Searches: 1',
    '        Buffers: shared hit=330',
    'Planning:',
    '  Buffers: shared hit=284',
    'Planning Time: 53.371 ms',
    'Execution Time: 431.572 ms'
  ],
  pg18Async: [
    'Aggregate  (cost=186.66..186.67 rows=1 width=32) (actual time=164.077..164.078 rows=1.00 loops=1)',
    '  Buffers: shared hit=355',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..103.53 rows=6 width=146) (actual time=0.129..11.601 rows=2919.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000090B86140D5C3C9336FA145400000000090B86140BB7A166C9FA945400000000060BB6140BB7A166C9FA945400000000060BB6140D5C3C9336FA145400000000090B86140D5C3C9336FA14540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000090B86140D5C3C9336FA145400000000090B86140BB7A166C9FA945400000000060BB6140BB7A166C9FA945400000000060BB6140D5C3C9336FA145400000000090B86140D5C3C9336FA14540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000090B86140D5C3C9336FA145400000000090B86140BB7A166C9FA945400000000060BB6140BB7A166C9FA945400000000060BB6140D5C3C9336FA145400000000090B86140D5C3C9336FA14540'::geometry)",
    '        Rows Removed by Filter: 4',
    '        Index Searches: 1',
    '        Buffers: shared hit=349',
    'Planning:',
    '  Buffers: shared hit=285',
    'Planning Time: 23.486 ms',
    'Execution Time: 164.316 ms'
  ]
}
{
  pg18Sync: [
    'Aggregate  (cost=35.31..35.32 rows=1 width=32) (actual time=93.973..93.974 rows=1.00 loops=1)',
    '  Buffers: shared hit=108',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..20.93 rows=1 width=146) (actual time=0.044..32.581 rows=1110.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000060BB61407873EE25089145400000000060BB614093170BC73C9945400000000030BE614093170BC73C9945400000000030BE61407873EE25089145400000000060BB61407873EE2508914540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000060BB61407873EE25089145400000000060BB614093170BC73C9945400000000030BE614093170BC73C9945400000000030BE61407873EE25089145400000000060BB61407873EE2508914540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000060BB61407873EE25089145400000000060BB614093170BC73C9945400000000030BE614093170BC73C9945400000000030BE61407873EE25089145400000000060BB61407873EE2508914540'::geometry)",
    '        Index Searches: 1',
    '        Buffers: shared hit=108',
    'Planning Time: 0.247 ms',
    'Execution Time: 94.014 ms'
  ],
  pg18Async: [
    'Aggregate  (cost=35.31..35.32 rows=1 width=32) (actual time=81.956..81.956 rows=1.00 loops=1)',
    '  Buffers: shared hit=109',
    '  ->  Index Scan using bq_rg_geometry_geom_idx on bq_rg f  (cost=0.41..20.93 rows=1 width=146) (actual time=0.048..12.595 rows=1110.00 loops=1)',
    "        Index Cond: ((geometry && '0103000020E610000001000000050000000000000060BB61407873EE25089145400000000060BB614093170BC73C9945400000000030BE614093170BC73C9945400000000030BE61407873EE25089145400000000060BB61407873EE2508914540'::geometry) AND (geometry && '0103000020E610000001000000050000000000000060BB61407873EE25089145400000000060BB614093170BC73C9945400000000030BE614093170BC73C9945400000000030BE61407873EE25089145400000000060BB61407873EE2508914540'::geometry))",
    "        Filter: st_intersects(geometry, '0103000020E610000001000000050000000000000060BB61407873EE25089145400000000060BB614093170BC73C9945400000000030BE614093170BC73C9945400000000030BE61407873EE25089145400000000060BB61407873EE2508914540'::geometry)",
    '        Index Searches: 1',
    '        Buffers: shared hit=109',
    'Planning Time: 0.223 ms',
    'Execution Time: 81.995 ms'
  ]
}
Enter fullscreen mode Exit fullscreen mode

Results

Honestly… I'm not sure how async affects on performance. However even when the execution plans look nearly identical, the async version tends to show shorter execution times. Because the difference in performance was too large, I suprised and I suspects there is a mistake on the benchmarking or not but in this time, I’ll conclude that async does provide some benefits!

If any experts out there can extract deeper insight from these results, please share your comments.

Subjectively, async seems to make a noticeable difference when the query hits a relatively large number of records. Since that’s a common pattern in PostGIS workloads, async I/O could end up being a meaningful performance boost in practice.

Top comments (0)