Re: [PERFORM] Large Database Design Help

2006-02-22 Thread Orion
I just wanted to thank everyone for your input on my question.  You've
given me a lot of tools to solve my problem here.

Orion

---(end of broadcast)---
TIP 4: Have you searched our list archives?

   http://archives.postgresql.org


Re: [PERFORM] Large Database Design Help

2006-02-10 Thread Markus Schaber
Hi, Greg,

Greg Stark wrote:

(Aside question: if I were to find a way to use COPY and I were loading
data on a single client_id, would dropping just the indexes for that client_id
accelerate the load?)
 Dropping indexes would accelerate the load but unless you're loading a large
 number of records relative the current size I'm not sure it would be a win
 since you would then have to rebuild the index for the entire segment.

And, additionally, rebuilding a partial index with WHERE client_id=42
needs a full table scan, which is very slow, so temporarily dropping the
indices will not be useful if you merge the tables.

Btw, I don't know whether PostgreSQL can make use of partial indices
when building other partial indices. If yes, you could temporarily drop
all but one of the partial indices for a specific client.


HTH,
Markus

-- 
Markus Schaber | Logical TrackingTracing International AG
Dipl. Inf. | Software Development GIS

Fight against software patents in EU! www.ffii.org www.nosoftwarepatents.org

---(end of broadcast)---
TIP 4: Have you searched our list archives?

   http://archives.postgresql.org


Re: [PERFORM] Large Database Design Help

2006-02-10 Thread Markus Schaber
Hi, Henry,

Orion Henry wrote:

 1) The database is very large, the largest table has 40 million tuples.

I'm afraid this doesn't qualify as '_very_ large' yet, but it
definitively is large enough to have some deep thoughts about it. :-)

 1) The data is easily partitionable by client ID.  In an attempt to keep
 the indexes small and the inserts fast one table was made per client
 ID.  Thus the primary table in the database (the one with 40 million
 tuples) is really 133 tables each ending with a three digit suffix.  
 The largest of these client tables has 8 million of the 40 million
 tuples.   The system started with around a half dozen clients and is now
 a huge pain to manage with so many tables.  I was hoping new hardware
 and new postgres features would allow for this data to be merged safely
 into a single table.

It possibly is a good idea to merge them.

If you decide to keep them separated for whatever reason, you might want
to use schemas instead of three digit suffixes. Together with
appropriate named users or 'set search_path', this may help you to
simplify your software.

In case you want to keep separate tables, but need some reports touching
all tables from time to time, table inheritance may help you. Just
create a base table, and then inherit all user specific tables from that
base table. Of course, this can be combined with the schema approach by
having the child tables in their appropriate schemas.

 2) The imports are not done inside of transactions.  I'm assuming the
 system designers excluded this for a reason.  Will I run into problems
 performing tens of thousands of inserts and updates inside a single
 transaction?

Yes, it should give you a huge boost. Every commit has to flush the WAL
out to disk, which takes at least one disk spin. So on a simple 7200 RPM
disk, you cannot have more than 120 transactions/second.

It may make sense to split such a bulk load into transactions of some
tens of thousands of rows, but that depends on how easy it is for your
application to resume in the middle of the bulk if the connection
aborts, and how much concurrent access you have on the backend.

 3) The current code that bulk loads data into the database is a loop
 that looks like this:
 
$result = exe(INSERT INTO $table ($name_str) SELECT
 $val_str WHERE NOT EXISTS (SELECT 1 FROM $table WHERE $keys));
if ($result == 0)
{
$result = exe(UPDATE $table SET $non_keys WHERE
 $keys);
} 
 Is there a faster way to bulk load data when it's not known ahead of
 time if it's a new record or an updated record?

Perhaps the easiest way might be to issue the update first. Update
returns a row count of the updated rows. If it is 0, you have to insert
the row.

This can even be encapsulated into a before insert trigger on the
table, which tries the update and ignores the insert if the update
succeeded. This way, you can even use COPY on the client side.

We're using this approach for one of our databases, where a client side
crash can result in occasional duplicates being COPYed to the table.

 Dual Opteron 246, 4 disk SCSI RAID5, 4GB of RAM

For lots non-read-only database workloads, RAID5 is a performance
killer. Raid 1/0 might be better, or having two mirrors of two disks
each, the first mirror holding system, swap, and the PostgreSQL WAL
files, the second one holding the data. Don't forget to tune the
postgresql settings appropriately. :-)

 # du -sh /var/lib/postgres/data/
 16G /var/lib/postgres/data/ 

Your database seems to be small enough to fit on a single disk, so the
two mirrors approach I described above will be the best IMHO.

 ( the current database is PG 7.4 - I intend to upgrade it to 8.1 if and
 when I do this refactoring )

This is a very good idea, 8.1 is miles ahead of 7.4 in many aspects.

 ( the current OS is Debian Unstable but I intend to be running RHEL 4.0
 if and when I do this refactoring )

