Cuando el tamaño de la base de datos crece más allá de millones de filas, es aconsejable empezar a escalar la aplicación y dividir la base de datos en varios servidores físicos.
El mayor problema de dividir la base de datos en varias partes es su posterior sincronización si el usuario solicita datos específicos.
Suponga que tiene una tabla de "artículos", pero como tiene un sitio enorme, hay decenas de millones de artículos más arriba y tiene que dividirlos físicamente en varias máquinas.
Si utilizáramos un entero ordinario como id
(clave primaria) con la configuración como autoincremento, nos encontraríamos muy rápidamente con que al crear registros en diferentes máquinas de forma descentralizada y luego sincronizarlos, hay colisiones de ID y tenemos que renumerar los registros de forma complicada. Además, si estamos resolviendo muchas sesiones a otras tablas, esto puede ser una sobrecarga muy compleja en la que es fácil cometer errores.
Por lo tanto, en lugar de un identificador numérico, podemos generar un UUID
, que es una cadena de texto que se genera mediante un complejo algoritmo que garantiza que será único aunque se genere de forma independiente en varias máquinas.
Ventajas:
19010018
, es fácil adivinar que el usuario 19010017
y otros también existen. El ataque se denomina ataque vectorial.El UUID se puede obtener mediante una simple consulta SQL SELECT UUID();
, pero esto aumenta el número de consultas a la base de datos y perdemos la posibilidad de preparar los datos primero en bloque en la lógica de la aplicación y luego escribirlos de una vez.
Por ello, me gusta utilizar el paquete ramsey/uuid obtenido por Composer como una buena solución. El UUID en sí mismo tiene varias versiones, y el paquete puede generar juguetonamente todos los tipos según sea necesario.
Esto facilita su uso:
require 'vendor/autoload.php';use Ramsey\Uuid\Uuid;// Genera el objeto UUID de la versión 1 (basado en el tiempo)$uuid1 = Uuid::uuid1();echo $uuid1->toString() . "\n"; // e4eaaaf2-d142-11e1-b3e4-080027620cdd// Genera la versión 3 (basada en el nombre y con hash como MD5) del objeto UUID$uuid3 = Uuid::uuid3(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid3->toString() . "\n"; // 11a38b9a-b3da-360f-9353-a5a725514269// Genera la versión 4 (aleatoria) del objeto UUID$uuid4 = Uuid::uuid4();echo $uuid4->toString() . "\n"; // 25769c6c-d34d-4bfe-ba98-e0ee856f3e7a// Genera un objeto UUID de la versión 5 (basado en el nombre y con hash como SHA1)$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid5->toString() . "\n"; // c4a760a8-dbcf-5254-a0d9-6a4474bd1b62
Si utiliza Doctrine, existe una extensión ramsey/uuid-doctrine que genera el ID directamente como un tipo de dato.
En mis primeros intentos utilicé varchar(36)
como clave primaria (ID), pero eso no es para nada una buena idea.
Explicación de la lógica interna:
Las bases de datos MySql (y muchas otras) no pueden utilizar
varchar
,char
u otros tipos de datos que expresen una cadena como clave primaria de forma eficiente. En algunas bases de datos, existe un tipo de datosGUID
que está diseñado para almacenar UUIDs directamente. Si no puede utilizar este tipo, existe un sustituto adecuado con la formabinario(16)
.
Al examinar físicamente la base de datos, el ID se representa en formato HEX (ya que el formato binario no puede mostrarse), en lugar del bonito ID 726c67c4-e5eb-4a4c-8fcc-031da5d6f3c6
, sólo verá 726C67C4E5EB4A4C8FCC031DA5D6F3C6
, que se parece a '?kYߟKg2c;'
en la consulta INSERT.
varchar(36)
a binario(16)
Supongo que representas (o planeas representar) el nuevo ID establecido en la base de datos como:
`id` binary(16) NOT NULL
Sin embargo, el simple hecho de cambiar el tipo de datos no funciona, así que algo como
SET FOREIGN_KEY_CHECKS=0;ALTER TABLE article CHANGE id id BINARY(16) NOT NULLSET FOREIGN_KEY_CHECKS=1;
Hay básicamente dos razones:
deben tener el mismo tipo de datos
. Por lo tanto, es necesario cambiar tanto el tipo de datos para el ID del artículo como, por ejemplo, en la tabla relacional que relaciona los artículos con los autores.Por lo tanto, la única solución correcta es hacer una copia de seguridad de los datos (pero, de todas formas, debería hacerlo antes de cada migración), preparar una base de datos vacía con relaciones funcionales y volver a poner los datos allí mediante la migración.
Si ha generado UUIDs de forma extraña antes, es mejor elegir algún método secuencial para obtener el UUID y renumerar todos los registros. La razón es que la disposición secuencial permite ordenar mejor los valores y crear un btree
, lo que hace que el rendimiento sea casi idéntico al de bigint
.
Si conoce una forma mejor de convertir una base de datos existente de UUID almacenado como varchar a formato binario sin tener que idear migraciones complejas y con preservación de las claves foráneas, estaría muy agradecido por los comentarios.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | es