Estamos considerando usar valores UUID como claves principales para nuestra base de datos MySQL. Los datos que se insertan se generan a partir de docenas, cientos o incluso miles de computadoras remotas y se insertan a una velocidad de 100 a 40 000 inserciones por segundo, y nunca realizaremos ninguna actualización.
La base de datos en sí generalmente alcanzará alrededor de 50 millones de registros antes de que comencemos a seleccionar datos, por lo que no es una base de datos masiva, pero tampoco pequeña. También estamos planeando ejecutar InnoDB, aunque estamos abiertos a cambiar eso si hay un motor mejor para lo que estamos haciendo.
Estábamos listos para usar el UUID Tipo 4 de Java, pero en las pruebas hemos visto un comportamiento extraño. Por un lado, estamos almacenando como varchar (36) y ahora me doy cuenta de que estaríamos mejor usando binary (16), aunque no estoy seguro de cuánto mejor.
La pregunta más importante es: ¿en qué medida estos datos aleatorios arruinan el índice cuando tenemos 50 millones de registros? ¿Estaríamos mejor si usáramos, por ejemplo, un UUID de tipo 1 en el que los bits más a la izquierda tuvieran una marca de tiempo? ¿O tal vez deberíamos deshacernos de los UUID por completo y considerar las claves primarias auto_increment?
Estoy buscando ideas / consejos generales sobre el rendimiento de diferentes tipos de UUID cuando se almacenan como un índice / clave principal en MySQL. ¡Gracias!
Respuestas:
Un UUID es una identificación única universal. Es la parte universal que deberías considerar aquí.
¿ Realmente necesitas que los ID sean universalmente únicos? Si es así, los UUID pueden ser su única opción.
Me gustaría sugerir fuertemente que si haces UUID de uso, los almacena como un número y no como una cadena. Si tiene más de 50 millones de registros, el ahorro de espacio de almacenamiento mejorará su rendimiento (aunque no podría decir cuánto).
Si sus ID no necesitan ser universalmente únicos, entonces no creo que pueda hacerlo mucho mejor que simplemente usando auto_increment, lo que garantiza que los ID serán únicos dentro de una tabla (ya que el valor se incrementará cada vez)
fuente
binary
formato. Me refiero a un número de 128 bits, en lugar de una cadena de 288 bits. Por ejemplo, la palabra 'hola' en ASCII es68 65 6C 6C 6F
, que es el número 448,378,203,247. El almacenamiento de la cadena '68656C6C6F' requiere 10 bytes. El número 448,378,203,247 requiere solo 5. En total, a menos que realmente necesite la primera U en UUID, no puede hacerlo mucho mejor queauto_increment
En mi trabajo, usamos UUID como PK. Lo que puedo decirles por experiencia es que NO LOS USE como PKs (SQL Server por cierto).
Es una de esas cosas que cuando tienes menos de 1000 registros está bien, pero cuando tienes millones, es lo peor que puedes hacer. ¿Por qué? Debido a que los UUID no son secuenciales, cada vez que se inserta un nuevo registro, MSSQL debe ir a buscar la página correcta para insertar el registro y luego insertar el registro. La consecuencia realmente desagradable de esto es que las páginas terminan todas en diferentes tamaños y terminan fragmentadas, por lo que ahora tenemos que hacer una desfragmentación periódica.
Cuando usa un autoincremento, MSSQL siempre irá a la última página, y terminará con páginas del mismo tamaño (en teoría) por lo que el rendimiento para seleccionar esos registros es mucho mejor (también porque los INSERTs no bloquearán la tabla / página para hasta la vista).
Sin embargo, la gran ventaja de usar UUID como PK es que si tenemos clústeres de DB, no habrá conflictos al fusionar.
Recomendaría el siguiente modelo: 1. PK INT Identity 2. Columna adicional generada automáticamente como UUID.
De esta manera, el proceso de fusión es posible (UUID sería su clave REAL, mientras que PK sería algo temporal que le brinda un buen rendimiento).
NOTA: Que la mejor solución es usar NEWSEQUENTIALID (como decía en los comentarios), pero para la aplicación heredada con poco tiempo para refactorizar (y peor aún, no controlar todas las inserciones), no es posible hacerlo. Pero, de hecho, a partir de 2017, diría que la mejor solución aquí es NEWSEQUENTIALID o hacer Guid.Comb con NHibernate.
Espero que esto ayude
fuente
Algo a tener en cuenta es que los aumentos automáticos se generan uno a la vez y no se pueden resolver con una solución en paralelo. La lucha por el uso de UUID finalmente se reduce a lo que desea lograr frente a lo que potencialmente sacrifica.
Sobre el rendimiento, brevemente :
Recomiendo leer las siguientes dos publicaciones:
Calculo que entre los dos responden a tu pregunta.
fuente
Tiendo a evitar UUID simplemente porque es difícil de almacenar y difícil de usar como clave primaria, pero hay ventajas. El principal es que son ÚNICOS.
Por lo general, resuelvo el problema y evito UUID mediante el uso de campos de clave dual.
COLECTOR = ÚNICO ASIGNADO A UNA MÁQUINA
ID = REGISTRO RECOGIDO POR EL COLECTOR (campo auto_inc)
Esto me ofrece dos cosas. Velocidad de los campos de autoinclución y singularidad de los datos que se almacenan en una ubicación central después de que se recopilan y agrupan. También sé mientras navego por los datos dónde se recopilaron, lo que a menudo es bastante importante para mis necesidades.
He visto muchos casos al tratar con otros conjuntos de datos para clientes en los que han decidido usar UUID pero aún tienen un campo para el lugar donde se recopilaron los datos, lo que realmente es una pérdida de esfuerzo. Simplemente usar dos (o más si es necesario) campos como clave realmente ayuda.
Acabo de ver demasiados golpes de rendimiento con UUID. Se sienten tramposos ...
fuente
En lugar de generar claves únicas de forma centralizada para cada inserción, ¿qué tal si asignamos bloques de claves a servidores individuales? Cuando se quedan sin claves, pueden solicitar un nuevo bloque. Luego, resuelve el problema de la sobrecarga conectando para cada inserto.
Keyserver mantiene la siguiente identificación disponible
servidor 1 puede insertar 1000 registros hasta que necesite solicitar un nuevo bloque
Podría crear una versión más sofisticada en la que un servidor podría solicitar la cantidad de claves necesarias o devolver los bloques no utilizados al servidor de claves, que por supuesto necesitaría mantener un mapa de bloques usados / no utilizados.
fuente
Asignaría a cada servidor una identificación numérica de manera transaccional. Luego, cada registro insertado aumentará automáticamente su propio contador. La combinación de ServerID y RecordID será única. El campo ServerID se puede indexar y el rendimiento de selección futuro basado en ServerID (si es necesario) puede ser mucho mejor.
fuente
La respuesta corta es que muchas bases de datos tienen problemas de rendimiento (en particular con volúmenes INSERT altos) debido a un conflicto entre su método de indexación y la entropía deliberada de los UUID en los bits de orden superior. Hay varios trucos comunes:
... pero todos estos son trucos, y probablemente frágiles.
La mejor respuesta, pero desafortunadamente la más lenta, es exigirle a su proveedor que mejore su producto para que pueda manejar los UUID como claves primarias como cualquier otro tipo. No deberían obligarlo a lanzar su propio truco a medias para compensar su fracaso en resolver lo que se ha convertido en un caso de uso común y solo continuará creciendo.
fuente
¿Qué pasa con algunos UID hechos a mano? Dé a cada uno de los miles de servidores un ID y convierta la clave principal en una clave combinada de autoincremento, MachineID ???
fuente
Dado que la clave principal se genera de manera descentralizada, no tiene la opción de usar un auto_increment de todos modos.
Si no tiene que ocultar la identidad de las máquinas remotas, utilice UUID de tipo 1 en lugar de UUID. Son más fáciles de generar y al menos no pueden dañar el rendimiento de la base de datos.
Lo mismo ocurre con varchar (char, en realidad) frente a binario: solo puede ayudar en las cosas. ¿Es realmente importante, cuánto se mejora el rendimiento?
fuente