2012-11-27 23 views
14

Ho due tabelle nel mio db che hanno milioni di righe ora, la selezione e l'inserimento sta diventando sempre più lento.Mysql 5.5 Tabella partizione utenti e amici

Sto usando la primavera + hibernate MySQL + 5.5 e leggere le sharding così come il partizionamento del tavolo e come l'idea di partizionamento mie tabelle,

mio attuale struttura Db è come

CREATE TABLE `user` (
    `id` BIGINT(20) NOT NULL, 
    `name` VARCHAR(255) DEFAULT NULL, 
    `email` VARCHAR(255) DEFAULT NULL, 
    `location_id` bigint(20) default NULL, 
    `updated_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    PRIMARY KEY (`id`), 
    KEY `FK3DC99772C476E06B` (`location_id`), 
    CONSTRAINT `FK3DC99772C476E06B` FOREIGN KEY (`location_id`) REFERENCES `places` (`id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8 


CREATE TABLE `friends` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT, 
    `user_id` BIGINT(20) DEFAULT NULL, 
    `friend_id` BIGINT(20) DEFAULT NULL, 
    `updated_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `unique_friend` (`user_id`,`friend_id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8 

Ora sto testando come utilizzare meglio il partizionamento, per la tabella degli utenti che segue penso sia buona per l'utilizzo.

CREATE TABLE `user_partition` (
    `id` BIGINT(20) NOT NULL, 
    `name` VARCHAR(255) DEFAULT NULL, 
    `email` VARCHAR(255) DEFAULT NULL, 
    `location_id` bigint(20) default NULL, 
    `updated_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    PRIMARY KEY (`id`), 
    KEY `FK3DC99772C476E06B` (`location_id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8 
PARTITION BY HASH(id DIV 100000) 
PARTITIONS 30; 

ho creato una procedure per caricare i dati in due tavolo e verificare le prestazioni delle due tabelle

DELIMITER // 
CREATE PROCEDURE load_partition_table() 
BEGIN 
DECLARE v INT DEFAULT 0; 
    WHILE v < 1000000 
    DO 
    INSERT INTO user_partition (id,NAME,email) 
    VALUES (v,CONCAT(v,' name'),CONCAT(v,'@yahoo.com')), 
    (v+1,CONCAT(v+1,' name'),CONCAT(v+1,'@yahoo.com')), 
    (v+2,CONCAT(v+2,' name'),CONCAT(v+2,'@yahoo.com')), 
    (v+3,CONCAT(v+3,' name'),CONCAT(v+3,'@yahoo.com')), 
    (v+4,CONCAT(v+4,' name'),CONCAT(v+4,'@yahoo.com')), 
    (v+5,CONCAT(v+5,' name'),CONCAT(v+5,'@yahoo.com')), 
    (v+6,CONCAT(v+6,' name'),CONCAT(v+6,'@yahoo.com')), 
    (v+7,CONCAT(v+7,' name'),CONCAT(v+7,'@yahoo.com')), 
    (v+8,CONCAT(v+8,' name'),CONCAT(v+8,'@yahoo.com')), 
    (v+9,CONCAT(v+9,' name'),CONCAT(v+9,'@yahoo.com')) 
    ; 
    SET v = v + 10; 
    END WHILE; 
    END 
    // 

CREATE PROCEDURE load_table() 
BEGIN 
DECLARE v INT DEFAULT 0; 
    WHILE v < 1000000 
    DO 
    INSERT INTO user (id,NAME,email) 
    VALUES (v,CONCAT(v,' name'),CONCAT(v,'@yahoo.com')), 
    (v+1,CONCAT(v+1,' name'),CONCAT(v+1,'@yahoo.com')), 
    (v+2,CONCAT(v+2,' name'),CONCAT(v+2,'@yahoo.com')), 
    (v+3,CONCAT(v+3,' name'),CONCAT(v+3,'@yahoo.com')), 
    (v+4,CONCAT(v+4,' name'),CONCAT(v+4,'@yahoo.com')), 
    (v+5,CONCAT(v+5,' name'),CONCAT(v+5,'@yahoo.com')), 
    (v+6,CONCAT(v+6,' name'),CONCAT(v+6,'@yahoo.com')), 
    (v+7,CONCAT(v+7,' name'),CONCAT(v+7,'@yahoo.com')), 
    (v+8,CONCAT(v+8,' name'),CONCAT(v+8,'@yahoo.com')), 
    (v+9,CONCAT(v+9,' name'),CONCAT(v+9,'@yahoo.com')) 
    ; 
    SET v = v + 10; 
    END WHILE; 
    END 
    // 

I risultati sono stati Sorprendente, inserire/selezionare nella tabella delle partizioni non dare risultati migliori.

mysql> select count(*) from user_partition; 
+----------+ 
| count(*) | 
+----------+ 
| 1000000 | 
+----------+ 
1 row in set (0.40 sec) 

mysql> select count(*) from user; 
+----------+ 
| count(*) | 
+----------+ 
| 1000000 | 
+----------+ 
1 row in set (0.00 sec) 


mysql> call load_table(); 
Query OK, 10 rows affected (20.31 sec) 

mysql> call load_partition_table(); 
Query OK, 10 rows affected (21.22 sec) 

mysql> select * from user where id = 999999; 
+--------+-------------+------------------+---------------------+ 
| id  | name  | email   | updated_time  | 
+--------+-------------+------------------+---------------------+ 
| 999999 | 999999 name | [email protected] | 2012-11-27 08:06:54 | 
+--------+-------------+------------------+---------------------+ 
1 row in set (0.00 sec) 

mysql> select * from user_no_part where id = 999999; 
+--------+-------------+------------------+---------------------+ 
| id  | name  | email   | updated_time  | 
+--------+-------------+------------------+---------------------+ 
| 999999 | 999999 name | [email protected] | 2012-11-27 08:03:14 | 
+--------+-------------+------------------+---------------------+ 
1 row in set (0.00 sec) 

Così due domanda

1) Che cosa è il modo migliore per partizionare user tavolo in modo che inseriscono e scelgono anche diventare veloce e la rimozione di chiave esterna sulla location_id è corretta? So che la partizione può essere buona solo se si accede alla base della chiave di partizione, nel mio caso voglio leggere la tabella solo da id. perché gli inserti sono più lenti nella tabella delle partizioni?

2) Quale è il modo migliore per partizionare la tabella come voglio partizionare gli amici sulle basi di user_id come si desidera posizionare tutti gli amici degli utenti nella stessa partizione e accedervi sempre utilizzando un id_utente. Devo rilasciare la chiave primaria su friend.id o aggiungere user_id nella chiave primaria?

