Thread: Performance Tuning

Performance Tuning

From
Chris Kratz
Date:
Hello All,

In contrast to what we hear from most others on this list, we find our
database servers are mostly CPU bound.  We are wondering if this is because
we have postgres configured incorrectly in some way, or if we really need
more powerfull processor(s) to gain more performance from postgres.

We continue to tune our individual queries where we can, but it seems we still
are waiting on the db a lot in our app.  When we run most queries, top shows
the postmaster running at 90%+ constantly during the duration of the request.
The disks get touched occasionally, but not often.  Our database on disk is
around 2.6G and most of the working set remains cached in memory, hence the
few disk accesses.  All this seems to point to the need for faster
processors.

Our question is simply this, is it better to invest in a faster processor at
this point, or are there configuration changes to make it faster?  I've done
some testing with with 4x SCSI 10k and the performance didn't improve, in
fact it actually was slower the the sata drives marginally.  One of our
developers is suggesting we should compile postgres from scratch for this
particular processor, and we may try that.  Any other ideas?

-Chris

On this particular development server, we have:

Athlon XP,3000
1.5G Mem
4x Sata drives in Raid 0

Postgresql 7.4.5 installed via RPM running on Linux kernel 2.6.8.1

Items changed in the postgresql.conf:

tcpip_socket = true
max_connections = 32
port = 5432
shared_buffers = 12288        # min 16, at least max_connections*2, 8KB each
sort_mem=16384
vacuum_mem = 32768        # min 1024, size in KB
max_fsm_pages = 60000        # min max_fsm_relations*16, 6 bytes each
max_fsm_relations = 1000    # min 100, ~50 bytes each
effective_cache_size = 115200    # typically 8KB each
random_page_cost = 1        # units are one sequential page fetch cost


Re: Performance Tuning

From
John Arbash Meinel
Date:
Chris Kratz wrote:

>Hello All,
>
>In contrast to what we hear from most others on this list, we find our
>database servers are mostly CPU bound.  We are wondering if this is because
>we have postgres configured incorrectly in some way, or if we really need
>more powerfull processor(s) to gain more performance from postgres.
>
>
>
If everything is cached in ram, it's pretty easy to be CPU bound. You
very easily could be at this point if your database is only 2.6G and you
don't touch all the tables often.

I do believe that when CPU bound, the best thing to do is get faster CPUs.
...

>Our question is simply this, is it better to invest in a faster processor at
>this point, or are there configuration changes to make it faster?  I've done
>some testing with with 4x SCSI 10k and the performance didn't improve, in
>fact it actually was slower the the sata drives marginally.  One of our
>developers is suggesting we should compile postgres from scratch for this
>particular processor, and we may try that.  Any other ideas?
>
>-Chris
>
>On this particular development server, we have:
>
>Athlon XP,3000
>1.5G Mem
>4x Sata drives in Raid 0
>
>
>
I'm very surprised you are doing RAID 0. You realize that if 1 drive
goes out, your entire array is toast, right? I would recommend doing
either RAID 10 (0+1), or even Raid 5 if you don't do a lot of writes.

Probably most important, though is to look at the individual queries and
see what they are doing.

>Postgresql 7.4.5 installed via RPM running on Linux kernel 2.6.8.1
>
>Items changed in the postgresql.conf:
>
>tcpip_socket = true
>max_connections = 32
>port = 5432
>shared_buffers = 12288        # min 16, at least max_connections*2, 8KB each
>sort_mem=16384
>vacuum_mem = 32768        # min 1024, size in KB
>max_fsm_pages = 60000        # min max_fsm_relations*16, 6 bytes each
>max_fsm_relations = 1000    # min 100, ~50 bytes each
>effective_cache_size = 115200    # typically 8KB each
>random_page_cost = 1        # units are one sequential page fetch cost
>
>
Most of these seem okay to me, but random page cost is *way* too low.
This should never be tuned below 2.  I think this says "an index scan of
*all* rows is as cheap as a sequential scan of all rows." and that
should never be true.

What could actually be happening is that you are getting index scans
when a sequential scan would be faster.

I don't know what you would see, but what does "explain analyze select
count(*) from blah;" say. If it is an index scan, you have your machine
mistuned. select count(*) always grabs every row, and this is always
cheaper with a sequential scan.

