I've been having performance issues with Postgres (sequential scans vs index scans in an update statement). I've read that optimizer will change it's plan based on the resources it thinks are available. In addition, I've read alot of conflicting info on various parameters, so I'd like to sort those out as well.
 
Here's the query I've been having problems with:
 
UPDATE user_account SET last_name='abc'
FROM commercial_entity ce, commercial_service cs
WHERE user_account.user_account_id = ce.user_account_id AND
ce.commercial_entity_id=cs.commercial_entity_id;
 
or
 
UPDATE user_account SET last_name = 'abc'
 WHERE EXISTS (SELECT 1 FROM commercial_entity ce, commercial_service cs
 WHERE user_account.user_account_id = ce.user_account_id AND
 ce.commercial_entity_id = cs.commercial_entity_id);
 
Both are about the same.
All columns are indexed; all column-types are the same (numeric(10,0)). A vacuum analyze was run just before the last attempt at running the above statement.
 
 
MACHINE STATS
---------------------------
The machine is a dual-Pentium 3 933mhz, 2 gig of RAM, RAID 5 (3xWestern Digital 80 gig drives with 8-meg buffers on 3Ware), Red Hat 9.0
 
 
POSTGRES TUNING INFO
---------------------------------------
 
Here are part of the contents of my sysctl.conf file (note that I've played with values as low as 600000 with no difference)
kernel.shmmax=1400000000
kernel.shmall=1400000000
Here's the uncommented-lines from the postgresql.conf file (not the default one in the /usr/local/pgsql directory - I've initialzed the database on a different mount point with more space):
 
tcpip_socket = true
max_connections = 500
shared_buffers = 96000          # min max_connections*2 or 16, 8KB each
wal_buffers = 64                # min 4, typically 8KB each
sort_mem = 2048                 # min 64, size in KB
effective_cache_size = 6000     # typically 8KB each
LC_MESSAGES = 'en_US.UTF-8'
LC_MONETARY = 'en_US.UTF-8'
LC_NUMERIC = 'en_US.UTF-8'
LC_TIME = 'en_US.UTF-8'
Note that I've played with all these values; shared_buffers has been as low as 5000, and effective_cache_size has been as high as 50000. Sort mem has varied between 1024 bytes and 4096 bytes. wal_buffers have been between 16 and 128.
 
 
INFO FROM THE MACHINE
-----------------------------------------
Here are the vmstat numbers while running the query.
 
   procs                      memory      swap          io     system      cpu
 r  b  w   swpd   free   buff  cache   si   so    bi    bo   in    cs us sy id
 0  1  2 261940  11624 110072 1334896   12    0    12   748  177   101  2  4 95
 0  1  1 261940  11628 110124 1334836    0    0     0  1103  170    59  2  1 97
 0  3  1 261928  11616 110180 1334808    3    0     6  1156  169    67  2  2 96
 0  2  1 261892  11628 110212 1334636    7    0     7  1035  186   100  2  2 96
 0  1  1 261796  11616 110272 1334688   18    0    18   932  169    79  2  1 97
 0  1  1 261780  11560 110356 1334964    3    0     3  4155  192   118  2  7 92
 0  1  1 261772  11620 110400 1334956    2    0     2   939  162    63  3  0 97
 0  1  3 261744  11636 110440 1334872    6    0     9  1871  171   104  3  2 95
 0  0  0 261744  13488 110472 1332244    0    0     0   922  195  1271  3  2 94
 0  0  0 261744  13436 110492 1332244    0    0     0    24  115    47  0  1 99
 0  0  0 261744  13436 110492 1332244    0    0     0     6  109    36  0  5 95
 0  0  0 261744  13436 110492 1332244    0    0     0     6  123    63  0  0 100
 0  0  0 261744  13436 110492 1332244    0    0     0     6  109    38  0  0 100
 0  0  0 261744  13436 110492 1332244    0    0     0     6  112    39  0  1 99
I'm not overly familiar with Linux, but the swap in-out seems low, as does the io in-out. Have I allocated too much memory? Regardless, it doesn't explain why the optimizer would decide to do a sequential scan.
 
Here's the explain-analyze:
 
 Merge Join  (cost=11819.21..15258.55 rows=12007 width=752) (actual time=4107.64..5587.81 rows=20880 loops=1)
   Merge Cond: ("outer".commercial_entity_id = "inner".commercial_entity_id)
   ->  Index Scan using comm_serv_comm_ent_id_i on commercial_service cs (cost=0.00..3015.53 rows=88038 width=12) (actual time=0.05..487.23 rows=88038 loops=1)
   ->  Sort  (cost=11819.21..11846.08 rows=10752 width=740) (actual time=3509.07..3955.15 rows=25098 loops=1)
         Sort Key: ce.commercial_entity_id
         ->  Merge Join  (cost=0.00..9065.23 rows=10752 width=740) (actual time=0.18..2762.13 rows=7990 loops=1)
               Merge Cond: ("outer".user_account_id = "inner".user_account_id)
               ->  Index Scan using user_account_pkey on user_account (cost=0.00..8010.39 rows=72483 width=716) (actual time=0.05..2220.86 rows=72483 loops=1)
               ->  Index Scan using comm_ent_usr_acc_id_i on commercial_entity ce  (cost=0.00..4787.69 rows=78834 width=24) (actual time=0.02..55.64 rows=7991 loops=1)
 Total runtime: 226239.77 msec
(10 rows)
 
------------------------------------------------------------
 
Tied up in all this is my inability to grasp what shared_buffers do
 
 
"Shared buffers defines a block of memory that PostgreSQL will use to hold requests that are awaiting attention from the kernel buffer and CPU." and "The shared buffers parameter assumes that OS is going to cache a lot of files and hence it is generally very low compared with system RAM."
 
 
"Increase the buffer size. Postgres uses a shared memory segment among its subthreads to buffer data in memory. The default is 512k, which is inadequate. On many of our installs, we've bumped it to ~16M, which is still small. If you can spare enough memory to fit your whole database in memory, do so."
 
Our database (in Oracle) is just over 4 gig in size; obviously, this won't comfortably fit in memory (though we do have an Opteron machine inbound for next week with 4-gig of RAM and SCSI hard-drives). The more of it we can fit in memory the better.
 
What about changing these costs - the doc at http://www.varlena.com/varlena/GeneralBits/Tidbits/annotated_conf_e.html doesn't go into a lot of detail. I was thinking that maybe the optimizer decided it was faster to do a sequential scan rather than an index scan based on an analysis of the cost using these values.
 
#random_page_cost = 4           # units are one sequential page fetch cost
#cpu_tuple_cost = 0.01          # (same)
#cpu_index_tuple_cost = 0.001   # (same)
#cpu_operator_cost = 0.0025     # (same)
 
David

Reply via email to