This should not make too much difference.

HTH,
Markus
-- 
Markus Schaber | Logical TrackingTracing International AG
Dipl. Inf. | Software Development GIS

Fight against software patents in EU! www.ffii.org www.nosoftwarepatents.org

---(end of broadcast)---
TIP 1: if posting/reading through Usenet, please send an appropriate
   subscribe-nomail command to [EMAIL PROTECTED] so that your
   message can get through to the mailing list cleanly


Re: [PERFORM] Large Database Design Help

2006-02-10 Thread PFC



was origionally designed for Postgres 7.0 on a PIII 500Mhz and some


Argh.


1) The database is very large, the largest table has 40 million tuples.


Is this simple types (like a few ints, text...) ?
How much space does it use on disk ? can it fit in RAM ?

2) The database needs to import 10's of thousands of tuples each night  
quickly.  The current method is VERY slow.


	You bet, COMMIT'ing after each insert or update is about the worst that  
can be done. It works fine on MySQL/MyISAM (which doesn't know about  
commit...) so I'd guess the system designer had a previous experience with  
MySQL.


My advice woule be :

- get a decent machine with some RAM (I guess you already knew this)...

Now, the update.

I would tend to do this :

- Generate a text file with your update data, using whatever tool you like  
best (perl, php, python, java...)

- CREATE TEMPORARY TABLE blah ...
- COPY blah FROM your update file.

	COPY is super fast. I think temporary tables don't write to the xlog, so  
they are also very fast. This should not take more than a few seconds for  
a few 10 K's of simple rows on modern hardware. It actually takes a  
fraction of a second on my PC for about 9K rows with 5 INTEGERs on them.


	You can also add constraints on your temporary table, to sanitize your  
data, in order to be reasonably sure that the following updates will work.


	The data you feed to copy should be correct, or it will rollback. This is  
your script's job to escape everything.


Now you got your data in the database. You have several options :

	- You are confident that the UPDATE will work without being rolled back  
by some constraint violation. Therefore, you issue a big joined UPDATE to  
update all the rows in your main table which are also in your temp table.  
Then you issue an INSERT INTO ... SELECT ... to insert the ones which were  
not already in the big table.


	Joined updates can be slow if your RAM is too small and it has to thrash  
the disk looking for every tuple around.
	You can cheat and CLUSTER your main table (say, once a week), so it is  
all in index order. Then you arrange your update data so it is in the same  
order (for instance, you SELECT INTO another temp table, with an ORDER BY  
corresponding to the CLUSTER on the main table). Having both in the same  
order will help reducing random disk accesses.


	- If you don't like this method, then you might want to use the same  
strategy as before (ie. a zillion queries), but write it in PSQL instead.  
PSQL is a lot faster, because everything is already parsed and planned  
beforehand. So you could do the following :


- for each row in the temporary update table :
- UPDATE the corresponding row in the main table
- IF FOUND, then cool, it was updated, nothing more to do.
 You don't need to SELECT in order to know if the row is there.
 UPDATE does it for you, without the race condition.
- IF NOT FOUND, then insert.
This has a race condition.
You know your application, so you'll know if it matters or not.

What do you think ?

3) I can't import new records with a COPY or drop my indexes b/c some of  
them are new records (INSERTS) and some are altered records (UPDATES)  
and the only way I can think of to identify these records is to perform  
a select for each record.


	Yes and no ; if you must do this, then use PSQL, it's a lot faster. And  
skip the SELECT.

Also, use the latest version. It really rocks.
	Like many said on the list, put pg_xlog on its own physical disk, with  
ext2fs.


3) Wrap each load into a transaction ( tens of thousands of records per  
load )


	That's the idea. The first strategy here (big update) uses one  
transaction anyway. For the other one, your choice. You can either do it  
all in 1 transaction, or in bunches of 1000 rows... but 1 row at a time  
would be horrendously slow.


Regards,

P.F.C

---(end of broadcast)---
TIP 9: In versions below 8.0, the planner will ignore your desire to
  choose an index scan if your joining column's datatypes do not
  match


Re: [PERFORM] Large Database Design Help

2006-02-10 Thread Ragnar
On Fri, 2006-02-10 at 11:24 +0100, Markus Schaber wrote:

 For lots non-read-only database workloads, RAID5 is a performance
 killer. Raid 1/0 might be better, or having two mirrors of two disks
 each, the first mirror holding system, swap, and the PostgreSQL WAL
 files, the second one holding the data.

I was under the impression that it is preferable to keep the WAL on 
its own spindles with no other activity there, to take full advantage
of the sequential nature of the WAL writes.

That would mean one mirror for the WAL, and one for the rest.
This, of course, may sometimes be too much wasted disk space, as the WAL
typically will not use a whole disk, so you might partition this mirror
into a small ext2 filesystem for WAL, and use the rest for files seldom 
accessed, such as backups. 

gnari