+0

Il post non mostra alcuna differenza di prestazioni apprezzabile; una sola differenza di inserimento di un milione di record è piuttosto insignificante (~ 0,5%). –

+0

AFAIK per accelerare SELECT, [indicizzazione] (http://dev.mysql.com/doc/refman/5.5/en/mysql-indexes.html) è la soluzione corretta. C'è [qualche consiglio su INSERT speed-up] (http://dev.mysql.com/doc/refman/5.5/en/insert-speed.html), anche la linea principale è quella di fare inserimenti di massa. –

risposta

4

Prima di tutto, ti consiglio se possibile di eseguire l'aggiornamento a 5.6.5 o successivo di Mysql per assicurarti di sfruttare il partizionamento in modo corretto e con le migliori prestazioni. Ciò non è sempre possibile a causa delle preoccupazioni di GA, ma la mia esperienza è che c'era una differenza nelle prestazioni tra 5.5 e 5.6, e 5.6 offre alcuni altri tipi di partizionamento.

1) La mia esperienza è che gli inserimenti e gli aggiornamenti sono più veloci su insiemi partizionati e selezionano QUANTO TEMPO SEI COMPRESO LA COLONNA CHE SEI DISPONENDO NELLA QUERY. Se chiedo il conteggio di tutti i record su tutte le partizioni, vedo risposte più lente. Questo è normale perché le partizioni funzionano come tabelle separate, quindi se hai 30 partizioni è come leggere 30 tabelle e non solo una.

È necessario includere il valore su cui si esegue il partizionamento nella chiave primaria E deve rimanere stabile durante la vita del record.

2) Includerei user_id e id nella chiave primaria, supponendo che le tue tabelle amici id_utente e id non cambino affatto una volta che il record è stato stabilito (cioè qualsiasi modifica sarebbe una cancellazione/inserimento). Nel mio caso era "ridondante" ma più che degno l'accesso. Sia che tu scelga user_id/id o id/user_id dipende dall'accesso più frequente.

