Live data from Hacker News

B+ Trees and why I love them, part 1

ayende.com

1–10 of 33 posts

Re: B+ Trees and why I love them, part 1

#6

It should be noted that textbook B+ trees, like those described, are nowhere near state of the art performance wise. It's possible to do orders of magnitude better.

The B-tree implementations used in a lot of databases have tweaks, but they are surprisingly similar to the textbook descriptions. In general, B-trees are actually a very useful data structure when: a) Your data is much larger than main memory (e.g. terabytes/petabytes of data and gigabytes of RAM). b) You want to support random insert/update/delete, seek, and next/previous key. c) There has to be a reasonable constraint on storage space (i.e. data density/compaction). In those cases the log(n) performance of a b-tree is extremely hard to improve on.

Alternate data structures a useful if: 1. The ratio of ram/disk is more favourable, in which case using an improved in-memory data structure can be more efficient. 2. Not all operations have to be supported. In particular eliminating the next/previous key operations allows something like a hashtable to be used.

Re: B+ Trees and why I love them, part 1

#7
post #6

It should be noted that textbook B+ trees, like those described, are nowhere near state of the art performance wise. It's possible to do orders of magnitude better.

The B-tree implementations used in a lot of databases have tweaks, but they are surprisingly similar to the textbook descriptions. In general, B-trees are actually a very useful data structure when: a) Your data is much larger than main memory (e.g. terabytes/petabytes of data and gigabytes of RAM). b) You want to support random insert/update/delete, seek, and next/previous key. c) There has to be a reasonable constr…

There's two reasons a textbook b+ tree is never going to perform very well:

* Binary searches suck

The memory accesses of a binary search look pretty much like complete random access, so prefetching is useless. Once your b+ tree is big enough that most of it won't fit in L2, you've got a tight inner loop that's waiting on DRAM at every single iteration.

This one is at least more or less solvable without changing the rest of the design, but then if you also want to solve the other main issue it gets harder.

* With small btree nodes, the overhead of looking up and locking btree nodes (even assuming they're all cached in memory) kills you.

Trouble is if you make your btree nodes big enough to fix that overhead, they're now so big that rewriting them when you insert a single key is just ridiculous.

So where you end up is to get anywhere near the highest possible performance you're forced into log structured btree nodes. Which actually has its advantages for solving the first problem, but now what you've got is really a hybrid b+ tree/compacting data structure.

Source: bcache author.

Re: B+ Trees and why I love them, part 1

#8
I too "discovered" B-Trees (and B+Trees) recently and wrote about it: http://grisha.org/blog/2013/05/11/relational-database-on-top...

I even implemented a key/value backed SQL database (using Thredis, which is Redis and SQLite) http://grisha.org/blog/2013/05/29/sqlite-db-stored-in-a-redi...

SQLite3 has an awesome C implementation with copious comments if you really want to understand them, BTW.

The most fascinating thing about B-Trees is that the entire data structure is stored in equally sized blocks (pages), and getting a page by number is a very easy operation in block storage, which is what our RAM and hard drives are (even though back then they were tapes and punch cards).

Few people realize that it's not just DB's that use B-Trees, our filesystems are also largely B-Tree based.

It's remarkable how few today's "developers" understand how they work or even know what they are, yet they are so fundamental to everything computers do.

Without B-Trees our present world of computing wouldn't exist as we know it. It could be the coolest data structure ever :)

Re: B+ Trees and why I love them, part 1

#9
post #6

Earlier quoted context omitted.

The B-tree implementations used in a lot of databases have tweaks, but they are surprisingly similar to the textbook descriptions. In general, B-trees are actually a very useful data structure when: a) Your data is much larger than main memory (e.g. terabytes/petabytes of data and gigabytes of RAM). b) You want to support random insert/update/delete, seek, and next/previous key. c) There has to be a reasonable constr…

There's two reasons a textbook b+ tree is never going to perform very well: * Binary searches suck The memory accesses of a binary search look pretty much like complete random access, so prefetching is useless. Once your b+ tree is big enough that most of it won't fit in L2, you've got a tight inner loop that's waiting on DRAM at every single iteration. This one is at least more or less solvable without changing the…

I'm not talking about a b-tree that won't fit in L2, I'm talking about a B-tree that won't even fit in main memory. In those cases, even with SSDs, the cost of pulling a page off disk dominates the in-memory cost of the binary search so the number of I/Os needed to find something becomes the dominant factor in performance. That leads to a focus on data density and cache replacement algorithms.

"Binary searches suck"

That is a valid point. Cache misses when doing the search inside of the b-tree can be painful. I know that there is some research into structuring the b-tree nodes to be more cache-friendly (David Lomet mentions this in his "Evolution of Effective B-Tree" paper: http://research.microsoft.com/pubs/77583/p64-lomet.pdf) but haven't actually seen any in production.

Part of the problem here is that page density is so critical that people are often wary of having a less-efficient, but more cache-friendly data representaton. It is normally better to have 10% more records in main memory than being able to search the nodes a touch faster. Various forms of key prefix compression are an example of this tradeoff.

Another consideration is the actual cost of doing the key comparison. I'm most familiar with relational database systems and they all support multi-column keys (e.g string+integer+boolean+date), which normally lead to an expensive, branch-laden comparison function because the comparison has to be type-aware and support various collation options. The cost of doing that branching means the CPU cache misses are a smaller overall part of the overall search cost. One exception to that is ESENT, which uses memcmp-able normalized keys, but doing that creates its own set of problems (Unicode is a pain, lack of denormalization means storing data twice, etc.).

"Rewriting them when you insert a single key is just ridiculous."

I don't think anyone does this. Most implementations have settled on a system that stores the keys in the node in an ad-hoc fashion and have an array of 'pointers' to the keys which starts at the end of the page and grows towards the front (except for Postgres, which appears to do it the other way around). Inserting a new key means moving the pointers, but not the actual keys. Some systems try to avoid doing even that -- for example the InnoDB engine in MySQL links together the keys and the page directory only points to every 4th-8th key. I believe that this is an attempt to balance the speed of the binary search against the cost of update/delete.

In addition, with write-ahead-logging the node isn't flushed when updated, instead the log records describing the update are flushed and the page is lazily flushed in the background by a checkpointing process, hopefully after several other updates have been made to the same node.

Microsoft SQL Server, Microsoft ESENT, MySQL's InnoDB and Postgres' b-tree index all use minor variations on the basic textbook b-tree for their data storage.

Source: Many years working on the ESENT database engine, a brief stint in Microsoft SQL Server, some hacking done on the MySQL InnoDB engine.

Re: B+ Trees and why I love them, part 1

#10

I too "discovered" B-Trees (and B+Trees) recently and wrote about it: http://grisha.org/blog/2013/05/11/relational-database-on-top... I even implemented a key/value backed SQL database (using Thredis, which is Redis and SQLite) http://grisha.org/blog/2013/05/29/sqlite-db-stored-in-a-redi... SQLite3 has an awesome C implementation with copious comments if you really want to understand them, BTW. The most fascinating t…

I've read your first link and I don't get why you talk about "A relational database needs to store rows in order". AFAIK you have to add an "order by" clause to ensure ordering of any kind.

Relational algebra is probably not dependent of ordering for the common operations. Of course, the actual implementation of a relational database might need ordering of keys to be reasonably efficient. Is that what you meant?

Post reply on HN