---(end of broadcast)---
TIP 9: In versions below 8.0, the planner will ignore your desire to
   choose an index scan if your joining column's datatypes do not
   match


Re: [PERFORM] Large Database Design Help

2006-02-10 Thread Scott Marlowe
On Fri, 2006-02-10 at 16:39, Ragnar wrote:
 On Fri, 2006-02-10 at 11:24 +0100, Markus Schaber wrote:
 
  For lots non-read-only database workloads, RAID5 is a performance
  killer. Raid 1/0 might be better, or having two mirrors of two disks
  each, the first mirror holding system, swap, and the PostgreSQL WAL
  files, the second one holding the data.
 
 I was under the impression that it is preferable to keep the WAL on 
 its own spindles with no other activity there, to take full advantage
 of the sequential nature of the WAL writes.
 
 That would mean one mirror for the WAL, and one for the rest.
 This, of course, may sometimes be too much wasted disk space, as the WAL
 typically will not use a whole disk, so you might partition this mirror
 into a small ext2 filesystem for WAL, and use the rest for files seldom 
 accessed, such as backups. 

Well, on most database servers, the actual access to the OS and swap
drives should drop to about zero over time, so this is a workable
solution if you've only got enough drives / drive slots for two mirrors.

---(end of broadcast)---
TIP 5: don't forget to increase your free space map settings


[PERFORM] Large Database Design Help

2006-02-09 Thread Orion Henry


Hello All,

I've inherited a postgresql database that I would like to refactor.   It 
was origionally designed for Postgres 7.0 on a PIII 500Mhz and some 
design decisions were made that don't make sense any more.  Here's the 
problem:


1) The database is very large, the largest table has 40 million tuples.

2) The database needs to import 10's of thousands of tuples each night 
quickly.  The current method is VERY slow.


3) I can't import new records with a COPY or drop my indexes b/c some of 
them are new records (INSERTS) and some are altered records (UPDATES) 
and the only way I can think of to identify these records is to perform 
a select for each record.


Here is how the database is currently laid out and you'll see why I have 
a problem with it


1) The data is easily partitionable by client ID.  In an attempt to keep 
the indexes small and the inserts fast one table was made per client 
ID.  Thus the primary table in the database (the one with 40 million 
tuples) is really 133 tables each ending with a three digit suffix.   
The largest of these client tables has 8 million of the 40 million 
tuples.   The system started with around a half dozen clients and is now 
a huge pain to manage with so many tables.  I was hoping new hardware 
and new postgres features would allow for this data to be merged safely 
into a single table.


2) The imports are not done inside of transactions.  I'm assuming the 
system designers excluded this for a reason.  Will I run into problems 
performing tens of thousands of inserts and updates inside a single 
transaction?


3) The current code that bulk loads data into the database is a loop 
that looks like this:


   $result = exe(INSERT INTO $table ($name_str) SELECT 
$val_str WHERE NOT EXISTS (SELECT 1 FROM $table WHERE $keys));

   if ($result == 0)
   {
   $result = exe(UPDATE $table SET $non_keys WHERE 
$keys);
   }  

Is there a faster way to bulk load data when it's not known ahead of 
time if it's a new record or an updated record?


What  I would LIKE to do but am afraid I will hit a serious performance 
wall (or am missing an obvious / better way to do it)


1) Merge all 133 client tables into a single new table, add a client_id 
column, do the data partitioning on the indexes not the tables as seen here:


 CREATE INDEX actioninfo_order_number_XXX_idx ON actioninfo ( 
order_number ) WHERE client_id = XXX;
 CREATE INDEX actioninfo_trans_date_XXX_idx ON actioninfo ( 
transaction_date ) WHERE client_id = XXX;


   (Aside question: if I were to find a way to use COPY and I were 
loading data on a single client_id, would dropping just the indexes for 
that client_id accelerate the load?)


2) Find some way to make the bulk loads faster or more efficent (help!)

3) Wrap each load into a transaction ( tens of thousands of records per 
load )


Is this a good plan?  Is there a better way?  Am I walking into a trap?  
Should I leave well enough alone and not try and fix something that's 
not broken?


FWIW here's the hardware and the profile of the current uber table:

 Column   |  Type   | Modifiers
---+-+---
order_number  | integer | not null
order_line_number | integer | not null
action_number | integer | not null
transaction_date  | date|
code  | text|
trans_group_code  | text|
quantity  | integer |
extension | money   |
sales_tax | money   |
shipping  | money   |
discount  | money   |

Dual Opteron 246, 4 disk SCSI RAID5, 4GB of RAM

# du -sh /var/lib/postgres/data/
16G /var/lib/postgres/data/  

( the current database is PG 7.4 - I intend to upgrade it to 8.1 if and 
when I do this refactoring )
( the current OS is Debian Unstable but I intend to be running RHEL 4.0 
if and when I do this refactoring )



---(end of broadcast)---
TIP 4: Have you searched our list archives?

  http://archives.postgresql.org