mysql rządzi? indeksy pokrywające

przeczytałem właśnie o pewnej funkcjonalności mysql'a o której wcześniej nie wiedziałem. w dodatku – której postgresql nie ma!
chodzi o indeksy pokrywające.
co to jest?
ogólna idea polega na tym, że silnik bazodanowy może wykorzystać do zwracania wartości wartości pobrane z indeksu a nie z tabeli.
kumacie coś z tego? pewnie nie. ja też nie. więc przykład.
mamy tabelkę:

# create table zakupy (id serial primary key, user_id int4, kwota int4);

piszę po postgresowemu, ale chodzi o pokazanie idei.
teraz.
często potrzebujemy zrobić zestawienie nt. łącznej sumy kwot zakupów użytkownika. czyli wynik zapytania:

select sum(kwota) from zakupy where user_id = <costam>

aby to przyspieszyć robimy indeks na pole user_id:

create index x on zakupy (user_id).

i jest lepiej.
system działa tak, że wyszukuje które rekordy w tabeli powinien wziąść pod uwagę (przy pomocy indeksu), potem je znajduje w tabeli, odczytuje, sumuje i zwraca.
proste.
ale wbrew pozorom mało wydajne.
w mysql'u jest coś takiego jak rzeczone indeksy pokrywające.
oznacza to, że jeśli zrobimy indeks:

create index x on zakupy (user_id, kwota).

to mysql użyje tego indeksu w dwóch celach:

  1. do znalezienia odpowiednich rekordów
  2. do pobrania kwot do zsumowania

na czym polega rewolucja? nie trzeba sięgać do tabeli by znaleźć dane!
szybkie. wydajne. zajebiste. tyle, że zżera trochę więcej miejsca na dysku. ale to jest tani zasób.
covering indices nie są domeną mysql'a. mają je też inne bazy. szybki searchmash pokazał, że na pewno są one obecne też w mssql'u (więc pewnie w sybase też). zgaduję, że oracle i db2 też je mają.
a czemu postgres nie? no cóż. temat był kilkukrotnie poruszany na liście pgsql-hackers, ale okazało się, że ze względu na mvcc sprawa jest mocno skomplikowana. i (na razie) nie ma. muszę przyznać, że jest to pierwsza rzecz jakiej (jako postgresowiec) zazdroszczę mysql'owi.

losowy rekord z bazy danych

czy stanęliście kiedyś przed problemem wylosowania rekordu z tabeli? dowolnego rekordu?
oczywistym pomysłem jest:

# SELECT * FROM tabelka ORDER BY random() LIMIT 1;

no ale to jest wolne. wymaga posortowania całej tabeli. co w najlepszym układzie ma złożoność "n log n".
przykładowo u mnie na testowej tabelce trwało to 90 sekund! (1.7 miliona rekordów).
no nie za dobrze.
niektórzy mogą sugerować takie rozwiązanie:

  1. znajdź maksymalne
  2. SELECT * FROM tabelka WHERE id <= random() * maksymalne_id limit 1;

na oko jest ok. tzn. akurat nie jest, bo random jest funkcją volatile, i trzeba by raczej … WHERE id <= (select random() * maksymalne_id) LIMIT 1, ale to już szczegół.
czemu to jest złe?
bo wprowadza pewien istotny problem. jeśli numeracja pola id w naszej tabelce zawiera dziury (czyli jest takie id, które jest większe od minimalnego i mniejsze od maksymalnego, dla którego nie ma rekordu) – to te losowane rekordy wcale nie będą dobrze losowane.
jako ekstremalny przykład (ale dobrze pokazujący rzeczywistość) podajmy tabelkę z dwoma rekordami, o id odpowiednio: 1 i 100. rekord z id = 1 będzie wypadał 99 razy częściej niż rekord z id = 100!.
cóż więc pozostaje? siąść i płakać?
nie.
można użyć inteligencji. czyli funkcji/procedury.
przykładowo taka funkcja:

CREATE OR REPLACE FUNCTION random_record() RETURNS tabelka AS $BODY$
DECLARE
    id_min INT8;
    id_max INT8;
    range INT8;
    temp_id INT8;
    temprec RECORD;
BEGIN
    SELECT min(id) INTO id_min FROM tabelka;
    SELECT max(id) INTO id_max FROM tabelka;
    range:= 1 + ( id_max - id_min );
    LOOP
        temp_id := id_min + (random() * range::float8)::INT8;
        SELECT * INTO temprec FROM tabelka WHERE id = temp_id;
        IF found THEN
            RETURN temprec;
        END IF;
    END LOOP;
END;
$BODY$ language 'plpgsql';

co ona robi?zwraca losowy rekord. całkowicie losowy – każdy rekord ma te same szanse bycia wylosowanym.
warunki brzegowe? pole id musi być unikatowe (szokujące, nie?). no i: im więcej dziur w numeracji tym wolniej działa. ale co znaczy wolniej?
ta moja testowa tabelka ma takie dane:

# select min(id), max(id), count(*) from tabelka;
 min |   max    |  count
-----+----------+---------
   3 | 36574227 | 1721217
(1 row)

