TLDR: I want to bulk insert into many initially empty tables at once, without the performance of each insert being affected too badly. I seem to be hitting a performance bottleneck I can't yet explain. I am looking for performance debugging avenues or advice on parallel inserts in Postgres.

I am prototyping a system and trying to import data into many Postgres tables at once as fast as possible. My example table has 100 columns (all short strings) and 20k rows. I can insert this data into a fresh Postgres table in around 30 seconds (*). However, if I run the same insert script 10 times against different (unrelated) tables, the performance of each individual insert drops dramatically - up to around 3.5 minutes for each table. The total insert (3 tables per process, 10 processes) takes around 8 minutes, so I am getting less than 2x speedup by running these queries in parallel vs. running them all one after the other for 30s each. I was hoping to get ~10x speedup with 10 parallel unrelated imports, until I reach some resource limit of the database host.

I am using an SSD-based Google Cloud SQL database with Postgres 12, 20vcpus and 75GB memory. While handling my 10 parallel fills, it uses less than 15% CPU, around 200 write ops per second (I am not clear what counts as one op in GCP), and around 10-15MiB/s data ingress. None of these feel like they should be a bottlneck for a relatively beefy host instance, but I can't understand what is slowing down these queries.

What should I check to investigate this bottleneck, and what should I be doing to import many tables in parallel efficiently?

My ideal solution would be to use 10x as much CPU (or whatever other resource) on the host instance, and complete the 10 parallel table loads in roughly the same time as a single one. I am hoping to be able to reach around 200 imports in parallel, even if it has to be spread over a few separate database hosts (non-ideally).

It seems clear that it is not (yet) CPU bound; att the tables at once can fit into memory a few times over, so it is not memory bound. I would be surprised to find it is IO or disk bound at this scale, but I don't know how to check or investigate these.

All the information I have been able to find has been about improving the speed of a single-table insert. Some examples:

I haven't found anything addressing concurrent inserts into separate tables or databases on the same host.

I also tried having my 10 client processes import data into differewnt databases to each other, with almost exactly the same performance.

I tried switching my tables to be UNLOGGED. This made a big improvement to the speed when running 10 imports in parallel (down to around 45 seconds), but running 20 imports immediately jumped back up to 2-4 minutes per table.

(*) Currently for each single-table load I am using a single insert statement with all the rows at once:

insert into test_table values ('id1', 'Sample data 1', ...), ... ('idN', 'Sample data N', ...);

I have also tried bulk loading from a CSV file via copy, inserting in batches of 100 or 1000 records at a time, and increasing the power of the host instance, none of which makes any significant difference on my example. The tables have an externally-specified primary key and no other constraints. But improving the performance of a single-table import is not what this question is about!

  • 1
    Find out where the bottleneck is with tools like vmstat and perf - ah, wait a minute, you are using a hosted database. Too bad. Hosted database are great for reducing the maintenance burden, but bad when it comes to diagnosing a performance problem. Can you add EXPLAIN (ANALYZE, BUFFERS) output for one of the slow inserts to the question? Do you see any wait_events in pg_stat_activity? Oct 22, 2020 at 9:48
  • I can reproduce the same issue on my local machine, so I can try vmstat and perf there (as well as the others), thanks! Any tips on where to start?
    – Ben
    Oct 22, 2020 at 10:10
  • 1
    See in vmstat if you are bottlenecked on the OS. Check with EXPLAIN (ANALYZE, BUFFERS) why the statement takes a long time. Look at the wait_events to see if you are stalled. perf will tell you where in PostgreSQL the time is spent. You should also edit the question and tell us the exact sequence of statements, whether they are running in a single transaction or not, if they are slow right away or after a while, ... Oct 22, 2020 at 10:42


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.