John
=:->


Attachment

Re: Performance Tuning

From
Greg Stark
Date:
Chris Kratz <chris.kratz@vistashare.com> writes:

> We continue to tune our individual queries where we can, but it seems we still
> are waiting on the db a lot in our app.  When we run most queries, top shows
> the postmaster running at 90%+ constantly during the duration of the request.
> The disks get touched occasionally, but not often.  Our database on disk is
> around 2.6G and most of the working set remains cached in memory, hence the
> few disk accesses.  All this seems to point to the need for faster
> processors.

I would suggest looking at the top few queries that are taking the most
cumulative time on the processor. It sounds like the queries are doing a ton
of logical i/o on data that's cached in RAM. A few indexes might cut down on
the memory bandwidth needed to churn through all that data.

> Items changed in the postgresql.conf:
> ...
> random_page_cost = 1        # units are one sequential page fetch cost

This makes it nigh impossible for the server from ever making a sequential
scan when an index would suffice. What query made you do this? What plan did
it fix?


--
greg

Re: Performance Tuning

From
Chris Kratz
Date:
On Wednesday 09 February 2005 03:38 pm, John Arbash Meinel wrote:
>...
> I'm very surprised you are doing RAID 0. You realize that if 1 drive
> goes out, your entire array is toast, right? I would recommend doing
> either RAID 10 (0+1), or even Raid 5 if you don't do a lot of writes.

<grin>  Yeah, we know.  This is a development server and we drop and reload
databases regularly (sometimes several times a day).  In this case we don't
really care about the integrity of the data since it's for our developers to
test code against.  Also, the system is on a mirrored set of drives.  On our
live servers we have hardware raid 1 at this point for the data drives.  When
I/O becomes a bottleneck, we are planning on moving to Raid 10 for the data
and Raid 1 for the transaction log with as many drives as I can twist arms
for.  Up to this point it has been easier just to stuff the servers full of
memory and let the OS cache the db in memory.  We know that at some point
this will no longer work, but for now it is.

As a side note, I learned something very interesting for our developers here.
We had been doing a drop database and then a reload off a db dump from our
live server for test data.  This takes 8-15 minutes depending on the server
(the one above takes about 8 minutes).  I learned through testing that I can
use create database template some_other_database and make a duplicate in
about 2.5 minutes. which is a huge gain for us.  We can load a pristine copy,
make a duplicate, do our testing on the duplicate, drop the duplicate and
create a new duplicate in less then five mintes.

Cool.

> Probably most important, though is to look at the individual queries and
> see what they are doing.
>
> >Postgresql 7.4.5 installed via RPM running on Linux kernel 2.6.8.1
> >
> >Items changed in the postgresql.conf:
> >
> >tcpip_socket = true
> >max_connections = 32
> >port = 5432
> >shared_buffers = 12288        # min 16, at least max_connections*2, 8KB each
> >sort_mem=16384
> >vacuum_mem = 32768        # min 1024, size in KB
> >max_fsm_pages = 60000        # min max_fsm_relations*16, 6 bytes each
> >max_fsm_relations = 1000    # min 100, ~50 bytes each
> >effective_cache_size = 115200    # typically 8KB each
> >random_page_cost = 1        # units are one sequential page fetch cost
>
> Most of these seem okay to me, but random page cost is *way* too low.
> This should never be tuned below 2.  I think this says "an index scan of
> *all* rows is as cheap as a sequential scan of all rows." and that
> should never be true.

You caught me.  I actually tweaked that today after finding a page that
suggested doing that if the data was mostly in memory.  I have been running
it at 2, and since we didn't notice any improvement, it will be going back to
2.

> What could actually be happening is that you are getting index scans
> when a sequential scan would be faster.
>
> I don't know what you would see, but what does "explain analyze select
> count(*) from blah;" say. If it is an index scan, you have your machine
> mistuned. select count(*) always grabs every row, and this is always
> cheaper with a sequential scan.
>
> John
> =:->
With a random_page_cost set to 1, on a larger table a select count(*) nets
this...

                                                     QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=9848.12..9848.12 rows=1 width=0) (actual
time=4916.869..4916.872 rows=1 loops=1)
   ->  Seq Scan on answer  (cost=0.00..8561.29 rows=514729 width=0) (actual
time=0.011..2624.202 rows=514729 loops=1)
 Total runtime: 4916.942 ms
