Thread: 600 million rows of data. Bad hardware or need partitioning?

600 million rows of data. Bad hardware or need partitioning?

From
Arya F
Date:
I have created the following table to duplicate my performance
numbers, but I have simplified the table for this question.

I'm running PostgreSQL 12 on the following hardware.

Dual Xeon Quad-Core E5320 1.86GHz
4GB of RAM

The table structure is

 id uuid
 address_api_url text
 check_timestamp timestamp with time zone
 address text
Indexes:
    "new_table_pkey" PRIMARY KEY, btree (id)
    "test_table_check_timestamp_idx" btree (check_timestamp)


Right now the table has 100 Million rows, but I expect it to reach
about 600-700 Million. I am faced with slow updates/inserts and the
issue is caused by the indices as it gets updates on each
insert/update, If I remove the indexes the insert performance remains
excellent with millions of rows.

To demonstrate the update performance I have constructed the following
query which updates the timestamp of 10000 rows

UPDATE test_table set check_timestamp = now() FROM(select id from
test_table limit 10000) AS subquery where test_table.id = subquery.id;

That update took about 1 minute and 44 seconds
Time: 104254.392 ms (01:44.254)

Below is the EXPLAIN ANALYZE


EXPLAIN ANALYZE UPDATE test_table set check_timestamp = now()
FROM(select id from test_table limit 10000) AS subquery where
test_table.id = subquery.id;

  QUERY PLAN

-------------------------------------------------------------------------------------------------------------------------------------------------------------
 Update on test_table  (cost=0.57..28234.86 rows=10000 width=160)
(actual time=102081.905..102081.905 rows=0 loops=1)
   ->  Nested Loop  (cost=0.57..28234.86 rows=10000 width=160) (actual
time=32.286..101678.652 rows=10000 loops=1)
         ->  Subquery Scan on subquery  (cost=0.00..514.96 rows=10000
width=56) (actual time=0.048..45.127 rows=10000 loops=1)
               ->  Limit  (cost=0.00..414.96 rows=10000 width=16)
(actual time=0.042..26.319 rows=10000 loops=1)
                     ->  Seq Scan on test_table test_table_1
(cost=0.00..4199520.04 rows=101204004 width=16) (actual
time=0.040..21.542 rows=10000 loops=1)
         ->  Index Scan using new_table_pkey on test_table
(cost=0.57..2.77 rows=1 width=92) (actual time=10.160..10.160 rows=1
loops=10000)
               Index Cond: (id = subquery.id)
 Planning Time: 0.319 ms
 Execution Time: 102081.967 ms
(9 rows)

Time: 102122.421 ms (01:42.122)



with the right hardware can one partition handle 600 millions of rows
with good insert/update performance? if so what kind of hardware
should I be looking at? Or would I need to create partitions? I'd like
to hear some recommendations.



Re: 600 million rows of data. Bad hardware or need partitioning?

From
Michael Lewis
Date:
What kinds of storage (ssd or old 5400 rpm)? What else is this machine running?

What configs have been customized such as work_mem or random_page_cost?

Re: 600 million rows of data. Bad hardware or need partitioning?

From
Arya F
Date:
On Sun, May 3, 2020 at 11:46 PM Michael Lewis <mlewis@entrata.com> wrote:
>
> What kinds of storage (ssd or old 5400 rpm)? What else is this machine running?

Not an SSD, but an old 1TB 7200 RPM HDD

> What configs have been customized such as work_mem or random_page_cost?

work_mem = 2403kB
random_page_cost = 1.1



Re: 600 million rows of data. Bad hardware or need partitioning?

From
David Rowley
Date:
On Mon, 4 May 2020 at 15:52, Arya F <arya6000@gmail.com> wrote:
>
> On Sun, May 3, 2020 at 11:46 PM Michael Lewis <mlewis@entrata.com> wrote:
> >
> > What kinds of storage (ssd or old 5400 rpm)? What else is this machine running?
>
> Not an SSD, but an old 1TB 7200 RPM HDD
>
> > What configs have been customized such as work_mem or random_page_cost?
>
> work_mem = 2403kB
> random_page_cost = 1.1