czyli jak widać – dziur jest sporo. w szczególności – dziur jest 21 razy więcej niż istniejących rekordów!
przypomnę, że

select * from tabelka order by random() limit 1;

działało na tej tabelce w około 90 sekund.
ile czasu zajmuje to funkcji?
6 kolejnych wywołań. czasy odpowiednio: 124.700, 141.442, 201.708, 94.413, 145.128, 110.076. milisekund!
jak widać – jest szybko.
problemem tej funkcji jest to, że teoretycznie może się zdarzyć, że nigdy się nie skończy. ale w/g mnie jest to gdybanie. zresztą – zawsze można dorobić warunek, że jeśli np. wykonano już 1000 strzałów niecelnych, to zwróćmy pierwszy rekord z brzegu.
i już.
czy można to jakoś dopalić?
tak.
jeśli wiecie, że tabelka w której szukacie ma dużo dziur, to dodajcie do niej jedno pole:

create sequence random_thing_seq;
alter table tabelka add column random_thing int8;
alter table tabelka alter column random_thing set default nextval('random_thing_seq');
update tabelka set random_thing = nextval('random_thing_seq') where random_thing is null;
alter table tabelka alter column random_thing set not null;
create unique index ui_random_thing on tabelka (random_thing);

i potem używajcie w funkcji random_thing a nie id.
cel ćwiczenia?
jak sie pojawi za dużo dziur w numeracji (random_thing też będzie miał dziury) to zawsze możecie:

update tabelka set random_thing = nextval('random_thing_seq');

i już dziur nie ma,
a ponieważ random_thing nie jest do niczego innego używane – jest to w pełni bezpieczne.
oczywiście po takim update'cie dobrze jest zrobić vacuum'a. a najlepiej vacuum full'a.

mikro-dezynfekcja

jak każdy kto ma kuchnię, mam też zlew. przy nim różne gąbki, skrobaczki, myjki czy szczotki.
przeczytałem ostatnio, że w tych gąbkach zbiera się olbrzymia ilość bakterii – ciepło, wilgotno lub mokro non stop. żyć nie umierać.
ludzie którzy to napisali, napisali też, że jest prosta metoda na to by gąbkę pozbawić niechcianego towarzystwa. namoczyć i wstawić do mikrofali na 2 minuty na maksymalną moc.
nawet logiczne.
informacja jest fajna, podchwyciło ją wiele gazet i site'ów. podali.
no i tu zaczyna się haczyk.
część ludzi przeczytała, po czym zastosowała. zapominając o namoczeniu gąbki.
efekt? spalona gąbka, wszędzie pełno dymu, dom śmierdzi jak po paleniu opon. coś ślicznego. tak to po raz kolejny ludzkość udowodniła, że czytanie ze zrozumieniem to umierająca sztuka.
eh. a ja nie mam mikrofalówki 🙁

przełom w bateriach czy kolejna ściema?

eestor – firma o której nikt nigdy nie słyszał, wystąpiła ostatnio o patent na nowy typ baterii opartych o proszek barowo-tytanowy.
zgodnie z ich zapowiedziami do końca roku wejdą na rynek z produktem. a co produkt ma mieć?

  • pojemność (przy porównaniu masy) 10 krotnie większą niż baterie ołowiano-kwasowe przy połowie ceny
  • brak substancji toksycznych

tu pojawia sie wielkie "łał" i szacun od czytelników.
gdyby nie jedno ale.
wypowiedziało się parę osób ze sporą wiedzą i twierdzą, że zrobienie czegoś takiego jest fizycznie niemożliwe. że baterie (a dokładniej ultra-kondensatory, cokolwiek by to znaczyło) oparte o te technologie istnieją już od dawna, i są używane, ale istnieje jakaś bariera fizyczna której nie da sie pokonać i która uniemożliwia osiągnięcie tego o czym mówi eestor.
z drugiej strony – od zawsze wiadomo, że gdy 1000 ludzi wie, że coś jest niemożliwe, to przyjdzie 1001-a i to zrobi bo nie będzie wiedziała, że tego sie nie da.
co z tego wyjdzie? zobaczymy za jakiś czas.
pozostaje mieć nadzieję, że nie są to puste obietnice, i że słowa richarda weira (współtwórcy i szefa eestor) przełożą się na produkt.
przy tak dużym skoku jakościowo-cenowym możliwości są olbrzymie. zwłaszcza, że podobno produkt ma być stosowalny na każdą skalę: zegarki, laptopy, samochody.

sun na xeonach

sun oficjalnie ogłosił, że zacznie oferować serwery zbudowane w oparciu o procesory intela. jest to dosyć duże nowum. jeszcze nie dawno wszystko było oparte tylko o platformę sparc. potem doszły opterony z amd. teraz – sun musiał dojrzeć do tego, że nie sprzedawanie serwerów z xeonami obcina im potężną część zysków – we wszystkich nowych testach xeony biją na głowę opterony, a amd cały czas zwleka z kontratakiem.
tak czy inaczej – co z tego wyjdzie? większy wybór. i większa konkurencja. co powinno nam wszystkim wyjść na zdrowie 🙂