How long do GUIDs really need to be?
eager.io
eager.io
Using the standard UUID generation facilities in your OS of choice there's zero chance you get something wrong and screw yourself.
UUIDs are great because we can pretty much guarantee global uniqueness. Acquire a company, decide to integrate with someone, need to merge a database, etc? No problem, zero chance of record collisions no matter what happens in the future. (It also means zero chance of accidentally interpreting record #58274 as type A when you meant type C).
Furthermore, a 1 in 1 million chance of collision is far too frequent for my liking, but even if it were acceptable what happens when your service/product becomes far more popular than you imagined and you blow through your initial estimates?
For comparison, here's an eager.io URL:
https://eager.io/app/ZYBle8qUhKFJ
And here's an equivalent with UUID in base-64:
https://eager.io/app/b8tRS7h4TJ2Vt43Dp85v2A
It's a rare application for which those differences matter. (NB: I don't know anything about eager.io... it might be important for them!)
Given what we've seen from the browser vendors lately (hiding bits of the address bar, etc), I'd say there's a lot of momentum to disagree with that statement.
(FWIW I like seeing UUIDs in my URLs, because it tells me that the company on the other end has its shit together. That might be a bit programmer-specific though)
We looked at using UUIDs last year for a project in MySQL since we had good use cases for handling ID generation outside of the data tier. We originally prototyped using type-4 UUIDs, but found the locality problem made that a non-starter (updating indexes seemed to get exponentially slower when tables went over 10m rows). But switching to type-1 UUIDs made that problem go away. Still, we eventually chose not to use UUIDs since 128 bits made our indexes too large. At the DB level, we represented them as BINARY(16), but even with the pain that caused, our indexes were still too large.
Perhaps it's just a MySQL thing (I didn't make that choice) but, from our testing, I'd be very wary of using 128-bit IDs and would probably choose something like Snowflake or other centralized ID generation service.
http://blogs.msdn.com/b/larryosterman/archive/2004/03/30/104...
In any case, the article exists to help you decide how long you need to make your ids for you to be comfortable. If you decide you aren't comfortable with a 1 in a million chance, the math is there for you to figure out what does work for you.
Their purpose is to be universally unique, presumably indefinitely. The practical choice is to align on a power of 2.
When UUIDs were invented, they were based on a MAC address plus a timestamp. MAC addresses are 48 bits, leaving 16 bits. Of these, between 1 and 3 are used up giving the variant code and another four bits are used giving the version code.
If they'd used 64 bits, there'd be between 8 and 11 bits left for a unique timestamp. Which is OK, but not a very strong guarantee for a "unique" scheme.
As it happens the length has made it possible to create multiple versions of UUIDs that are generated in different ways. UUID has been successful at separating the concerns of the structure of the ID from how it is generated. Most alternative schemes are effectively implementation-defined.
http://www.postgresql.org/docs/8.3/static/datatype-uuid.html makes it look pretty easy…
In any case though, there are certainly many bad things which could happen to your company which are more likely than 1 in a million.
Well until 5236-03-31 21:21:00 UTC (assuming my calculations are correct) using v1 UUIDs and assuming unique MAC addresses and no race conditions.
At one company I worked at we once received a huge shipment of ethernet cards (several thousand) that all had the same MAC address. We didn't realize it until customers who had purchased multiple pieces of equipment began to call in.
I've heard several similar anecdotes from other engineers at other companies.
Yeah, very bad assumption.
Needless to say NetApp where quite embarrassed and we are glad that we noticed it in pre-prod otherwise it could have been quite bad.
* https://blog.twitter.com/2010/announcing-snowflake
* http://engineering.custommade.com/simpleflake-distributed-id...
* http://boundary.com/blog/2012/01/12/flake-a-decentralized-k-...
That said, this appears to be exactly the scheme MongoDB uses (except Mongo IDs are 96 bits).
2 machines can have the same MAC addresses (they are reprogrammable) and can operate at the same microsecond.
"aren't randomly generated" is not practical constraint in a high-speed distributed system (where you don't have time for synchronization overhead).
I imagine this approach could also work on Postgres and other dbs that have made GUID/UUIDs a first class data type. You'd just have to understand how that database applies its indexing algorithm.
Description of the GuidComb approach here: http://www.informit.com/articles/article.asp?p=25862
GuidComb implementation in C# (from NHibernate core) here: https://github.com/nhibernate/nhibernate-core/blob/master/sr...
http://www.solipsys.co.uk/new/TheBirthdayParadox.html?HN_201...
It's intended to be gentle, but a few people have said it's a bit quick in places. I'd appreciate any feedback.
Added in edit: I've submitted it as a separate item - it's been a few months since it was discussed here.
For example you could encode a number that identifies the host (like, the last byte or last two bytes of the public IP address) and the process id of the process generating the ID, and as a result you need less entropy for avoiding collisions.
But you risk that somebody who doesn't know UID algorithm screws things up. For example if you use the last byte of the IP address, and some network administrator decides to give each host an IPv6 net, the last byte of the IP might very well be one for each host. (OK, that's a bit of a contrived example; maybe PID namespaces are a better one?).
Or things outside of your control. Your company gets acquired by a much bigger one, and for some reason they decide to use your system for the whole company. Or for a huge customer. And now you're facing a factor 1000 more records than you ever thought possible. Or a factor 10000. History is full of software systems that have been used way beyond what they were planned for originally, and of course nobody revisited all relevant design decisions.
Second point to consider: by making parts of your UIDs deterministic, you also leak information. Like when a dataset was created, and on what host. Which might be relevant for timing attacks, or other kinds of security nastiness that you don't even think about right now.
UUID v1 does this by encoding the MAC address as part of the UUID. v3 and v5 use a scheme that encode information from other namespaces, eg FQDNs.
Locality is definitely important, but I must be missing something -- if lookup by date, machine ID etc is required, why not create indices on those fields? Why rely on coincidental locality?
// Make a "pretty unique" ID for this session.
// Since RethinkDB doesn't have a way for us to guarantee a _short_
// random unique value (short of trying the insert and regenerating if it
// doesn't save), we'll just have to rely on the unlikeliness of a collision
// with both this time-based ID and the title-based slug.
// I'm sure this will never ever cause any problems ʘ‿ʘ
var alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
var id = new Date().getTime().toString().match(/.{1,2}/g).map(function(val){return alphabet[val % alphabet.length];}).join('');
var slugPart = slug((this.title || "").substring(0,60).toLowerCase());
this.url_slug = id + "/" + slugPart;
That is, get a current timestamp (in milliseconds), and use every group of 2 digits to pull a letter out of an alphabet string. Then append "/title-of-the-thing-made-url-safe". This results in strings that look like "ee7zrm9/something-goes-here", which is then used as the primary key for the document. It's not perfect by any means, but it gets the job done, and I thing appending the title makes collisions extremely rare.I had a catastrophic bug (ala private data going to the wrong person) from 96-bit (32 bit segment number, 64 bit random local docid) ID collisions when the caching code decided it was going to use docid as the cache key without realizing it was missing a bunch of bits.
For a blog post, for example, there is a title. The classic way of adding a readable date to the URL is useful, if you're reading the URL in the first place. This particular blog post uses that approach: https://eager.io/blog/how-long-does-an-id-need-to-be/.
For other objects there might still be useful data. Instead of /invitation/3jdix8jAJm you might have /invitation/myblog/bob@example.com/u7pW, the last part being an auto-generated random component. The benefit is that the ID becomes self-explanatory (self-describing) and very nice for tracing through logs and the like. Of course, one has to be careful about not exposing anything exploitable.
It's a moot issue anyways since type-1 UUIDs don't have this problem. They're monotonic, which allows index updates to only need to append.
But then, it's MySQL. I was surprised by your experience, but not that much.
Just use the GUID externally and use have a sequential primary key as the table index?
> I mean, given the birthday paradox calculation, couldn't you just take head of the uuidv4 (i.e.: the first x characters) to arrive at the collision/space-consumption tradeoff you want?
Yes you could, but is that actually any easier than "generate x random bytes"?
I do like the point that UUIDs are generally stored as strings whereas they represent a 122 bit value. Seems encoding the UUIDs as binary would offer much greater efficiency in storage space as well as indexes.
Also, to whoever downvoted my very first post here: Way to build a community @sshole. I'm never commenting here again thanks to you, jack@ss.
And anyway, all a UUID is, ultimately, is a big number. It's a simple transcoding to get it into base-10 integer format and back.
OR you could store the uuid twice, once "natively" and once as a computed column. Searches on the native field would be faster vs. an index on a string column.
> you're presumably doing so so that humans can run ad-hoc reports (otherwise there are better datastores)
oh dear, someone has drank the NoSQL punch... Storing data relationally is NOT something only suitable for ad-hoc queries by end-users! :O
I have either a high or total assurance that any UUID I self-assign has never been used anywhere, ever, for anything else, by anyone else, in any system.
In fact, the odds that I will have a collision with another UUID are lower than a cosmic ray striking a computer at the moment when it is performing the uniqueness check.
Unfortunately, or fortunately, depending on your requirements, it is easily predictable.
UUIDs solve a problem many, many people do have - identifying things over time and space - without having to even consider communication/coordination. That's a very powerful property.
The debate regarding the length is the same though.