Una nota finale. Ho provato a creare LOTS di partizioni quando ho iniziato a rompere i miei dati in partizioni, e ho scoperto che solo alcune sembravano andare a buon fine - le partizioni 6-12 sembravano funzionare al meglio per me. YMMV.

+0

Grazie ma poi perché inserisco nella tabella delle partizioni più tempo nel mio caso di test sto facendo qualcosa di sbagliato. Di solito seleziono gli amici in base ad alcuni user_id e non ho mai usato un campo ID – mtariq

+0

Dovrei vedere come appare il tuo inserto, ma concordo con te che dovrebbe impiegare meno tempo, non più tempo. Suppongo che tu abbia provato queste ultime versioni di MySQL? – TJChambers

+0

mysql versione 5.5 Ho usato – mtariq

1

1.Utilizzare questa query SQL per selezionare tavolo e salvo tutte le colonne, ad eccezione di id:

rispondo quello che vi serve:

suggerisco di rimuovere FOREIGN KEY e PRIMARY KEY

So che questo è pazzo, ma puoi chiedere al computer di sapere quale sia l'ID corrente, l'ultimo id, il prossimo id e questo richiederà molto tempo rispetto alla creazione manuale di id. in altro modo è possibile creare l'ID int manualmente da java.

utilizzare questa query SQL per inserire velocemente:

INSERT INTO user (id,NAME,email) 
VALUES ('CREATE ID WITH JAVA', 'NAME', '[email protected]') 

Non riesco a decidere la mia domanda può lavorare più velocemente o no ...

Perché tutto dipende le prestazioni del computer, assicurarsi di utilizzare sul server, perché il server può completare tutte le attività velocemente.

e per selezionare, nella pagina in cui sono contenute le informazioni sul profilo, sarà necessaria una riga per un utente definito nell'ID profilo. limite mysql

uso se avete solo bisogno di uno e se avete bisogno di più di una ... basta cambiare i valori limite come questo per una riga:

select * from user where id = 999999 limit 1; 

e per sette consecutive:

select * from user where id = 999999 limit 7; 

penso che questa query funzionerà più velocemente di quanto non limit e ricordare limite può lavorare con insert troppo

012.351.

2. amico partizione: la risposta è cadere la chiave

tabella primaria senza chiave primaria non è un problema

Ancora una volta, creare l'ID con java ... Java progettato per essere più veloce nell'interfaccia e il tuo codice include while e java può farlo. Per esempio è necessario recuperare i vostri tutti i dati amico ... utilizzare questa query per eseguire più velocemente:

select fr.friend_id, usr.* from friends as fr INNER JOIN user as usr 
ON dr.friend_id = usr.id 
where fr.user_id = 999999 LIMIT 10; 

e credo che questo sia sufficiente dispiace posso solo spiegare su MySQL e non in java. Perché, non sono esperto di java ma ne sono a conoscenza

+0

in modo provocatorio ma non ha risposto completamente, puoi aggiornare la risposta per 1 e 2. – mtariq

+0

Prima quale linguaggio di programmazione hai usato? –

+0

java ma è possibile utilizzare qualsiasi lingua in quanto è una domanda mysql – mtariq

0

1) Se si utilizza sempre (o principalmente) solo id per selezionare i dati, è ovvio utilizzare questo campo come base per la condizione di partizionamento. Poiché è un numero, non è necessario utilizzare la funzione hash, utilizzare semplicemente range partitioning. Quante partizioni creare (quali numeri scegliere come bordi) che devi trovare da te ma come @TJChambers menzionato prima intorno all'8-10 dovrebbe essere abbastanza efficiente.

L'inserimento è più lento perché si verifica un errore. Basta inserire 1000000 righe una dopo l'altra senza alcuna casualità e l'unica differenza è che per la tabella partizionata mysql è necessario calcolare l'hash che è un tempo extra. Ma come nel tuo caso l'ID è una condizione di base per il partizionamento Non otterrai mai nulla con l'inserimento, poiché tutte le nuove righe si trovano alla fine del tavolo.