(3 rows)

Now here is a very curious thing.  If I turn on timing and run the count
without explain analyze, I get...

 count
--------
 514729
(1 row)

Time: 441.539 ms

How odd.  Running the explain adds 4.5s to it.  Running the explain again goes
back to almost 5s.  Now I wonder why that would be different.

Changing random cpu cost back to 2 nets little difference (4991.940ms for
explain and 496ms)  But we will leave it at that for now.

--
Chris Kratz
Systems Analyst/Programmer
VistaShare LLC
www.vistashare.com


Re: Performance Tuning

From
"Merlin Moncure"
Date:
> Hello All,
>
> In contrast to what we hear from most others on this list, we find our
> database servers are mostly CPU bound.  We are wondering if this is
> because
> we have postgres configured incorrectly in some way, or if we really
need
> more powerfull processor(s) to gain more performance from postgres.

Yes, many apps are not I/O bound (mine isn't).  Here are factors that
are likely to make your app CPU bound:

1. Your cache hit ratio is very high
2. You have a lot of concurrency.
3. Your queries are complex, for example, doing sorting or statistics
analysis
4. Your queries are simple, but the server has to process a lot of them
(transaction overhead becomes significant) sequentially.
5. You have context switching problems, etc.

On the query side, you can tune things down considerably...try and keep
sorting down to a minimum (order on keys, avoid distinct where possible,
use 'union all', not 'union').  Basically, reduce individual query time.

Other stuff:
For complex queries, use views to cut out plan generation.
For simple but frequently run queries (select a,b,c from t where k), use
parameterized prepared statements for a 50% cpu savings, this may not be
an option in some client interfaces.

On the hardware side, you will get improvements by moving to Opteron,
etc.

Merlin

Re: Performance Tuning

From
Chris Kratz
Date:
On Wednesday 09 February 2005 03:27 pm, you wrote:
---snip---
> > We continue to tune our individual queries where we can, but it seems we
> > still are waiting on the db a lot in our app.  When we run most queries,
> > top shows the postmaster running at 90%+ constantly during the duration
> > of the request.
>
> Is this for the duration of a single request or 90% constantly?

No, this is during the processing of a request.  The rest of the time, it sits
idle.

We thought we would post our config and see if there was something obvious we
were missing.  I expect the only real answer is to continue to optimise the
sql our app generates since compexity seems to be the issue.

> If it's a single request, odds are you're going through much more
> information than you need to. Lots of aggregate work (max / min) perhaps
> or count(*)'s where an approximation would do?

Yes, many of our queries heavily use common aggregates and grouping.  And the
explains bears out that we spend most of our time in sorts related to the
grouping, aggregating, etc.  The problem we often need to get multiple
records per person, but then summarize that data per person.  Our users want
Accurate, Fast and Complex.  It's hard to convince them they can only have 2
of the 3. :-)

> > Our question is simply this, is it better to invest in a faster processor
> > at this point, or are there configuration changes to make it faster?
> > I've done
>
> If it's for a single request, you cannot get single processors which are
> much faster than what you describe as having.
>
> Want to send us a few EXPLAIN ANALYZE's of your longer running queries?

Many (most) of our queries are dynamic based on what the user needs.
Searches, statistics gathering, etc are all common tasks our users do.

Here is an explain from a common search giving a list of people.  This runs in
about 4.2s (4.5s with web page generation) which is actually pretty amazing
when you think about what it does.  It's just that we are always looking for
speed in the web environment since concurrent usage can be high at times
making the server feel less responsive.  I'm looking at possibly moving this
into lazy materialized views at some point since I can't seem to make the sql
go much faster.

 Sort  (cost=8165.28..8198.09 rows=13125 width=324) (actual
time=4116.714..4167.915 rows=13124 loops=1)
   Sort Key: system_name_id, fullname_lfm_sort
   ->  GroupAggregate  (cost=6840.96..7267.53 rows=13125 width=324) (actual
time=2547.928..4043.255 rows=13124 loops=1)
         ->  Sort  (cost=6840.96..6873.78 rows=13125 width=324) (actual
time=2547.876..2603.938 rows=14115 loops=1)
               Sort Key: system_name_id, fullname_last_first_mdl, phone,
