W moim filtrze antyspamowym mam filtr bayesowskopodobny, który nauczył się już 4 milionów szeroko pojętych “słów”.

Ponieważ baza ma strukturę (char(32) primary key, int, int), trzymanie w niej indeksu osobno, jak to robi MyISAM, jest bez sensu — lwia część bazy jest dublowana. Natomiast InnoDB po prostu sortuje rzędy wg klucza głównego, więc w tym przypadku nie potrzebuje zapisać ani bitu więcej, niż same dane.

Postanowiłem przestawić bazę na InnoDB prostym

alter table engine=InnoDB

Ups. Zapytanie trwało 5 godzin mielenia dyskiem.

\ char() w MyISAM miał ważną przewagę nad varchar() — zachowywał stałą wielkość rzędu, co przyśpiesza wyszukwiania. Tylko taki mały problem — stała wielkość to maksymalna wielkość. Dla MySQLowego “UTF—7.5” to po 3 bajty na każdy znak. Każde 32—znakowe słowo zajmuje 97 bajtów.

W przypływie zdrowego rozsądku postanowiłem zapisywać słowa jako hashe MD5. Do wyszukiwania wystarczy, a każde, dowolnie długie słowo, będzie zajmowało 16 bajtów (16 binarnie, 32 dla ASCII-hex-cieniasów). Teoretycznie możliwymi powtórkami nie przejmuję się, bo jak znajdę kolizję na krótkich, czytelnych słowach, to będę sławny i bogaty.

Zmiana klucza głównego na tabeli InnoDB to nie jest dobry pomysł. Dodanie kolumny na hash trwało 20 minut. Potem zorientowałem się, że char(16) binary to zupełnie co innego, niż binary(16) i katowałem bazę przez następne 20 minut tylko po to, żeby odpuścić i (po 20 kolejnych minutach) zmienić strategię na skopiowanie danych do nowej tabeli.

insert into nowatabela select unhex(md5(slowo)), itd, itp from staratabela;

Nie wiem jak długo trwało to zapytanie, bo po 18 godzinach pracy non-stop zlitowałem się nad bazą i je ubiłem.

*) \<small\>no dobra, nie każdego, bo ma różne optymalizacje z buforami, stronami, itp. ale przy takiej ilości danych najwidoczniej to na niewiele się zdaje.\</small\>

Diabeł tkwi w szczególe: InnoDB musi mieć zawsze posortowane rzędy. Nowo wstawiane zahashowane MD5 są idealną odwrotnością posortowania, czyli przy wstawianiu każdego* rzędu baza musi przesunąć (statystycznie) połowę już wstawionych. Dla 4 milionów rzędów daje ok. 4 bilionów (amerykańskich trylionów — oni mają wszystko większe) operacji.

I jeszcze bardziej diabelski szczegół:

insert into nowatabela select unhex(md5(slowo)) as hash, itd, itp from staratabela order by hash;

18 minut.