How long does it take if you first do:

SET enable_nestloop TO off;

If you find it's faster then you most likely have random_page_cost set
unrealistically low. In fact, I'd say it's very unlikely that a nested
loop join will be a win in this case when random pages must be read
from a mechanical disk, but by all means, try disabling it with the
above command and see for yourself.

If you set random_page_cost so low to solve some other performance
problem, then you may wish to look at the effective_cache_size
setting. Having that set to something realistic should allow indexes
to be used more in situations where they're likely to not require as
much random I/O from the disk.

David



Re: 600 million rows of data. Bad hardware or need partitioning?

From
Justin Pryzby
Date:
On Sun, May 03, 2020 at 11:51:44PM -0400, Arya F wrote:
> On Sun, May 3, 2020 at 11:46 PM Michael Lewis <mlewis@entrata.com> wrote:
> > What kinds of storage (ssd or old 5400 rpm)? What else is this machine running?
> 
> Not an SSD, but an old 1TB 7200 RPM HDD
> 
> > What configs have been customized such as work_mem or random_page_cost?
> 
> work_mem = 2403kB
> random_page_cost = 1.1

I mentioned in February and March that you should plan to set shared_buffers
to fit the indexes currently being updated.

Partitioning can help with that *if* the writes mostly affect 1-2 partitions at
a time (otherwise not).

On Wed, Feb 05, 2020 at 11:15:48AM -0600, Justin Pryzby wrote:
> > Would that work? Or any recommendations how I can achieve good performance
> > for a lot of writes?
> 
> Can you use partitioning so the updates are mostly affecting only one table at
> once, and its indices are of reasonable size, such that they can fit easily in
> shared_buffers.

On Sun, Mar 22, 2020 at 08:29:04PM -0500, Justin Pryzby wrote:
> On Sun, Mar 22, 2020 at 09:22:50PM -0400, Arya F wrote:
> > I have noticed that my write/update performance starts to dramatically
> > reduce after about 10 million rows on my hardware. The reason for the
> > slowdown is the index updates on every write/update.
> 
> It's commonly true that the indexes need to fit entirely in shared_buffers for
> good write performance.  I gave some suggestions here:
> https://www.postgresql.org/message-id/20200223101209.GU31889%40telsasoft.com



Re: 600 million rows of data. Bad hardware or need partitioning?

From
Arya F
Date:
On Mon, May 4, 2020 at 12:44 AM David Rowley <dgrowleyml@gmail.com> wrote:
> How long does it take if you first do:
>
> SET enable_nestloop TO off;

I tried this, but it takes much longer

Time: 318620.319 ms (05:18.620)

Below is the EXPLAIN ANALYZE


      QUERY PLAN

--------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Update on test_table  (cost=639.96..4581378.80 rows=10000 width=160)
(actual time=290593.159..290593.159 rows=0 loops=1)
   ->  Hash Join  (cost=639.96..4581378.80 rows=10000 width=160)
(actual time=422.313..194430.318 rows=10000 loops=1)
         Hash Cond: (test_table.id = subquery.id)
         ->  Seq Scan on test_table  (cost=0.00..4200967.98
rows=101238898 width=92) (actual time=296.970..177731.611
rows=101189271 loops=1)
         ->  Hash  (cost=514.96..514.96 rows=10000 width=56) (actual
time=125.312..125.312 rows=10000 loops=1)
               Buckets: 16384  Batches: 1  Memory Usage: 959kB
               ->  Subquery Scan on subquery  (cost=0.00..514.96
rows=10000 width=56) (actual time=0.030..123.031 rows=10000 loops=1)
                     ->  Limit  (cost=0.00..414.96 rows=10000
width=16) (actual time=0.024..121.014 rows=10000 loops=1)
                           ->  Seq Scan on test_table test_table_1
(cost=0.00..4200967.98 rows=101238898 width=16) (actual
time=0.021..120.106 rows=10000 loops=1)
 Planning Time: 0.304 ms
 JIT:
   Functions: 12
   Options: Inlining true, Optimization true, Expressions true, Deforming true
   Timing: Generation 2.178 ms, Inlining 155.980 ms, Optimization