daytime_phone, email_address, fullname_lfm_sort, firstname, is_business, ssn,
inactive
               ->  Subquery Scan foo  (cost=5779.15..5943.21 rows=13125
width=324) (actual time=2229.877..2459.003 rows=14115 loops=1)
                     ->  Sort  (cost=5779.15..5811.96 rows=13125 width=194)
(actual time=2229.856..2288.350 rows=14115 loops=1)
                           Sort Key: dem.nameid, dem.name_float_lfm_sort
                           ->  Hash Left Join  (cost=2354.58..4881.40
rows=13125 width=194) (actual time=1280.523..2139.423 rows=14115 loops=1)
                                 Hash Cond: ("outer".relatednameid =
"inner".nameid)
                                 ->  Hash Left Join  (cost=66.03..1889.92
rows=13125 width=178) (actual time=576.228..1245.760 rows=14115 loops=1)
                                       Hash Cond: ("outer".nameid =
"inner".nameid)
                                       ->  Merge Left Join
(cost=0.00..1758.20 rows=13125 width=174) (actual time=543.056..1015.657
rows=13124 loops=1)
                                             Merge Cond: ("outer".inactive =
"inner".validanswerid)
                                             ->  Index Scan using
namemaster_inactive_idx on namemaster dem  (cost=0.00..3714.19 rows=13125
width=163) (actual time=0.594..188.219 rows=13124 loops=1)
                                                   Filter: (programid = 55)
                                             ->  Index Scan using
validanswerid_pk on validanswer ina  (cost=0.00..1103.61 rows=46367 width=19)
(actual time=0.009..360.218 rows=26005 loops=1)
                                       ->  Hash  (cost=65.96..65.96 rows=31
width=8) (actual time=33.053..33.053 rows=0 loops=1)
                                             ->  Nested Loop
(cost=0.00..65.96 rows=31 width=8) (actual time=0.078..25.047 rows=1874
loops=1)
                                                   ->  Index Scan using
relationship_programid on relationship s  (cost=0.00..3.83 rows=1 width=4)
(actual time=0.041..0.047 rows=1 loops=1)
                                                         Index Cond:
(programid = 55)
                                                         Filter:
(inter_agency_id = 15530)
                                                   ->  Index Scan using
"relationshipdetail_relatio-4" on relationshipdetail r  (cost=0.00..61.17
rows=77 width=12) (actual time=0.017..9.888 rows=1874 loops=1)
                                                         Index Cond:
(r.relationshipid = "outer".relationshipid)
                                 ->  Hash  (cost=2142.84..2142.84 rows=58284
width=24) (actual time=704.197..704.197 rows=0 loops=1)
                                       ->  Seq Scan on namemaster rln155301
(cost=0.00..2142.84 rows=58284 width=24) (actual time=0.015..402.784
rows=58284 loops=1)
 Total runtime: 4228.945 ms

Re: Performance Tuning

From
Chris Kratz
Date:
On Wednesday 09 February 2005 03:59 pm, Greg Stark wrote:
> Chris Kratz <chris.kratz@vistashare.com> writes:
> > We continue to tune our individual queries where we can, but it seems we
> > still are waiting on the db a lot in our app.  When we run most queries,
> > top shows the postmaster running at 90%+ constantly during the duration
> > of the request. The disks get touched occasionally, but not often.  Our
> > database on disk is around 2.6G and most of the working set remains
> > cached in memory, hence the few disk accesses.  All this seems to point
> > to the need for faster processors.
>
> I would suggest looking at the top few queries that are taking the most
> cumulative time on the processor. It sounds like the queries are doing a
> ton of logical i/o on data that's cached in RAM. A few indexes might cut
> down on the memory bandwidth needed to churn through all that data.

Hmmm, yes we continue to use indexes judiciously.  I actually think we've
overdone it in some cases since inserts are starting to slow in some critical
areas.