Se si dispone ad esempio di una tabella con localizzazioni GPS e partizionata da lat e lon Si potrebbe vedere la differenza nell'inserimento se ad esempio ogni partizione fosse continente diverso. E la differenza si vedrebbe se avessi una tabella con alcuni dati (reali) casuali e stavi inserendo alcuni valori casuali non lineari.

La selezione per la tabella partizionata è più lenta perché di nuovo si verifica un errore.

@TJChambers ha scritto prima di me su di esso, la query deve funzionare su tutte le partizioni (è come lavorare con molte tabelle) in modo da prolungare il tempo. Prova a usare dove lavorare con i dati di una sola partizione per vedere una differenza.

ad esempio la routine:

select count(*) from user_partition where id<99999; 

e

select count(*) from user where id<99999; 

Vedrete una differenza.

2) Questo è difficile. Non c'è modo di partizionarlo senza ridondanza dei dati (almeno nessuna idea mi viene in mente) ma se il tempo di accesso (selezionare la velocità) è il più importante il modo migliore potrebbe essere quello di partizionarlo allo stesso modo della tabella utente (intervallo su uno degli id) e inserire 2 righe per ogni relazione è (a, b) e (b, a). Raddoppierà il numero di righe ma se esegui il partizionamento su più di 4 parti lavorerai comunque su meno record per query e avrai solo una condizione per verificare che non sia necessario o.

ho provato con con questo schema

CREATE TABLE `test`.`friends` (
`a` INT NOT NULL , 
`b` INT NOT NULL , 
INDEX (`a`), 
INDEX (`b`) 
) ENGINE = InnoDB; 

CREATE TABLE `test`.`friends_part` (
`a` INT NOT NULL , 
`b` INT NOT NULL , 
INDEX (`a` , `b`) 
) ENGINE = InnoDB 
PARTITION BY RANGE (a) (
    PARTITION p0 VALUES LESS THAN (1000), 
    PARTITION p1 VALUES LESS THAN (2000), 
    PARTITION p2 VALUES LESS THAN (3000), 
    PARTITION p3 VALUES LESS THAN (4000), 
    PARTITION p4 VALUES LESS THAN (5000), 
    PARTITION p5 VALUES LESS THAN (6000), 
    PARTITION p6 VALUES LESS THAN (7000), 
    PARTITION p7 VALUES LESS THAN (8000), 
    PARTITION p8 VALUES LESS THAN (9000), 
    PARTITION p9 VALUES LESS THAN MAXVALUE 
); 

delimiter // 
DROP procedure IF EXISTS fill_friends// 
create procedure fill_friends() 
begin 
    declare i int default 0; 
    declare a int; 
    declare b int; 
    while i<2000000 
    do 
    set a = rand()*10000; 
    set b = rand()*10000; 
    insert into friends values(a,b); 
    set i = i + 1; 
    end while; 
end 
// 
delimiter ; 

delimiter // 
DROP procedure IF EXISTS fill_friends_part// 
create procedure fill_friends_part() 
begin 
    insert into friends_part (select a,b from friends); 
    insert into friends_part (select b as a, a as b from friends); 
end 
// 
delimiter ; 

query Ho corso sono: insieme

select * from friends where a=317 or b=317; 

risultato: 475 volte: 1.43, 0.02, 0,01

select * from friends_part where a=317; 

set di risultati: 475 volte: 0,10, 0,00, 0,00

set
select * from friends where a=4887 or b=4887; 

risultato: 483 volte: 1.33, 0.01, 0,01

select * from friends_part where a=4887; 

set di risultati: 483 orari: 0.06, 0.01, 0.00

non mi sono preoccupato circa l'unicità dei dati ma nel tuo esempio puoi usare un indice univoco. Inoltre ho usato il motore InnoDB, ma MyISAM è migliore se la maggior parte delle query sono selezionate e non si stanno andando a fare molte scritture. Non c'è una grande differenza per la 2a e la 3a corsa probabilmente a causa della memorizzazione nella cache, ma c'è una differenza visibile per la prima corsa. È più veloce perché stiamo infrangendo una delle prime regole di progettazione del database, ma il fine giustifica i mezzi per cui può essere una buona soluzione per tavoli davvero grandi. Se hai meno di 1 milione di record, penso che tu possa sopravvivere senza partizionare.