Live data from Hacker News

Choosing a Postgres primary key

supabase.com

111–120 of 163 posts

Re: Choosing a Postgres primary key

#111
post #6

My opinion. Always if in any way possible pick a semantic key. There is usually something defining the thing you are working on. If there isnt work on your normalisation. Main benefits to this: Avoids accidental duplication (happens so much). Avoids additional round trips to fetch the id to make a mutation. Of course if you work on something where you don’t know what it is yet (actually humans are a good example for…

I have to disagree with you there, for all the reasons mentioned above, but also it makes generating queries and tooling so much harder.

If you see a user_id column, you know it's going to link to user.id. if you see email_id, you know it's going to link to email.id, etc, etc. There's a lot of value in having a predictable schema.

Re: Choosing a Postgres primary key

#113

The author misses one advantage of UUIDs: if you’re working in high-throughput distributed systems, serial IDs create a bottleneck and single point of failure in the service handing out IDs. With UUIDs any service can generate an ID itself and tell downstream services about it in parallel—even if one of them is down, slow, or needs retrying.

Another great advantage of UUIDs is that it can help prevent you shooting yourself in the foot when you accidentally join the wrong tables. E.g. `DELETE FROM users WHERE id IN (SELECT id FROM user_orders);`

Re: Choosing a Postgres primary key

#114

Earlier quoted context omitted.

Why would you use an integer primary key and a public facing UUID? That seems like it's the worst of both worlds: ugly externally visible identifiers, record bloat, a database that you can't easily merge in the event of backups or DR, and having to roundtrip to the DB before you know the ID of a record. I personally stick to UUIDs in pretty much all cases, with the exception of where there are justified and benchmark…

> That seems like it's the worst of both worlds: ugly externally visible identifiers Sometimes the external identifier is needed due to interaction with external systems, so it isn't really your choice as the DB/app designer. >, record bloat, Depending on the DB, the opposite can be true. In SQL Server if the integer key is the clustering key, which is usually the case for a table's primary key then you may get a sma…

What I mean is that in most cases, BOTH autoinc int and UUID is wasteful.

For example if you have a situation where you really need the high performance of an integer ID like in your SQL Server example, why introduce a UUID into the equation at all? If you are in the unusual situation of needing such extreme performance that you're worrying about 4 vs 8 bytes on the PK, but also need to obfuscate the public facing ID, _potentially_ int+UUID makes sense. But in my experience that is that a pretty rare situation and there are other things you can do such as using a shorter randomly generated ID such as snowflake, random integers with collision detection (depending on write patterns) or encrypting your ID at the application layer (caveats emptor but unless you're relying on IDs for absolute security they shouldn't be a problem).

However, defaulting to autoinc int as FK and publicly visible UUID for "user friendly" ID seems like an odd thing to do. It seems like one of the least useful ID schemes.

> For internal use you should just use the integer ID for the most part, the UUID or similar being for external references.

I disagree. UUIDs are very useful even as internal identifiers in any area where performance isn't your top concern.

UUIDs are better than autoincs in almost every way except being slightly less performant. You could argue for a strictly internal ID that the security and uniqueness advantages don't matter very much, but I think it's better to default to the safest option in case your ID inadvertently becomes public at some point, or indeed in case you want to one day make public a previously internal-only record. But even if you know, for sure, that your ID will only ever be internal, they still make it possible to merge datasets easily, they're easy to correlate across multiple systems, are easier to find in log files, and can be generated at the application layer, which can make a big difference in transaction time in some cases.

The only reasons not to use UUIDs are that they are ugly and marginally slower, which for the vast majority of entities doesn't particularly matter. If you're at the point where the rest of your code is so optimal that the UUID is causing you problems, or your records are so tiny that the UUID is a big overhead, then that is an exception that I am very happy to make, but it rarely applies. Too often people assume that the overhead of a UUID is worse than it is because of how long they look, but then when you actually benchmark it, it's swings and roundabouts.

Besides, in most cases where I've defaulted to integers, I've come to regret that decision a few years down the line, where some complex system migration or new business requirement would have ended up much easier if we'd just bit the UUID bullet earlier on.

Re: Choosing a Postgres primary key

#115
post #101

Earlier quoted context omitted.

Are sequential id’s a security risk? In one of our systems we’ve seen customer guess at other accounts by just incrementing the sequence. The rule of thumb I used to use is if an Id is going to be used for lookups or being exposed externally use uuid otherwise us sequential. The hard thing about the above rule is that it’s hard to tell when you are designing the db if the id will be used externally/for lookups or not…

"Protecting" records by making IDs hard-to-guess just seems like putting the responsibility in the wrong place. If you're so worried about people getting their hands on the wrong records, I'd be more worried about your lack of trust in the application that queries that database in the first place: remember, even if you do make the IDs hard to guess, your "untrusted application" might at some point decide to simply le…

> "Protecting" records by making IDs hard-to-guess just seems like putting the responsibility in the wrong place.

It's a pretty powerful implementation of capability based security.

Re: Choosing a Postgres primary key

#116
post #9

While this is a good overview of the options for primary key generation, there's no silver bullet here. Most projects that are using SQL should just use the gold standard: an auto-incrementing integer for an internal primary key. And then decouple the public-facing primary key from it into a separate column, whether it be ULID, UUID, or a random-project-slug-123. Also, during debugging, it's a lot nicer to look at sh…

When I first started using Postgres, I sat and thought forever about which PKs to use, and looking back, I was way overthinking it. Combination of unique fields, UUIDs, hashed data... Now I always use bigserial without thinking about it. When my DBA hat is on, it's none of my concern how the user-facing IDs will look; I just know it's gonna be a string of some kind.

What's the common use case for the others? I can imagine for weird performance reasons you might want to pick special PKs, but that implies you're exposing them to clients, which you almost never want. The only more reasonable thing I can think of is a UUIDv4 for a special sharded database.

Re: Choosing a Postgres primary key

#117
post #106

Earlier quoted context omitted.

Are sequential id’s a security risk? In one of our systems we’ve seen customer guess at other accounts by just incrementing the sequence. The rule of thumb I used to use is if an Id is going to be used for lookups or being exposed externally use uuid otherwise us sequential. The hard thing about the above rule is that it’s hard to tell when you are designing the db if the id will be used externally/for lookups or not…

UUID does not protect your records, that is, it is not a security measure against what you describe.

It is if you don't expose a 'list' api, only a 'get'.

Re: Choosing a Postgres primary key

#118

Earlier quoted context omitted.

Why would you use an integer primary key and a public facing UUID? That seems like it's the worst of both worlds: ugly externally visible identifiers, record bloat, a database that you can't easily merge in the event of backups or DR, and having to roundtrip to the DB before you know the ID of a record. I personally stick to UUIDs in pretty much all cases, with the exception of where there are justified and benchmark…

IMO use a UUID + a "type code" so something like: xxxx-xxxx-xxxxxxxx-xxxx-CUST xxxx-xxxx-xxxxxxxx-xxxx-ADDR It makes seas of UUIDs much easier to reason about Depends on your tolerance of wasted disk space for binary vs char, but you can shorten the binary to base64 or use a record as a primary key if you want. Other advantages of UUIDs: - they can be generated by clients or by the server safely - they can be concurr…

For interacting with humans, I rarely expose a UUID directly. I'm not particularly bothered about ugly URLs but I don't work in an industry where SEO is relevant.

As you suggest, I sometimes incorporate type information into the ID to convey a bit more context, often in the form of a URL (org.com/customer/xxxxx). If you do need to put a UUID in the UI, depending on the constraints of the application, it might make sense to just display the first group of characters from the UUID and separately handle the very rare collisions you may encounter, similar to git and its short SHAs.

For any situation where there will be transcription or copying IDs between systems manually, I will typically add another group that incorporates some metadata about where the ID came from (similar to the type code you mention) and a check digit, but obviously I try to avoid any situation that involves transcribing a UUID.

Re: Choosing a Postgres primary key

#119

Earlier quoted context omitted.

> That seems like it's the worst of both worlds: ugly externally visible identifiers Sometimes the external identifier is needed due to interaction with external systems, so it isn't really your choice as the DB/app designer. >, record bloat, Depending on the DB, the opposite can be true. In SQL Server if the integer key is the clustering key, which is usually the case for a table's primary key then you may get a sma…

What I mean is that in most cases, BOTH autoinc int and UUID is wasteful. For example if you have a situation where you really need the high performance of an integer ID like in your SQL Server example, why introduce a UUID into the equation at all? If you are in the unusual situation of needing such extreme performance that you're worrying about 4 vs 8 bytes on the PK, but also need to obfuscate the public facing ID…

> What I mean is that in most cases, BOTH autoinc int and UUID is wasteful.

> I disagree. UUIDs are very useful even as internal identifiers in any area where performance isn't your top concern.

If performance isn't your top concern, having both seems fine. If it is, the int is probably faster anyway.

Re: Choosing a Postgres primary key

#120

I've had good success with using auto-incrementing BIGINTs as internal IDs and creating an additional BYTEA field as external IDs. Foreign keys would be based on the internal IDs, anything user-facing would use external IDs. I think it's a good compromise as it keeps foreign key size small and still allows hiding internal structure from users.

This is the standard way of doing it.
Post reply on HN