Auto increment is a terrible idea
clever-cloud.com
clever-cloud.com
This line seems to be the sole justification for the hyperbolic title. You might do tab-completed typo...
There's three justifications listed - the above (fairly minor, imo) one, information disclosure, and entity enumeration (which in this context is just another term for information disclosure).
The part on information disclosure has this line:
> all it takes to know your user count is to create an account. A number of social networks boasting impressive user bases were pwn3d this way
What? I would love a reference or explanation here. Who has been "pwn3d" by exposing their user count?
The "security through obscurity is bad" rhetoric has been rightly debunked plenty at this stage - obscurity is a valid component of layered security, but obscuring your table row count seems... odd. Why would that be a secret?
If you're worried about people being able to enter primary keys into a URL to access/scrape arbitrary records, you need better auth.
We, unfortunately, edit the live db all the time.
Hope everything doesn't blow up.
It's not ideal.
All dev and testing is dont against the live database. YOu just have to be very, very, very careful.
It's something we are trying to implement now, honestly. It's on the TODO list.
The problem I'm having is where do you start? How do you simulate the data flowing through live in the test / dev enviornment.
> How do you simulate the data flowing through live in the test / dev enviornment.
Most don't, rolling with the schema and 'minimum viable' data flow instead.
Make darn sure your backups work by testing restores regularly. Restoring a production backup elsewhere may be an option if it's possible to avoid sensitive data (scrubbing afterward introduces risk of missing something, and should be scripted/repeatable).
Exactly matching production data flow is not as important as avoiding accidentally hosing the prod db.
It doesn't take that long to put some safeguards.
Here is the thing: WHENEVER you say "you have to be VERY careful" or blame someone for making a mistake, that's a place you need to put more checks. The computer should check routine things, not people.
"The problem I'm having is where do you start? How do you simulate the data flowing through live in the test / dev enviornment."
I am guessing you use MySQL so simply set up MYSQL REPLICATION and test all your stuff on the replica database first. Actually you should periodically dump the replica and test on a snapshot, so you can mess up the snapshot.
Main db -> Replica -> Snapshot.
Got any good articles on how to setup test environments. I would then need all the dev code to point to the test one, not the live db and any Web APIs to be able to switch to point to the test db (which must mean running 2 web apis, a live and a test one...)
Seems like it gets very complicated, but probably worth figuring out.
We don't even have CI yet.
If all you want to do is functional testing of the application by yourself, then it is OK to deploy a copy of the software on your desktop along with a test database. I do this for the application I work on, and when I need to test against MSSQL, the Express edition is good enough.
If you need to do load testing or integration testing or other more complicated forms of testing, you usually try to duplicated the expected production environment as well as you can. This may not be possible, so you may have to make decisions about what's most relevant and simply accept those tradeoffs.
EDIT: Don't worry about CI or anything else that fancy and complicated for now. You may not even need it; that depends on your team and how often your application changes. Just for now take the simplest steps you can until you get this basic stuff figured out first.
1. Set up replication, with your test db the replication slave of your production database, such that changes in production are mirrored in testing nearly instantly.
2. Take your production database backups (you do have those, right?) and restore them to the test database. This is less fresh than a replication setup but if you're just doing functional testing it should be enough to say "Well, okay, this little change isn't going to blow everything up."
Depending on what you're working on, chances are there even are people who use _security_ as an excuse to basically prevent any halfway useful emergency strategy one could think of when handling data impossible to pull off (oh the irony).
This isn't really an issue at all but can upset those with OCD-ish tendencies.
There are arguments for and against any technique, but this one seems really ridiculous to me, especially if you consider what any "countermeasure" would do to the code (which is usually much more important than the data in the long run!).
Imagine that the social network does a rewrite, and rather than converting all the old accounts/data it just uses an additional flag to indicate which database to use. In that case a user id would not be globally unique for the website, but it would be unique for the specific database.
This is actually more likely than it sounds. The company can hire a fresh new team to make an experimental new site, and if it's a hit, they can make some ugly hack to do calls to the old website using web APIs to get any old users from that system. This way the new team doesn't need to learn and figure out how the old system was built, which likely lacks any documentation and where the original developers has left the company.
Without a full understanding of the layers of indirection it would be impossible to determine with certainty the number of users a social network has.
Mark Zuckerberg is famously 4 and you can find a lot of famous silicon valley types in the first million user ids :)
It's less about direct "pwn3ge" than about business intelligence. The number of users could be sensitive information for some businesses, but it's not just users. Any item where your volume is valuable proprietary information, you better make sure you aren't exposing an auto-incrementing id. Number of accounts, number of daily orders, number of distribution centers, number of product models, these might be data points that a growing business would prefer not to expose to their competitors via, for example, URL query parameters. You should make competitors work harder for their intelligence.
Also, this is only the case if your raw user IDs are visible. You only need a very basic encryption scheme to hide them. It can be as simple as taking two large prime numbers and sending (uid + x) % y to the user.
https://www.troyhunt.com/kids-pass-just-reminded-us-how-hard...
UUIDs are double the size of 64 bit integers. Which could mean fewer rows on data and index pages, leading to memory pressure, leading to I/O pressure.
UUIDs are also random. I'm not an expert when it comes to PostgreSQL internals. But for MSSQL it's common and recommended to use your PK as a clustered index. The randomness can wreck havoc on the clustered index, causing severe fragmentation due to page splitting. There are some techniques to mitigate the effects of this (decreasing page fill factor, using incrementing UUIDs, using a surrogate IDENTITY clustered key), but each has significant tradeoffs
If you need multi-DC uniqueness. I prefer to add a 2nd DC column. The combination of the two is unique across DC.
If you need to hide the count from customers--often the case--add a 2nd surrogate unique key that is a UUID. This keeps your joins small, but gives you a random key to give back to the customer.
On the read side it shouldn't make any difference, as there's no reason the rows you want to access at any given point should have any correlation to when those rows were created.
I'm not sure what you mean by "inserting new values and rebuilding the index can happen in parallel." In most storage engines using clustered indexes, writes that append to a b-tree are faster than writes that insert somewhere in the middle of a b-tree so there really isn't any rebuilding of the index required in the former case.
>>On the read side it shouldn't make any difference, as there's no reason the rows you want to access at any given point should have any correlation to when those rows were created.<<
That depends entirely on the nature of your workload. There are plenty of common workloads such as sensor data or log entries for which the common read patterns are very much correlated with the order in which the rows were written.
This generates a v1 uuid, so the first few bytes are time dependant, which reduces fragmentation compared to v4 uuids. At the same time, it generates random values for the "mac" section, so it isn't as host-specific and low-entropy as a normal v1 uuid.
You probably don’t want to be literally passing around the literal primary key if you are concerned about leaking security information.
But I think it’s probably better so the user id being known is not a problem. For example, not caring if people know how users/widgets/whatever.
No, it's not. That's a common rationalization of the term by people who have zero knowledge of relational DB theory, but a relation is answer of entities, not a relationship between them; do, in this graph interpretation, “relation” isn't the right name for an edge, as it names a set of structurally similar nodes. An edge is a “(foreign key) relationship”.
> The role of a primary key is to provide a stable, indexable reference to an entity. You have two ways to construct your primary key: either as a semantic key, or as a technical key.
Why is this piece inventing new language when there are existing, widely recognized terms? This is the classic ”natural key” vs. “surrogate key” distinction.
> Also, serial is a 32 bit integer.
There are good reasons to avoid autoincrement, but that's not one, it’s just a reason to use bigserial instead of serial.
> If you don't have mutable state, then you don't need a technical primary key, and you can use a semantic key.
Even if you have mutable state, you can use a natural (“semantic”) key instead of surrogate (“technical”) key so long as the identifying attributes are immutable (which, if they are genuinely identifying, they logically must be.)
E.g.
uuid v4: de7013fb-658e-40a0-9c02-d23080b57fdb
base58 : tt7Udwi9Tx9VE1ze7HDBxi
Here's a lib I wrote in c++ for doing this, with python and erlang bindings, https://github.com/cieplak/uuidYou could also use base64, but it has some non-URL-friendly characters so it would have to be escaped.
Here's the code I'm using for Rails: https://gist.github.com/ndbroadbent/628ef1d0c044e402d7501fb7...
One of my biggest wishes from a database would be a function similar to auto increment, except that would pick randomly from the column's full integer range, guaranteed not to collide. I almost never need 128 bits -- 32 bits will let me handle 4 billion users.
It seems to me that a column index could be used to do that quickly -- essentially, if it's a binary tree, pick branches randomly but never pick a branch known to be full.
In general, order determinism. The elements in the table or list don't shuffle on every refresh.
* it prevents tables having any obvious "natural" ordering. I still see developers writing queries, relying on the order of the data but not specifying ORDER BY. The flip-side to this is that sometimes they discover they need an ordering that they don't have - e.g. date record created - which is also great practice. * it removes pre-conceptions that developers might have about auto increments. I've seen auto-increment used for invoice numbers more times than I care to remember: a task it's entirely unsuited for. If a developer needs a monotonic ID, but don't have an AI field, they have to think longer and harder about the type of number they need, and what the rules about transactions rolling back need to be. Again, better correctness is the result (at a cost of more time / thought).
Auto-increment isn't a terrible idea at all, and there are good UI reasons to not like it, but there are also decent reasons to use something else.
When you need to shard your table, the autoincrementing is is a bottleneck across all shards as you need to hold a lock on it when incrementing. In fact, that's exactly how it's implemented explicitly in POSTGRES.
And if you want something that's way MORE distributed such as CockroachDB or (RIP) Riak then forget about it.
But you don't necessarily need UUIDs. You can use for example https://www.npmjs.com/package/shortid
What we do is generate a random 7 character string (alphanumeric lowercase) and then check if it exists in the db and - if not - insert it, using a transaction. Otherwise repeat until one works.
* Typing SQL queries in the development database.
* Trying to remember which record is which by looking at the UUIDs. Often the first two characters are enough but it's a lot of random noise.
Inside the program it's all the same. No apparent performance penalties so far, but we don't have much data.
I used slugs in Rails apps not to disclose numerical ids from public URLs and data. There is no need to do that with UUIDs. However, if this were a server rendered web app I'll probably still generate slugs and use them in the URLs. UUIDs in permalinks are just too weird to look at.
A similar solution called COMB identifiers dates back at least 15 years in the MSSQL community.
Watch out for sort order in MSSQL with binary ULIDs cast to UNIQUEIDENTIFIERs; they sort with the last 4 bytes as most significant, so some byte swapping is necessary to prevent the performance degradation caused by index page splitting (I wrote a ULID implementation for MSSQL that handles this).
Looks like there's an old patent related to this:
When have a primary key ever been sensitive infomation ? And why would they be predictable ?
[0]https://medium.com/swlh/watch-paint-dry-how-i-got-a-game-on-...
The downside is that they are really long and can’t be typed or remembered.
Using integers, auto incrementing or not, reduces the resource pressure on indexing when doing complex joins.
When you need to expose a key, expose the APIID.
Many of your critiques stem from the simple assumption that primary keys will be shared externally (see final link below on why that's not necessarily a good idea). There's nothing wrong with using auto-increment primary keys, then creating a different, dedicated field to store external identifiers. Before UUID's were invented we used simple random numbers for the latter (converting to hex or base-64 in URL's if more compactness was desired) and they worked just fine.
I agree 100% with frik's comments. Keep in mind your team will be dealing with primary keys a LOT. In our own projects I've seen developers become a little less efficient when working with databases that overuse bulky UUID's. I don't feel they're particularly "human friendly". They take up more pixel width in SQL query results, leading to more back and forth scrolling. JOIN results starting with a whole screenfull of ID garble weren't uncommon. Unless your developers have eidetic memory they wind up doing more copy and pasting (with the associated cursor movements, clicks and ergonomic odometer ticks) to jot them down or share them. Ever tried to name a UUID over the phone? In the early days of your project, when you're doing a whole heck of a lot of raw interaction with your database, auto-increments can offer a slight advantage of being so much more compact, easier to remember, and simple to communicate. It may not sound like much of an edge, but it's one less friction point slowing down collaboration.
I think you might also be overlooking the fact that some UUID implementations are designed to be unique, not random, and depending on which you use might leak information or be predictable (https://blogs.msdn.microsoft.com/oldnewthing/20120523-00/?p=...). I'd be careful about sharing those primary keys unless you're sure they're generated from a decent [maybe cryptographically secure pseudo-] random number generator and aren't derived from the current time / MAC address. If you want to dive deeper there's a subtle tradeoff here between privacy and ease of sorting (e.g. in a clustered index).
I empathize with your argument that sequential keys can lend themselves to typos deleting the wrong record (which can be a pain even in non-production environments) and have seen arguments to the benefit of knowing UUID's in advance before a record is inserted (less constraints writing your SQL, reduced roundtrips) and ease of merging data between databases. It can also be deadly if your auto-increment generator reuses deleted keys and your system doesn't account for that. In the past I've created my own ID dispensing mechanisms optimized according to the features I wanted (e.g. sequential or random, reservable, globally or locally unique, human-friendly with number of digits growing in chunks, etc) but admittedly that could be overkill for your project.
I don't have anything personal against UUID's and don't mean to come off condescending, I just think you missed some aspects in the message you broadcast.
Here's a great, easy-to-read analysis of the pros/cons and when to use each: https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-ca...
I favor auto increment IDs any day, except in specific situations. It's worth to consider to learn how other sites dealt with IDs in public, while internally using auto increment IDs. Short: there are several strategies.
But to each their own, if you love UUID, just use it, but don't write lame blog posts how terrible things are.
Don't write lame HN comments deriding others opinions.
id utility
1 electric
2 gas
3 water
What is the point of the numeric ids versus simply making the name the primary key? I'd argue there is none, plus there significant drawbacks.
Every time we start with the utility name and need to get the row id, the database needs to search on a non-primary key. This is going to be commonly used in JOINs (and tables designed with superfluous ids lend themselves to many gratuitous joins.) Optimizing the primary keys based on the typical data used to search (actual key data from the user's standpoint) will make your DB blazing fast.
Also, using integers everywhere (or even worse, UUIDs) destroys the readability of queries. How many integer ids could be used in a query and still be understandable? The id `3` could mean almost anything, but the id `water` brings meaning to queries. The benefits to maintainability are significant.
Lazy DB designers seem to have a bad habit of adding auto-ids to everything regardless of value. I even see joiner tables (that should be using the composite primary key of multiple foreign keys) using unnecessary auto ids! This is sloppy work.
In short, I have found significant benefits to using semantic keys first (if appropriate - there are definitely reasons to use incremental integer or UUID keys in many tables) for these reasons:
1. Fewer columns without loss of meaning 2. Fewer joins needed 3. Faster searches 4. More human-readable
This is for every single row in your database and will increase the size of your database substantially.
To clarify, UUID is stored in the db as 16 bytes (128 bits): https://en.wikipedia.org/wiki/Universally_unique_identifier#...
You would not store the 32 hex characters plus 4 hyphen characters as strings in the db. That UUID string format is for human readable display not disk storage.
(To downvoters, if 16 bytes is incorrect, please reply with what you believe to be the correct # of bytes for UUID.)
Use the native UUID data type, which only uses 16 bytes (UUIDs are basically just 128 bit integers). That is still double the size of a 64 bit integer, but the trade off is that it can be random and universally unique.
Try it for yourself.
As a side comment about MySQL, there are various techniques to store UUID more compactly as 16 bytes inside of a "binary(16)" type like this:
https://stackoverflow.com/a/28252188
... or similar technique shown at the bottom of the Percona blog:
https://www.percona.com/blog/2014/12/19/store-uuid-optimized...