> > Items changed in the postgresql.conf:
> > ...
> > random_page_cost = 1        # units are one sequential page fetch cost
>
> This makes it nigh impossible for the server from ever making a sequential
> scan when an index would suffice. What query made you do this? What plan
> did it fix?

Yes, it got set back to 2.  I was testing various settings suggested by a
posting in the archives and that one didn't get reset.

Re: Performance Tuning

From
Chris Kratz
Date:
On Wednesday 09 February 2005 05:08 pm, Merlin Moncure wrote:
> > Hello All,
> >
> > In contrast to what we hear from most others on this list, we find our
> > database servers are mostly CPU bound.  We are wondering if this is
> > because
> > we have postgres configured incorrectly in some way, or if we really
>
> need
>
> > more powerfull processor(s) to gain more performance from postgres.
>
> Yes, many apps are not I/O bound (mine isn't).  Here are factors that
> are likely to make your app CPU bound:
>
> 1. Your cache hit ratio is very high
> 2. You have a lot of concurrency.
> 3. Your queries are complex, for example, doing sorting or statistics
> analysis

For now, it's number 3.  Relatively low usage, but very complex sql.

> 4. Your queries are simple, but the server has to process a lot of them
> (transaction overhead becomes significant) sequentially.
> 5. You have context switching problems, etc.
>
> On the query side, you can tune things down considerably...try and keep
> sorting down to a minimum (order on keys, avoid distinct where possible,
> use 'union all', not 'union').  Basically, reduce individual query time.
>
> Other stuff:
> For complex queries, use views to cut out plan generation.
> For simple but frequently run queries (select a,b,c from t where k), use
> parameterized prepared statements for a 50% cpu savings, this may not be
> an option in some client interfaces.

Prepared statements are not something we've tried yet.  Perhaps we should look
into that in cases where it makes sense.

>
> On the hardware side, you will get improvements by moving to Opteron,
> etc.
>
> Merlin

Well, that's what we were looking for.

---

It sounds like our configuration as it stands is probably about as good as we
are going to get with the hardware we have at this point.

We are cpu bound reflecting the fact that we tend to have complex statements
doing aggregates, sorts and group bys.

The solutions appear to primarily be:
1. Going to faster hardware of which probably Opterons would be about the only
choice.  And even that probably won't be a huge difference.
2. Moving to more materialized views and prepared statements where we can.
3. Continue to tweak the sql behind our app.

Re: Performance Tuning

From
PFC
Date:
> As a side note, I learned something very interesting for our developers
> here.
> We had been doing a drop database and then a reload off a db dump from
> our
> live server for test data.  This takes 8-15 minutes depending on the
> server
> (the one above takes about 8 minutes).  I learned through testing that I
> can
> use create database template some_other_database and make a duplicate in
> about 2.5 minutes. which is a huge gain for us.  We can load a pristine
> copy,
> make a duplicate, do our testing on the duplicate, drop the duplicate and
> create a new duplicate in less then five mintes.

    I think thats because postgres just makes a file copy from the template.
Thus you could make it 2x faster if you put the template in another
tablespace on another drive.

Re: Performance Tuning

From
Mike Rylander
Date:
On Wed, 9 Feb 2005 17:30:41 -0500, Chris Kratz
<chris.kratz@vistashare.com> wrote:
> The solutions appear to primarily be:
> 1. Going to faster hardware of which probably Opterons would be about the only
> choice.  And even that probably won't be a huge difference.

I'd beg to differ on that last part.  The difference between a 3.6GHz
Xeon and a 2.8GHz Opteron is ~150% speed increase on the Opteron on my
CPU bound app.  This is because the memory bandwidth on the Opteron is
ENORMOUS compared to on the Xeon.  Add to that the fact that you
actually get to use more than about 2G of RAM directly and you've got
the perfect platform for a high speed database on a budget.

> 2. Moving to more materialized views and prepared statements where we can.

Definitely worth investigating.  I wish I could, but I can't get my
customers to even consider slightly out of date stats.... :(

> 3. Continue to tweak the sql behind our app.

Short of an Opteron based system, this is by far your best bet.

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

Re: Performance Tuning

From
PFC
Date:
>> 2. Moving to more materialized views and prepared statements where we
>> can.
>
> Definitely worth investigating.  I wish I could, but I can't get my
> customers to even consider slightly out of date stats.... :(

    Put a button 'Stats updated every hour', which gives the results in 0.1
seconds, and a button 'stats in real time' which crunches 10 seconds
before displaying the page... if 90% of the people click on the first one
you save a lot of CPU.

    Seems like people who hit Refresh every 10 seconds to see an earnings
graph creep up by half a pixel every time... but it seems it's moving !

    More seriously, you can update your stats in near real time with a
materialized view, there are two ways :
    - ON INSERT / ON UPDATE triggers which update the stats in real time
based on each modification
    - Have statistics computed for everything until some point in time (like
an hour ago) and only compute and add stats on the records added or
modified since (but it does not work very well for deleted records...)

Re: Performance Tuning

From
Christopher Browne
Date:
The world rejoiced as lists@boutiquenumerique.com (PFC) wrote:
>> As a side note, I learned something very interesting for our
>> developers  here.
>> We had been doing a drop database and then a reload off a db dump
>> from  our
>> live server for test data.  This takes 8-15 minutes depending on the
>> server
>> (the one above takes about 8 minutes).  I learned through testing
>> that I  can
>> use create database template some_other_database and make a duplicate in
>> about 2.5 minutes. which is a huge gain for us.  We can load a
>> pristine  copy,
>> make a duplicate, do our testing on the duplicate, drop the duplicate and
>> create a new duplicate in less then five mintes.
>
>     I think thats because postgres just makes a file copy from the
> template.  Thus you could make it 2x faster if you put the template
> in another tablespace on another drive.

I had some small amusement today trying this feature out in one of our
environments today...

We needed to make a copy of one of the databases we're replicating for
the sysadmins to use for some testing.

I figured using the "template" capability was:
 a) Usefully educational to one of the other DBAs, and
 b) Probably a quick way to copy the data over.

We shortly discovered that we had to shut off the Slony-I daemon in
order to get exclusive access to the database; no _big_ deal.

At that point, he hit ENTER, and rather quickly saw...
CREATE DATABASE.

We then discovered that the sysadmins wanted the test DB to be on one
of the other servers.  Oops.  Oh, well, we'll have to do this on the
other server; no big deal.

Entertainment ensued...  "My, that's taking a while..."  At about the
point that we started thinking there might be a problem...

CREATE DATABASE

The entertainment was that the first box is one of those spiffy new
4-way Opteron boxes, whilst the "slow" one was a 4-way Xeon...  Boy,
those Opterons are faster...
--
output = reverse("moc.liamg" "@" "enworbbc")
http://cbbrowne.com/info/rdbms.html
"No matter how far you have gone on the wrong road, turn back."
-- Turkish proverb

Re: Performance Tuning

From
Rod Taylor
Date:
On Wed, 2005-02-09 at 15:01 -0500, Chris Kratz wrote:
> Hello All,
>
> In contrast to what we hear from most others on this list, we find our
> database servers are mostly CPU bound.  We are wondering if this is because
> we have postgres configured incorrectly in some way, or if we really need
> more powerfull processor(s) to gain more performance from postgres.

Not necessarily. I had a very disk bound system, bought a bunch of
higher end equipment (which focuses on IO) and now have a (faster) but
CPU bound system.

It's just the way the cookie crumbles.

Some things to watch for are large calculations which are easy to move
client side, such as queries that sort for display purposes. Or data
types which aren't really required (using numeric where an integer would
do).

> We continue to tune our individual queries where we can, but it seems we still
> are waiting on the db a lot in our app.  When we run most queries, top shows
> the postmaster running at 90%+ constantly during the duration of the request.

Is this for the duration of a single request or 90% constantly?

If it's a single request, odds are you're going through much more
information than you need to. Lots of aggregate work (max / min) perhaps
or count(*)'s where an approximation would do?

> Our question is simply this, is it better to invest in a faster processor at
> this point, or are there configuration changes to make it faster?  I've done

If it's for a single request, you cannot get single processors which are
much faster than what you describe as having.

Want to send us a few EXPLAIN ANALYZE's of your longer running queries?

--
Rod Taylor <rbt@sitesell.com>