2010-03-21 7 views
6

Soy bastante nuevo para trabajar con bases de datos relacionales, pero he leído algunos libros y conozco los conceptos básicos del buen diseño.mySQL Efficiency Issue - ¿Cómo encontrar el equilibrio correcto de normalización ...?

Estoy ante una decisión de diseño, y no estoy seguro de cómo continuar. Aquí hay una versión muy simplificada de lo que estoy construyendo: las personas pueden calificar las fotos de 1 a 5, y necesito mostrar los votos promedio en la imagen mientras hago un seguimiento de los votos individuales. Por ejemplo, 12 personas votaron 1, 7 personas votaron 2, etc, etc

El monstruo de la normalización de mí inicialmente diseñada la estructura de tabla como esta:

Table pictures 
id* | picture | userID | 

Table ratings 
id* | pictureID | userID | rating 

Con todas las restricciones de clave externa y todo lo establecido como deberían ser. Cada vez que alguien califica una imagen, simplemente inserto un nuevo registro en las calificaciones y termino con eso.

Para encontrar la calificación promedio de una imagen, que acababa de ejecutar algo como esto:

SELECT AVG(rating) FROM ratings WHERE pictureID = '5' GROUP by pictureID 

Tenerlo configuración de esta manera me permite ejecutar mis estadísticas de fantasía para. Puedo encontrar fácilmente quién calificó una cierta imagen en 3, y qué no.

Ahora estoy pensando si hay un montón de valoraciones (que es muy posible en lo que estoy realmente diseñando), encontrar el promedio será muy caro y doloroso.

El uso de una versión no normalizada parece ser más eficiente. e.g .:

Table picture 
id | picture | userID | ratingOne | ratingTwo | ratingThree | ratingFour | ratingFive 

Para calcular el promedio, solo tendría que seleccionar una sola fila. Parece mucho más eficiente, pero mucho más feo.

¿Puede alguien señalarme en la dirección correcta de qué hacer? Mi investigación inicial muestra que tengo que "encontrar el equilibrio correcto", pero ¿cómo hago para encontrar ese equilibrio? También se agradecerá cualquier artículo o información adicional de lectura.

Gracias.

+0

¿Ha topado con problemas de rendimiento o simplemente pedir? – Pentium10

+0

Aún no me he encontrado con problemas de rendimiento. Simplemente no quiero diseñar algo que se pandee bajo una carga potencialmente alta. – Foo

Respuesta

4

Su enfoque normalizado tiene mucho sentido, el no desnormalizado no.


En mi experiencia (Gestión del Rendimiento Telco, cientos de miles de puntos de datos por cada 1/4 hora) que haría lo siguiente:

Table: pictures 
id* | picture | userID | avg_rating | rating_count 

Table: ratings 
id* | pictureID | userID | rating 

Para la empresa de telecomunicaciones se volverá a calcular la calificación de fotos una vez al día, debe hacerlo periódicamente (p. ej.por hora) o cada vez que inserte (vuelva a calcular para la imagen clasificada, no toda la tabla). Esto depende de la cantidad de calificaciones que obtenga.


En la empresa de telecomunicaciones también mantenemos la calificación actualizados en lo que es su tabla de 'imágenes' y una marca de tiempo 1/4h en la tabla de clasificaciones, pero no creo que necesita ese nivel de detalle.


El 'desnormalización' es mover una (y avg (Cuenta de clasificación (rating))) hecho de calculateable a la mesa de imágenes. Esto ahorra ciclos de CPU, pero cuesta más almacenamiento.

+0

+1, habría recomendado lo mismo ... –

1

¿Qué puntuación les daría esta calificación de calificaciónCinco campos? ¿La cantidad de votos recibidos? Entonces no sabrás quién emitió el voto. Si realmente necesitas desnormalizar, solo agregaría un campo de "calificación promedio" a la tabla de imágenes, y actualizaría cada vez que se emitiera un voto (y mantendría la tabla de calificaciones tal como está).

Más en general, no quedar atrapado en la optimización prematura. Intente escribir un script de prueba que cree 100.000 imágenes y 1 millón de clasificaciones (o la cifra que desee admitir), y vea cuánto demora su consulta de AVG. Es probable que todavía sea bastante rápido. Asegúrese de que su tabla "ratings" tenga un índice en pictureID, por lo que no es necesario que el DB recorra las millones de filas.

+0

Gracias. Lo tendré en mente. Me centraré en escribir casos de prueba y ver cómo funciona la próxima vez. – Foo

1

En RDBMS mundo, desnormalización significa "Quiero aumentar la eficiencia de la consulta a costa de un mayor mantenimiento al tiempo que conserva modelo de corrección"

En su caso, la eficiencia se incrementará ligeramente de hecho (ya que todos las calificaciones siempre se recuperan de la misma página de datos).

Pero, ¿qué pasa con la corrección del modelo?

Con este diseño, usted, primero, no sabe quién hizo los votos (esta información ya no está almacenada) y, en segundo lugar, no puede calificar la imagen más de cinco veces.

Como el modelo inicial no tenía ninguna de estas restricciones, creo que este tipo de desnormalización no es lo que realmente desea.

1

Una buena manera de disfrutar de ambos mundos es utilizando Mysql Trigger. http://dev.mysql.com/doc/refman/5.0/en/triggers.html

Ahora añadir un disparador que cuando cada vez una tasa de usuario una imagen se actualizará el avg_rating en las fotos tablas. (utilizando el mismo seleccionar que haya indicado)

Ahora cuando selecciona, puede seleccionar en una sola tabla. Y siempre está actualizado. Y si desea obtener la información exacta de quién califica qué foto, puede seleccionarla de la tabla de clasificación también.

2

Así es como me gustaría abordar el problema http://pastie.org/879604

drop table if exists picture; 
create table picture 
( 
picture_id int unsigned not null auto_increment primary key, 
user_id int unsigned not null, -- owner of the picture, the user who uploaded it 
tot_votes int unsigned not null default 0, -- total number of votes 
tot_rating int unsigned not null default 0, -- accumulative ratings 
avg_rating decimal(5,2) not null default 0, -- tot_rating/tot_votes 
key picture_user_idx(user_id) 
)engine=innodb; 

insert into picture (user_id) values 
(1),(2),(3),(4),(5),(6),(7),(1),(1),(2),(3),(6),(7),(7),(5); 


drop table if exists picture_vote; 
create table picture_vote 
( 
picture_id int unsigned not null, 
user_id int unsigned not null,-- voter 
rating tinyint unsigned not null default 0, -- rating 0 to 5 
primary key (picture_id, user_id) 
)engine=innodb; 

delimiter # 

create trigger picture_vote_before_ins_trig before insert on picture_vote 
for each row 
begin 
declare total_rating int unsigned default 0; 
declare total_votes int unsigned default 0; 

select tot_rating + new.rating, tot_votes + 1 into total_rating, total_votes 
    from picture where picture_id = new.picture_id; 

-- counts/stats 
update picture set 
    tot_votes = total_votes, tot_rating = total_rating, 
    avg_rating = total_rating/total_votes 
where picture_id = new.picture_id; 

end# 
delimiter ; 

esperanza de que esto ayude :)