Understanding Postgres Performance
craigkerstiens.com
craigkerstiens.com
The trade-off with indexes is that they make reads faster and writes slower. But if you're running a high performance application, it is not that hard to have read-only slaves to offload read queries to. (I won't say that it is trivial, because there are issues, but it is doable. I've done it.) However fixing poor write performance is much more complicated.
Therefore create all of the indexes that you need. But no more. And any indexes that are not carrying their weight should be dropped. Furthermore if you have intensive queries that you have to do (eg for reports), consider offloading those to another database.
Of course if you don't have significant data/volume/etc, then don't worry about this. Any semi-sane approach will work for you. (That applies to most of you.)
because if that's correct then it makes sense to err on the side of too many indices.
of course, it is better to be perfect. but if you have an error, which side would you prefer to be on?
and you don't need "significant" volumes to hurt with a full table scan (ie it will hurt most of you).
[what surprised me was the 99% cache. that is very dependent on your application, but i guess is common for web apps?]
So if you're expecting a table to have a heavy UPDATE load it may be a good idea to lower its fillfactor from the default of 100 (for example, ALTER TABLE foo SET (fillfactor = 95);). This means that once a page is 95% full no new rows will be inserted into it, and the remaining 5% will be available for updated rows. HOT will also do "mini-vacuums" on individual pages as needed to free up space.
Of course, it's up to you to decide whether this tradeoff is worth increasing the effective size of your table by ~5%. But even tables with fillfactors at the default of 100 will get some benefit from HOT.
More info: https://github.com/postgres/postgres/blob/master/src/backend...
Edit to add: One caveat is that HOT won't work if you UPDATE a column that is indexed - then, of course, Postgres will have to touch the index.
As noted, 3 levels suffices for a moderate sized table, and it would be rare to need more than 5 levels. The "levels" in question are pages of memory that have to be looked at. The reason that this is less than you expect is that inside of each page you essentially have a tree that has several levels to traverse. However the time to traverse that tree is less than the time to fetch the next page, so the operations of concern are fetches of pages.
The most important number noted in that link is that under normal operation once caches are warmed up, it should a maximum of one disk seek to traverse the index. That single disk seek, if it happens, takes longer than all other operations.
The long and short of it is, "in theory log(n), in practice a reasonably small constant."
If the tables are only queried once in a blue moon but written to very often (e.g. events/history/logs) then indexes can be wasteful.
Also if you're creating indexes, the most powerful underutilized ability is to create concatenated indexes. Index 2 or 3 fields in one index. Those indexes can be used if you are querying the first field only, or the first 2, or the first 3. (Some databases will use them if you are querying the second, but that is much, much less efficient to do.) If you're looking things up by 2 fields sometimes, a concatenated index can really save time, and they are nearly as good as an index on a single field for queries that only need one field.
I for instance don't know what happens when I copy or dump and restore a postgres table that relies on a sequence. Mysql auto-magically takes care of it by saving the next auto increment id in table dump or copy.
It worked! Just downloaded the ZIP, unpacked and started: sbt ~container:start -J"-javaagent:/path/myapp/newrelic/newrelic.jar"
Now I have a nice graph which matches up with the numbers I see in my logs. As I suspected there's some work that needs to be done on the DB access side.
SELECT
relname,
CASE
WHEN seq_scan = 0 THEN 0
ELSE 100 * idx_scan / (seq_scan + idx_scan)
END AS percent_of_times_index_used,
n_live_tup rows_in_table
FROM
pg_stat_user_tables
ORDER BY
n_live_tup DESC; SELECT
sum(heap_blks_read) as heap_read,
sum(heap_blks_hit) as heap_hit,
sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio
FROM
pg_statio_user_tables;edit: This is what I get for leaving pages open and coming back to them later.