100.611 ms, Emission 39.481 ms, Total 298.250 ms
 Execution Time: 290595.448 ms
(15 rows)


> If you find it's faster then you most likely have random_page_cost set
> unrealistically low. In fact, I'd say it's very unlikely that a nested
> loop join will be a win in this case when random pages must be read
> from a mechanical disk, but by all means, try disabling it with the
> above command and see for yourself.

It's much slower with SET enable_nestloop TO off. Any other suggestions?



Re: 600 million rows of data. Bad hardware or need partitioning?

From
Arya F
Date:
On Mon, May 4, 2020 at 5:21 AM Justin Pryzby <pryzby@telsasoft.com> wrote:

> I mentioned in February and March that you should plan to set shared_buffers
> to fit the indexes currently being updated.
>

The following command gives me

select pg_size_pretty (pg_indexes_size('test_table'));
 pg_size_pretty
----------------
 5216 MB
(1 row)


So right now, the indexes on that table are taking about 5.2 GB, if a
machine has 512 GB of RAM and SSDs, is it safe to assume I can achieve
the same update that takes 1.5 minutes in less than 5 seconds while
having 600 million rows of data without partitioning?



Re: 600 million rows of data. Bad hardware or need partitioning?

From
Justin Pryzby
Date:
On Tue, May 05, 2020 at 08:31:29PM -0400, Arya F wrote:
> On Mon, May 4, 2020 at 5:21 AM Justin Pryzby <pryzby@telsasoft.com> wrote:
> 
> > I mentioned in February and March that you should plan to set shared_buffers
> > to fit the indexes currently being updated.
> 
> The following command gives me
> 
> select pg_size_pretty (pg_indexes_size('test_table'));
>  pg_size_pretty >  5216 MB
> 
> So right now, the indexes on that table are taking about 5.2 GB, if a
> machine has 512 GB of RAM and SSDs, is it safe to assume I can achieve
> the same update that takes 1.5 minutes in less than 5 seconds while
> having 600 million rows of data without partitioning?

I am not prepared to guarantee server performance..

But, to my knowledge, you haven't configured shared_buffers at all.  Which I
think might be the single most important thing to configure for loading speed
(with indexes).

Couple months ago, you said your server had 4GB RAM, which isn't much, but if
shared_buffers is ~100MB, I think that deserves attention.

If you get good performance with a million rows and 32MB buffers, then you
could reasonably hope to get good performance (at least initially) with
100million rows and 320MB buffers.  Scale that up to whatever you expect your
index size to be.  Be conservative since you may need to add indexes later, and
you can expect they'll become bloated, so you may want to run a reindex job.

shared_buffers is frequently set to ~25% of RAM, and if you need to efficiently
use indexes larger than what that supports, then you should add RAM, or
implement partitioning.

-- 
Justin



Re: 600 million rows of data. Bad hardware or need partitioning?

From
Arya F
Date:
On Tue, May 5, 2020 at 9:37 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
>
> On Tue, May 05, 2020 at 08:31:29PM -0400, Arya F wrote:
> > On Mon, May 4, 2020 at 5:21 AM Justin Pryzby <pryzby@telsasoft.com> wrote:
> >
> > > I mentioned in February and March that you should plan to set shared_buffers
> > > to fit the indexes currently being updated.
> >
> > The following command gives me
> >
> > select pg_size_pretty (pg_indexes_size('test_table'));
> >  pg_size_pretty >  5216 MB
> >
> > So right now, the indexes on that table are taking about 5.2 GB, if a
> > machine has 512 GB of RAM and SSDs, is it safe to assume I can achieve
> > the same update that takes 1.5 minutes in less than 5 seconds while
> > having 600 million rows of data without partitioning?
>
> I am not prepared to guarantee server performance..
>
> But, to my knowledge, you haven't configured shared_buffers at all.  Which I
> think might be the single most important thing to configure for loading speed
> (with indexes).
>

Just  wanted to give an update. I tried this on a VPS with 8GB ram and
SSDs, the same query now takes 1.2 seconds! What a huge difference!
that's without making any changes to postgres.conf file. Very
impressive.