[{"data":1,"prerenderedAt":52},["ShallowReactive",2],{"$f2qj6m5gopzr0y":3,"recommended":21},{"title":4,"path":5,"date":6,"updated":7,"tags":8,"excerpt":10,"readingMinutes":11,"commentCount":12,"html":13,"comments":14,"next":15,"prev":18},"UUIDs in MySQL are really not random","\u002F2019\u002F02\u002Fuuids-in-mysql-are-really-not-random\u002F","2019-02-05T13:36:00.003Z","2019-02-11T09:34:18.805Z",[9],"programming","Universally Unique Identifiers (UUIDs) are great. I love how you can tell the progress of a batch job just by looking at the current UUID. If it starts with 0... , the task is less than 1\u002F16th done. If it starts with…",2,0,"\u003Cp>\u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FUniversally_unique_identifier\">Universally Unique Identifiers\u003C\u002Fa> (UUIDs) are great. I love how you can tell the progress of a batch job just by looking at the current UUID. If it starts with \u003Cstrong>0...\u003C\u002Fstrong>, the task is less than 1\u002F16th done. If it starts with \u003Cstrong>7d..\u003C\u002Fstrong>, we're almost halfway there. At \u003Cstrong>ff...\u003C\u002Fstrong> we are nearing the end. The fact that you can tell this rests on two principles: 1) you sort your jobs by their uuid and 2) UUIDs are random, as in, \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FUniform_distribution_(continuous)\">distributed uniformly\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>However, last week, I noticed a strange thing: a clearly visible pattern in the uuid column of a database table. It should be impossible, but there it was. It looked like this:\u003C\u002Fp>\n\u003Cp>\u003Ccode>&gt; SELECT uuid FROM example ORDER BY id;   4f95de28-0fd1-48db-ad2e-34ecd169c483   4331cb9e-1d91-11e9-be2c-45923c63e8a2   4331cc4c-1d91-11e9-be2c-45923c63e8a2   4331ccec-1d91-11e9-be2c-45923c63e8a2   4331cd7e-1d91-11e9-be2c-45923c63e8a2   c7e2f124-f6ba-4434-843f-89958a7436ec   4331ce10-1d91-11e9-be2c-45923c63e8a2   4331ce9e-1d91-11e9-be2c-45923c63e8a2   4331cf28-1d91-11e9-be2c-45923c63e8a2   4331cfaf-1d91-11e9-be2c-45923c63e8a2   4331d017-1d91-11e9-be2c-45923c63e8a2   4331d0c6-1d91-11e9-be2c-45923c63e8a2   4331d139-1d91-11e9-be2c-45923c63e8a2   4331d1a7-1d91-11e9-be2c-45923c63e8a2   4331d20e-1d91-11e9-be2c-45923c63e8a2   4331d271-1d91-11e9-be2c-45923c63e8a2   4331d2d7-1d91-11e9-be2c-45923c63e8a2   3e18f8dd-b1d3-4e16-8a81-4bdceac91772   4331d33a-1d91-11e9-be2c-45923c63e8a2   f6b8658d-846b-4418-a79c-713db516203e   4331d3a8-1d91-11e9-be2c-45923c63e8a2   4331d40d-1d91-11e9-be2c-45923c63e8a2   4331d48e-1d91-11e9-be2c-45923c63e8a2   4331d4fe-1d91-11e9-be2c-45923c63e8a2   4331d567-1d91-11e9-be2c-45923c63e8a2   4331d5cb-1d91-11e9-be2c-45923c63e8a2   4331d630-1d91-11e9-be2c-45923c63e8a2   4331d691-1d91-11e9-be2c-45923c63e8a2   14a5cb4c-f336-4240-aff3-4ffcfa8d135f   4331d6f9-1d91-11e9-be2c-45923c63e8a2   4331d762-1d91-11e9-be2c-45923c63e8a2   4331d7c8-1d91-11e9-be2c-45923c63e8a2   4331d83d-1d91-11e9-be2c-45923c63e8a2   \u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>Hm, that is \u003Cstrong>very\u003C\u002Fstrong> weird. Did we maybe convert an old auto-incrementing integer column into UUIDs in a very stupid way? Did we maybe use UUID version 3 or 5? Did our library corrupt the first bits of the binary representation of the UUID? After a while, I remembered that we initialized this column like so:\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>UPDATE example SET uuid = UUID() WHERE uuid IS NULL;\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>I also remembered reading this in the \u003Ca href=\"https:\u002F\u002Fdev.mysql.com\u002Fdoc\u002Frefman\u002F8.0\u002Fen\u002Fmiscellaneous-functions.html#function_uuid\">MySQL documentation\u003C\u002Fa>:\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>&quot;Warning: Although UUID() values are intended to be unique, they are not necessarily unguessable or unpredictable. If unpredictability is required, UUID values should be generated some other way.&quot;\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>If you are like me, you won't use UUID() for secrets after reading this (and I didn't!). If you are even more like me, you will think that this is like the difference between \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002F\u002Fdev\u002Frandom\">\u002Fdev\u002Furandom and \u002Fdev\u002Frandom\u003C\u002Fa> and that the uniform distribution law still applies here. However, to my great surprise, the UUIDs generated by UUID() are not uniform at all! The result is that a significant portion of UUIDs in our database are not uniform:\u003C\u002Fp>\n\u003Cp>\u003Ccode>&gt; SELECT COUNT(*), LEFT(uuid, 1) FROM example GROUP BY LEFT(uuid, 1);   +----------+---------------+   | count(*) | LEFT(guid, 1) |   +----------+---------------+   | 1943     | 0\u003C\u002Fcode>             |\n\u003Ccode>| 1871\u003C\u002Fcode>     | 1             |\n\u003Ccode>| 1913\u003C\u002Fcode>     | 2             |\n\u003Ccode>| 1843\u003C\u002Fcode>     | 3             |\n\u003Ccode>| 3050\u003C\u002Fcode>     | 4             |\n\u003Ccode>| 1943\u003C\u002Fcode>     | 5             |\n\u003Ccode>| 1889\u003C\u002Fcode>     | 6             |\n\u003Ccode>| 1866\u003C\u002Fcode>     | 7             |\n\u003Ccode>| 1865\u003C\u002Fcode>     | 8             |\n\u003Ccode>| 1903\u003C\u002Fcode>     | 9             |\n\u003Ccode>| 1868\u003C\u002Fcode>     | a             |\n\u003Ccode>| 1898\u003C\u002Fcode>     | b             |\n\u003Ccode>| 1854\u003C\u002Fcode>     | c             |\n\u003Ccode>| 1897\u003C\u002Fcode>     | d             |\n\u003Ccode>| 1941\u003C\u002Fcode>     | e             |\n\u003Ccode>| 1836\u003C\u002Fcode>     | f             |\n\u003Ccode>+----------+---------------+   \u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>So, the lesson for today is: take warnings in documentation seriously. If you used UUID() for data that is supposed to be secret (like passwords), you have a serious problem, as these secrets can now be easily guessed.\u003C\u002Fp>\n\u003Cp>Edit: read this \u003Ca href=\"\u002F2019\u002F02\u002Fuuids-in-mysql-follow-up\u002F\">follow-up post\u003C\u002Fa>\u003C\u002Fp>\n",[],{"title":16,"path":17},"UUIDs in MySQL follow up","\u002F2019\u002F02\u002Fuuids-in-mysql-follow-up\u002F",{"title":19,"path":20},"Highly Productive Teams and their Speed Bumps","\u002F2019\u002F01\u002Fhighly-productive-teams-and-their-speed\u002F",[22,27,32,35,39,42,45,49],{"title":23,"path":24,"note":25,"series":26},"Mapping out Dark Code","\u002F2026\u002F03\u002Fmapping-out-dark-code\u002F","2026",false,{"title":28,"path":29,"note":30,"series":31},"The software that I love","\u002F2022\u002F10\u002Fthe-software-that-i-love\u002F","Series · 36 posts",true,{"title":33,"path":34,"note":25,"series":26},"Navigating the Fog of War: Age of Empires, AI Agents, and the Rise of Dark Code","\u002F2026\u002F05\u002Fnavigating-fog-of-war-age-of-empires-ai\u002F",{"title":36,"path":37,"note":38,"series":26},"The Future of Programming Systems - four thoughts","\u002F2024\u002F09\u002Fthe-future-of-programming-systems-four\u002F","2024",{"title":40,"path":41,"note":38,"series":26},"Always Optimize the Feedback Loop","\u002F2024\u002F07\u002Falways-optimize-feedback-loop\u002F",{"title":43,"path":44,"note":38,"series":26},"\"Security Is Our Top Priority\" is BS","\u002F2024\u002F07\u002Fsecurity-is-our-top-priority-is-bs\u002F",{"title":46,"path":47,"note":48,"series":26},"10x teams","\u002F2025\u002F02\u002F10x-teams\u002F","2025",{"title":50,"path":51,"note":48,"series":26},"Hiring, from the Other Side","\u002F2025\u002F10\u002Fhiring-from-other-side\u002F",1792876809086]