Live data from Hacker News

Optimistic Locking in B-Trees

cedardb.com

51–55 of 55 posts

Re: Optimistic Locking in B-Trees

#51

Earlier quoted context omitted.

But tables in PG are heaps, so appending to them is even faster than in b-trees.

This is specifically taking about indexed structures though, so inserting data in a way that means it can be efficiently searched afterwards. Comparing heaps to b-trees, hash tables, and other ones structures in this context isn't really a Granny Smith Vs Golden Delicious style comparison. Many (though not all) databases allow the base data to be a heap for those situations where a pure heap or a heap with small supp…

A SQL table that has a `PRIMARY KEY` is (or can be) an index. Indeed, in SQLite3 if you use the `WITHOUT ROWID` feature you get exactly that: a covering b-tree index where the `PRIMARY KEY` columns are a prefix of the index key.

Also in SQLite3 tables w/o `WITHOUT ROWID` are still b-tree indices, but they are indexed by the row ID, or the `INTEGER PRIMARY KEY` column (which if there is one, is the same thing as a row ID).

I.e., tables can be indices, and therefore covering indices can be tables.

If you have a table that has no `PRIMARY KEY` then a heap makes some sense, but then again so does a b-tree indexing by row ID. But then again a table without a `PRIMARY KEY` actually makes little sense in a world of relational algebra where tables are sets (as they are supposed to be in SQL) because a table without a `PRIMARY KEY` implies allowing duplicate rows, and then you need a row ID to deal with those... A table without a `PRIMARY KEY` is always a table that has an implied `PRIMARY KEY` that is just a row ID.

Re: Optimistic Locking in B-Trees

#52

Earlier quoted context omitted.

This is specifically taking about indexed structures though, so inserting data in a way that means it can be efficiently searched afterwards. Comparing heaps to b-trees, hash tables, and other ones structures in this context isn't really a Granny Smith Vs Golden Delicious style comparison. Many (though not all) databases allow the base data to be a heap for those situations where a pure heap or a heap with small supp…

It was unclear of me to say "row IDs." I meant primary keys, not something like ctid https://www.postgresql.org/docs/current/ddl-system-columns.h...

If you declare a table that has no `PRIMARY KEY` (and no `UNIQUE` constraints on `NOT NULL` columns) you're essentially allowing duplicate rows. Either the RDBMS treats such a table as having something of a primary key on all columns (but NULLs sneak duplicate rows back in!), or the RDBMS gives you a row ID like concept (ctid counts) to truly disambiguate duplicate rows.

Re: Optimistic Locking in B-Trees

#53
I think there are a couple of bugs in the pseudo-code:

traverse_node(page, version, key): retry: # Read the page pageCopy = copy(page)

    # BUG1: version is an argument so it likely won't be correct on retry
    if version != atomic_load(page.version):
        goto retry;
    
    # Go to the next page
    childPtr = binary_search(pageCopy, key)
    nextPage = load(childPtr)
    nextVersion = atomic_load(nextPage.version)

    # BUG2: seems like we should be using nextVersion instead of version
    # Validate that no writer overtook the reader
    if version != atomic_load(page.version):
        goto retry;
    
    # Safely traverse to the next node
    page = nextPage
Or am I misunderstanding something here?

Re: Optimistic Locking in B-Trees

#54

Earlier quoted context omitted.

This is specifically taking about indexed structures though, so inserting data in a way that means it can be efficiently searched afterwards. Comparing heaps to b-trees, hash tables, and other ones structures in this context isn't really a Granny Smith Vs Golden Delicious style comparison. Many (though not all) databases allow the base data to be a heap for those situations where a pure heap or a heap with small supp…

A SQL table that has a `PRIMARY KEY` is (or can be) an index. Indeed, in SQLite3 if you use the `WITHOUT ROWID` feature you get exactly that: a covering b-tree index where the `PRIMARY KEY` columns are a prefix of the index key. Also in SQLite3 tables w/o `WITHOUT ROWID` are still b-tree indices, but they are indexed by the row ID, or the `INTEGER PRIMARY KEY` column (which if there is one, is the same thing as a row…

> A table without a `PRIMARY KEY` is always a table that has an implied `PRIMARY KEY` that is just a row ID.

While it is true that there will be a surrogate key (the row ID), that doesn't imply there is any sort of index as there is in SQLite.

In SQL Server a heap table is not represented by a tree indexed by rowID, there will be one or more IAM (Index Allocation Map) pages which just list the pages used by that file. These are not even ordered lists. The internal RowID is actually a 8-byte encoding of the data's location in the files (two bytes for file, four bytes for page, two bytes for slot) for instance “1:560:0” which is file 1, page 560, first slot. This is what is stored in the IAM page and what any indexes point to for finding data that is not part of the index (i.e. not part of the index key and also not “included”). That isn't even necessarily where the data is, just where it was first put: if the row is updated in a way that it grows beyond what will fit in the page it shares with other rows, it goes elsewhere and a forwarding record in the original slot will point to the new location (this can become a long list of jumps to find the actual data, if a row grows many times in its existence, and you are unlucky wrt what free space is available on the current page each time it does).

Yes, this is more than a bit nasty for most uses. That is why most (almost all) tables in SQL Server should have a clustered index (at which point it is no longer a heap). Heaps have their uses where they are better than tables with clustered indexes, but those are few and far between (off the top of my head, some intermediate tables for ETL processes is all I can think of, technically very small (one-or-few page) tables too, but any measured efficiency difference there is so small either way that it is, or may as well be, statistical noise).

Having a primary key does not stop a heap being a head (in SQL Server) as the primary key does not have to be a clustering key. It usually is, but there are times when another key is a better choice and there can only be one clustered key (ignoring the wide index hack that makes things more-or-less behave like there are multiple clustering keys, at the expense of extra storage space and much slower insert/update operations).

Re: Optimistic Locking in B-Trees

#55

Earlier quoted context omitted.

A SQL table that has a `PRIMARY KEY` is (or can be) an index. Indeed, in SQLite3 if you use the `WITHOUT ROWID` feature you get exactly that: a covering b-tree index where the `PRIMARY KEY` columns are a prefix of the index key. Also in SQLite3 tables w/o `WITHOUT ROWID` are still b-tree indices, but they are indexed by the row ID, or the `INTEGER PRIMARY KEY` column (which if there is one, is the same thing as a row…

> A table without a `PRIMARY KEY` is always a table that has an implied `PRIMARY KEY` that is just a row ID. While it is true that there will be a surrogate key (the row ID), that doesn't imply there is any sort of index as there is in SQLite. In SQL Server a heap table is not represented by a tree indexed by rowID, there will be one or more IAM (Index Allocation Map) pages which just list the pages used by that file…

> While it is true that there will be a surrogate key (the row ID), that doesn't imply there is any sort of index as there is in SQLite.

In PG heaps it does, though it's not stable across `VACUUM`. From what you say the same is true of SQL Server. The point being that two duplicate rows in a table w/o a `PRIMARY KEY` will have different "row IDs" or "ctids" or whatever even though that row ID or ctid will not be stable across vacuuming though it should be stable across a single query.

(In PG all tables are heaps, always. You can have indices, and even covering indices, and your read queries might never hit the heap, but a heap will be there. IMO PG ought to support a b-tree instead of a heap for tables with `PRIMARY KEY`s -- it would be an improvement, and a performance optimization.)

Post reply on HN