Powinieneś przeczytać poniższe i dowiedzieć się trochę o zaletach dobrze zaprojektowanej tabeli innodb oraz o tym, jak najlepiej używać indeksów klastrowych - dostępne tylko w innodb!
http://dev.mysql.com/doc /refman/5.0/en/innodb-index-types.html
http://www. xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/
następnie zaprojektuj swój system zgodnie z następującym uproszczonym przykładem:
Przykładowy schemat (uproszczony)
Ważną cechą jest to, że tabele korzystają z mechanizmu innodb, a klucz podstawowy dla tabeli wątków nie jest już pojedynczym kluczem auto_inkrementacji, ale złożonym klastrowym klucz oparty na kombinacji forum_id i thread_id. np.
threads - primary key (forum_id, thread_id)
forum_id thread_id
======== =========
1 1
1 2
1 3
1 ...
1 2058300
2 1
2 2
2 3
2 ...
2 2352141
...
Każdy wiersz forum zawiera licznik o nazwie next_thread_id (unsigned int), który jest utrzymywany przez wyzwalacz i zwiększa się za każdym razem, gdy wątek zostanie dodany do danego forum. Oznacza to również, że możemy przechowywać 4 miliardy wątków na forum, a nie łącznie 4 miliardy wątków, jeśli użyjemy pojedynczego klucza podstawowego auto_increment dla thread_id.
forum_id title next_thread_id
======== ===== ==============
1 forum 1 2058300
2 forum 2 2352141
3 forum 3 2482805
4 forum 4 3740957
...
64 forum 64 3243097
65 forum 65 15000000 -- ooh a big one
66 forum 66 5038900
67 forum 67 4449764
...
247 forum 247 0 -- still loading data for half the forums !
248 forum 248 0
249 forum 249 0
250 forum 250 0
Wadą korzystania z klucza złożonego jest to, że nie można już wybrać wątku za pomocą pojedynczej wartości klucza w następujący sposób:
select * from threads where thread_id = y;
musisz zrobić:
select * from threads where forum_id = x and thread_id = y;
Jednak kod aplikacji powinien być świadomy tego, które forum przegląda użytkownik, więc nie jest to trudne do zaimplementowania - zapisz aktualnie przeglądany forum_id w zmiennej sesji lub ukrytym polu formularza itp...
Oto uproszczony schemat:
drop table if exists forums;
create table forums
(
forum_id smallint unsigned not null auto_increment primary key,
title varchar(255) unique not null,
next_thread_id int unsigned not null default 0 -- count of threads in each forum
)engine=innodb;
drop table if exists threads;
create table threads
(
forum_id smallint unsigned not null,
thread_id int unsigned not null default 0,
reply_count int unsigned not null default 0,
hash char(32) not null,
created_date datetime not null,
primary key (forum_id, thread_id, reply_count) -- composite clustered index
)engine=innodb;
delimiter #
create trigger threads_before_ins_trig before insert on threads
for each row
begin
declare v_id int unsigned default 0;
select next_thread_id + 1 into v_id from forums where forum_id = new.forum_id;
set new.thread_id = v_id;
update forums set next_thread_id = v_id where forum_id = new.forum_id;
end#
delimiter ;
Być może zauważyłeś, że dołączyłem liczbę odpowiedzi jako część klucza podstawowego, co jest nieco dziwne, ponieważ kompozyt (forum_id, thread_id) jest unikalny sam w sobie. Jest to po prostu optymalizacja indeksu, która oszczędza część operacji we/wy podczas wykonywania zapytań używających liczby_odpowiedzi. Aby uzyskać więcej informacji na ten temat, zapoznaj się z 2 linkami powyżej.
Przykładowe zapytania
Wciąż ładuję dane do moich przykładowych tabel i do tej pory mam załadowane ok. 500 milionów wierszy (o połowę mniej niż Twój system). Po zakończeniu procesu ładowania powinienem spodziewać się około:
250 forums * 5 million threads = 1250 000 000 (1.2 billion rows)
Celowo stworzyłem niektóre fora zawierające ponad 5 milionów wątków, na przykład forum 65 ma 15 milionów wątków:
forum_id title next_thread_id
======== ===== ==============
65 forum 65 15000000 -- ooh a big one
Czasy wykonywania zapytań
select sum(next_thread_id) from forums;
sum(next_thread_id)
===================
539,155,433 (500 million threads so far and still growing...)
pod innodb sumowanie next_thread_ids w celu uzyskania całkowitej liczby wątków jest znacznie szybsze niż zwykle:
select count(*) from threads;
Ile wątków ma forum 65:
select next_thread_id from forums where forum_id = 65
next_thread_id
==============
15,000,000 (15 million)
znowu jest to szybsze niż zwykle:
select count(*) from threads where forum_id = 65
Ok, teraz wiemy, że mamy do tej pory około 500 milionów wątków, a forum 65 ma 15 milionów wątków - zobaczmy, jak działa schemat :)
select forum_id, thread_id from threads where forum_id = 65 and reply_count > 64 order by thread_id desc limit 32;
runtime = 0.022 secs
select forum_id, thread_id from threads where forum_id = 65 and reply_count > 1 order by thread_id desc limit 10000, 100;
runtime = 0.027 secs
Wygląda na całkiem wydajną - więc jest to pojedyncza tabela z ponad 500 milionami wierszy (i rosnącymi) z zapytaniem, które obejmuje 15 milionów wierszy w 0,02 sekundy (podczas obciążenia!)
Dalsze optymalizacje
Należą do nich:
-
partycjonowanie według zakresu
-
fragmentowanie
-
rzucając w to pieniądze i sprzęt
itp...
mam nadzieję, że ta odpowiedź okaże się pomocna :)