10 ms·
Optimizing Postgres table layout for maximum efficiency
- remus 2y agoVery interesting, but I think the author overstates the importance of alignment a little. Unless your data/indexes are already of a challenging size (or you expect them to be imminently) for your hardware then fiddling with byte alignment details feels like a fairly premature optimisation. Disk is cheap, memory is plentiful, your time is expensive etc.
- koolba 2y ago> Disk is cheap, memory is plentiful, your time is expensive etc. Taking the time to know the in memory sizing for your data types is well worth it. Taking the time to think about the types to use and sorting them by size is also minimal and well worth it. It may make sense for the system to do it automatically for newly created tables. But maybe not as it’s possible you’d want the data layout to match some existing structure.
- egnehots 2y agoAs mentioned in the article, it's a good idea to consider this when creating a new table, since it's essentially a free optimization. However, it's probably not worth the hassle of reordering a production table for that. > Disk is cheap, memory is plentiful, but your time is expensive, etc. One thing to keep in mind, though, is that while you often have plenty of disk space, RAM is still relatively expensive. It's also divided into many smaller buffers, such as working memory and shared buffers, which are not that large. These optimizations help to fit more data into cache. However, what the article said about alignment being important for indexes is somewhat misleading. Reordering an index field is not the same as reordering columns in a table. Beside having to rewrite queries, it also changes the access pattern and the time required to access the data, which is often much more significant than the space saved. Indexes are, by nature, a tradeoff where you give up space to gain time, so this mindset doesn't really apply there.
- napsterbr 2y agoHey, author here. > Indexes are, by nature, a tradeoff where you give up space to gain time, so this mindset doesn't really apply there. I agree that (re)aligning indexes are a different beast entirely, but (as mentioned in my recommendation) ideally the developer should keep this in mind when creating the index initially. Factors like cardinality and even readability should take precedence over perfect alignment, but all else being equal, aligning your indexes from the very moment they are introduced in the codebase is the ideal scenario IMO.
- sgarland 2y ago> Disk is cheap, memory is plentiful, your time is expensive etc. Spend 30 minutes one day playing around with Postgres, trying different column combinations out. Boom, you now know how best to order columns. This doesn’t seem like a big ask. The flip side is that changing data at scale is HARD, so if you put things like this off, when you do finally need to squeeze bytes, it’s painful. Also, memory is absolutely not plentiful. That’s generally the biggest bottleneck (or rather, the lack of it then makes IO the bottleneck) for an RDBMS, assuming you have connection pooling and aren’t saturating the CPU with overhead.
- epgui 2y ago> Disk is cheap, memory is plentiful, your time is expensive etc. Index size is not solely a storage concern. I also don't really care about how much disk space I pay for, but sometimes I care a lot about how long it takes to vacuum a table.
- koolba 2y agoThe content itself is fine but the unnecessary image above the label “ You have the right to remain aligned” with the usual AI-generated garbage text cheapens the entire article.
- loloquwowndueo 2y agoAre you kidding, that image is hilarious. (Articles that intersperse annoying memes every 3 paragraphs with obnoxious gif animations are much much worse).
- wccrawford 2y agoInteresting. But if this is important, why doesn't Postgres do it invisibly, automatically? Surely there's a most-correct sequence, with the longest values first, and the shortest values last, and then (probably) the variable-length values? It could then happily report back the data in the order that the DB Admin/developer expects.
- j45 2y agoUpdating column rows in MySQL is pretty trivial, and I've wondered why it's not the same in Postgres, even at the GUI level. Ordering columns for performance might be a different order of reccomendation.
- jeltz 2y agoBecause nobody has implemented it yet. A patch would be very welcome by many but nobody has written one.
- johnthuss 2y agoThis is a super useful thing to know and I'm thankful for this article highlighting this aspect of Postgres. But I would disagree with the takeaway to focus on optimizing your indexes and not your tables. The reason is that the order of columns in a multi-column index is highly meaningful and intentional in order to support match on a range of values for the last column in the index. The way databases work you can only utilize a multi-column index on (customer_id int4, date timestamp) if have an equality match on customer_id, like "WHERE customer_id = 1 AND BETWEEN '2024-01-01' and '2025-01-01'". If you reorder these columns in the index to put the larger date column first, then, sure, you save space in the index, but you also make it worthless – it will never be used by the query above. As such, optimizing a multi-column index is only useful when all the columns are queried for equality rather than a range. In contrast, when you are creating a NEW table you might not think hard about the order of the columns in the table, and especially not about the data-sizes of each column and their alignment. But doing so at the time you create the table can be tremendously beneficial if it is going to be very large. It is important to note that you not only save space on-disk, but in precious RAM when the tuples are loaded.
- sgarland 2y agoThis (index ordering resulting in useless indices) is not true, at least not in newer versions of Postgres (I just tried with 15). While the query will take much longer (for me, it was about 47 msec vs 0.6 msec for 2,000,000 rows), it still uses the index. Similarly, while normally you wouldn’t expect a query using predicate Y to use an index defined on columns (X, Y, Z) if predicate X is also not referenced, Postgres may choose to do so, depending on table stats, and the relative difference between random_page_cost and seq_page_cost. I’ve seen it happen before.
- ghusbands 2y ago> This (index ordering resulting in useless indices) is not true, at least not in newer versions of Postgres (I just tried with 15). > While the query will take much longer (for me, it was about 47 msec vs 0.6 msec for 2,000,000 rows), it still uses the index. I'd argue that something being 78x slower can make it pretty useless, but it is indeed at least used, in some cases.
- rtuin 2y agoIt’s really something you don’t know how this applies to your Postgres DB, until you run into a situation where you do. The author explains this very well, it’s a good read! I’ve learned about this padding little over a year ago, while I was designing a data intensive application with a colleague. I was skeptical about the advantage at first, but for our specific design, where we have 100 to 480+ columns in one table it makes a huge difference on table store size. Not so much on the indexes, though.
- sgarland 2y ago> where we have 100 to 480+ columns in one table I’m sorry, what? Why?
- epgui 2y agoI don't know what they're doing, but highly-denormalized tables are very common, and sometimes even the norm, depending on how things are set up, especially in OLAP contexts.
- sgarland 2y agoHighly denormalized tables are often the norm simply because the tables weren’t properly normalized to begin with, and the data model wasn’t properly done, such that reasonable joins are overly difficult. OLAP is of course its own problem, and most of the best practices for OLTP do not apply.
- rtuin 2y agoGlad you asked! This system contains measurements and state of physical devices (time series). It’s designed for both heavy write and read, with slight emphasis on write. Each table is one type of device and contains 1 to 5 different measurements/states. But here’s the trick: because data is queried with minimum bucket size of 15minutes I figured we could just create a column for each measurement + quarter of the day (i.e. measure0000, measure0015), so that’s 100 columns for each measurement (96 quarter + 4 for DST), include the date in the key, et voila: excellent write performance (because it’s mainly UPDATE queries) and good read performance. Okay, the queries to make sense of the data aren’t pretty, but can be generated. I find it really cool how effective this is for time-series data without Postgres extensions (we’re on RDS).
- OliverJones 2y agoGood material! It has to be said, the order of columns in correctly designed multicolumn BTREE indexes is governed by the shape of the queries the indexes support. So don't arbitarily reorder columns in your indexes to handle alignment.
- mococa 2y agoCould the Django ORM deal with this?
- SoftTalker 2y agoThis is the sort of thing that good DBAs used to know about and manage, but nowadays that isn't a fashionable job.
- sgarland 2y agoIt’s coming back in the form of DBRE (hi, it’s me), but unfortunately actual core RDBMS knowledge is often lacking. RDBMS administration is legitimately difficult, as they have a million knobs to turn, and a million gotchas that can tank performance if you aren’t careful. I should mention here that this difficulty really only occurs at scale. If your DB has a few million rows, as long as your queries are indexed it’s unlikely that you’ll experience problems. You probably won’t have optimal performance, but it’s likely to be good enough. Personally, I love it, but also find it frustrating. The amount of times I’ve presented “here’s your problem, here’s how to fix it, sorry it’s gonna suck” only to be told “nah, we’ll just upsize the instance” is far too many.
- didgetmaster 2y agoIt's been awhile since I dug into the bowels of PG; but the overall row size with respect to page size used to be important. If you had a table where every row took up 4097 bytes and the page size was 8K; the your disk footprint for that table was double. Only one row fit in a page and the other (nearly) half of each page was wasted. If that is still true, then alignment issues could push you over the edge if you are close to it.
- gnfargbl 2y agoThe ever-useful postgres_dba tool (https://github.com/NikolayS/postgres_dba https://github.com/NikolayS/postgres_dba) can help with this. Install it, start psql, run :dba and choose option p1 to see how much table space you would save by reordering your columns.
- samokhvalov 2y agoThanks for mentioning!
- tiffanyh 2y agoHow much of this article is rooted in hard drives with physically mechanical spinning disk latency assumptions, vs ssd/nvme?
- pornel 2y agoNone at all. The alignment is for cheap and easy access of data once it is in memory. It's probably rooted in PostgreSQL being written in C where aligned access is trivial, and dereference of an unaligned pointer is Undefined Behavior and requires more clunky code instead.
- nzach 2y agoAs the sibling said: "not at all". But I see why this could be confusing. In the article he wrote: > Postgres will happily add padding to the underlying data in order to make sure it is properly aligned at the physical layer. Having the data aligned ensures faster access time when retrieving pages from disk. And this is correct. The problem is that "physical layer" refers to the physical memory layout and how things are loaded into the CPU. And not how they are stored in the disk(mostly). I'm not expert in this subject, but as far I understand the main factor for this kind of behavior is the way a CPU reads data from its cache, i.e. 1 line(64bytes) at a time. And this is why we always pad to factors of 64(2, 4, 8, 16, 32). This is the first time I read about this in the context of PG, but I've already encoutered the same issue in C and Go. So for me this is just a new manifestation of the same underlying problem. https://mecha-mind.medium.com/demystifying-cpu-caches-with-examples-810534628d71 https://mecha-mind.medium.com/demystifying-cpu-caches-with-e...
- branko_d 2y agoFrom the article: Having the data aligned ensures faster access time when retrieving pages from disk. Byte-level alignment cannot possibly have anything to do with retrieving pages from disk, simply because the unit of retrieval is the whole page. From the hardware/OS perspective, a page is just an opaque blob of bytes (comprised from one or more blocks on the physical drive). Only after these bytes have reached RAM does the byte-level alignment play a role, because CPU works slower on misaligned data. The article itself then goes on to illustrates the above (and seemingly contradict itself): SQLite does not pad or align columns within a row. Everything is tightly packed together using minimal space. Two consequences of this design: SQLite has to work harder (use more CPU cycles) to access data within a row once it has that row in memory. SQLite uses fewer bytes on disk, less memory, and spends less time moving content around because there are fewer bytes to move.
- winternewt 2y agoI think the idea is that padding results in lower storage efficiency, which means fewer rows per page and hence lower I/O throughput. By changing the column order you can reduce the amount of padding required.
- branko_d 2y agoSure, having less padding increases I/O efficiency. I was just commenting on the author's apparent confusion as to why the padding is there in the first place. Here is the full(er) quote: Postgres will happily add padding to the underlying data in order to make sure it is properly aligned at the physical layer. Having the data aligned ensures faster access time when retrieving pages from disk. This might be misunderstood as "Postgres adds padding to speed-up disk I/O", which is the opposite of what actually happens. Padding slows-down I/O but speeds-up the CPU processing afterwards. SQLite made the opposite tradeoff.
- napsterbr 2y agoYou are absolutely correct, the current wording causes confusion as to where the speed-up happens. Over the weekend I'll add a note and link to this thread, thanks for pointing that out.
- silvestrov 2y agoWild guess: this is a left-over from the old days of Sun SPARC processors which could only access doubles on 8-byte aligned memory addresses. For Intel processors you just paid a performance penality for unaligned accesses but SPARC processors would generate a TRAP for unaligned accesses. I'd really like to see some performance testing of alignment for modern Intel/ARM CPUs. My guess is that tightly packing would be better as more data would fit into caches and you would have less i/o. Modern CPUs are very different from back in the 90's when PostgreSQL was designed. Posgresql has traditionally been very slow to stop supporting old OSes that are no longer used.
- SchwKatze 2y agoSince in most applications the order doesn't matter, why postegres don't make itself the recording to archive better data alignment instead the padding approach?