MySQL: restricción única en columna grande

10

Estoy tratando de crear una tabla InnoDB que contenga una VARCHARcolumna que pueda contener hasta 3071 caracteres. Me gustaría imponer una UNIQUErestricción en los datos de esta columna.

MySQL parece imponer restricciones usando un índice. En InnoDB, los tamaños de índice parecen estar limitados a 767 bytes, lo que no es suficiente para la VARCHAR(3071)columna que contiene los datos.

¿Alguna idea sobre cómo hacer que la base de datos haga cumplir la unicidad de los datos, sin comprometer la longitud máxima de los datos o el uso de InnoDB?

Guus
fuente

Respuestas:

10

No desea un gen_clust_index gigantesco (índice agrupado interno). Ese tamaño es impío enorme incluso para un índice secundario.

Es posible que tenga que recurrir a disparadores o procedimientos almacenados para verificar la clave con mucha anticipación.

También podría pensar en realizar una llamada de función SHA1 usando el VARCHAR(3071)campo. SHA1 devolverá un campo de 40 caracteres. Este hash puede ser justo lo que necesita indexar.

Supongamos que tienes esto

CREATE TABLE mytable
(
    id int not null auto_increment,
    txt VARCHAR(3071),
    primary key (id)
) ENGINE=InnODB;

y quieres hacer un UNIQUEíndice en txt. Prueba el enfoque SHA1

CREATE TABLE mytablenew LIKE mytable;
ALTER TABLE mytable ADD txtsha1 CHAR(40);
ALTER TABLE mytable ADD UNIQUE KEY (txtsha1);
INSERT INTO mytablenew (id,txt,txtsha1)
SELECT id,txt,SHA1(txt) FROM mytable;

Entonces, cuéntales

SELECT COUNT(1) FROM mytable;
SELECT COUNT(1) FROM mytablenew;

Si los recuentos son iguales, ¡¡¡FELICIDADES !!! Ahora tiene un índice único de longitud 40. Puede terminar con:

ALTER TABLE mytable RENAME mytableold;
ALTER TABLE mytablenew RENAME mytable;
DROP TABLE mytableold;

Esto podría ser más atómicamente como se señala en los comentarios a continuación:

RENAME TABLE mytable TO mytableold, mytablenew TO mytable;
DROP TABLE mytableold;

Realice esto en cualquier tabla que desee tener esta gran columna. Debe recordar agregar el SHA1 de los datos junto con los datos sobre INSERT.

La probabilidad de duplicar claves es de 1 en 2 a la potencia 160 (que 1.4615016373309029182036848327163e + 48. Si obtengo la cifra exacta, la publicaré algún día).

Darle una oportunidad !!!

RolandoMySQLDBA
fuente
+1 ¡Esta es básicamente una muy buena idea! Me gustaría combinarlo con un disparador que verificaría si dos resúmenes son iguales, el contenido también es el mismo, exactamente cómo funciona un HashMap en Java ...
ppeterka
1
Rolando - Tengo muchas objeciones: (1) sha1 debería ser ascii, no utf8. (2) sha1 podría ser BINARY (20) si usa HEX () y UNHEX (). (3) para que el cambio de nombre sea atómico, sin tiempo de inactividad, haga RENAME TABLE mytable TO mytableold, mytablenew TO mytable. Luego, DROP TABLE mytableold después de que esté satisfecho. (4) Las probabilidades citadas son para una sola fila. (5) 2 64 está mal: son 2 160. (6) las probabilidades para una mesa son aproximadamente: "Hay una posibilidad en 2 53 de que una mesa con 2 53 filas tenga un dup sha1". (6a) Es más probable que pases por un asteroide mientras te acumulas en una mega lotería.
Rick James el
@RickJames todos los puntos anotados. Disculpe mis malas matemáticas para el punto # 5, es 2 ^ 160. Ajusté el # 3 en mi respuesta.
RolandoMySQLDBA
1
Chicos, las probabilidades que presentan suponen: 1. SHA tiene una distribución perfecta; y 2. la entrada es perfectamente aleatoria. SHA no tiene una distribución perfecta. Tampoco ningún otro algoritmo de hashing. La entrada no es perfectamente aleatoria, y aunque SHA, como otros resúmenes, causan grandes cambios en la salida por cualquier cambio menor en la entrada, es perfectamente posible que algunos conjuntos de entradas hagan la misma salida, y que esas entradas tengan alguna sistemática conexión entre ellos. Ahora, estoy balbuceando sobre todo aquí, ya que las probabilidades son muy bajas; pero aún así, uno debe ser cauteloso.
Shlomi Noach
Las claves de hash de @ShlomiNoach pueden ser trabajo pesado. A este ritmo, incluso la función CONTRASEÑA sería aceptable ( palominodb.com/blog/2011/12/04/hashing-algorithm-mysql-password )
RolandoMySQLDBA