drogie, piękne i szybkie

co to jest?

jest drogie. jest piękne. i jest szybkie.

nowe cudo aston martina. na razie faza “koncept", ale linie nadwozia wyglądają mocno znajomo, więc jest spora szansa, że ten samochód tak będzie wyglądał w seryjnej (ekhem) produkcji.

4 drzwi. przeszklony dach. i bagażnik na wino. coś ślicznego:


aston.martin.rapide.concept-01.jpg
aston.martin.rapide.concept-02.jpgaston.martin.rapide.concept-03.jpgaston.martin.rapide.concept-04.jpgaston.martin.rapide.concept-05.jpgaston.martin.rapide.concept-06.jpgaston.martin.rapide.concept-07.jpgaston.martin.rapide.concept-08.jpgaston.martin.rapide.concept-09.jpgaston.martin.rapide.concept-10.jpgaston.martin.rapide.concept-11.jpgaston.martin.rapide.concept-12.jpgaston.martin.rapide.concept-13.jpgaston.martin.rapide.concept-14.jpgaston.martin.rapide.concept-15.jpgaston.martin.rapide.concept-16.jpgaston.martin.rapide.concept-17.jpgaston.martin.rapide.concept-18.jpg

umiesz robić zdjęcia?

ja nie umiem. na szczęście. nie mam żadnych podstaw do tego by sądzić inaczej. ale gdyby mi kiedyś przyszło do głowy, że potrafię robić zdjęcia, to ta galeria fotografii makro by mi je rozwiała.

szybsze wifi w centrino. już!

intel potwierdził, że wprowadzi obsługę nowego standardu ieee 802.11n już do końca tego miesiąca!

jeśli nie wiecie co i jak – 802.11n jest nowym standardem który docelowo ma zastąpić używany teraz 802.11g. tak jak 802.11g ma prędkość maksymalną 54mbit, tak 80211n ma dawać pasmo do 540mbitów! i dodatkowo ma mieć większy zasięg – do 50 metrów w porównianiu do 30 metrów (w budynkach).

pierwsze lapy z nowymi centrino mają być wypuszczone przez acer'a, gateway'a i toshibę. ceny? zobaczymy. jak zawsze – nowości nie będą tanie.

amd kontratakuje. werbalnie.

intel dokopał amd na wszystkich frontach. w tym na tym co od dawna było niepodzielnie amd'owe – serwery 64bitowe, pod bazy danych. opteron padł u stóp woodcresta.

dodatkowo – intel już wypuścił procesory 4-rdzeniowe, a amd zwlekał.

i teraz przystąpił do kontrataku. niestety – tylko werbalnego – produktów nie zobaczymy.

amd mianowicie zapowiedział, że najnowsze 4-rdzeniowe opterony będą o 40% szybsze od najnowszych 4 rdzeniowych xeon'ów. tyle, że to nie jest produkt. to tylko zapowiedź. w dodatku zapowiedź informująca, że produkt ma być w połowie 2007. czyli – gruszki na wierzbie. po pierwsze – nadal nie ma produktu. po drugie – w połowie 2007 intel może wypuścić kolejną serię xeon'ów. po trzecie – wydaje mi się, że amd trochę za bardzo skupił sie na integracji z niedawno kupionym ati, i zaniedbał rynek serwerowy.

zasadniczo jestem fanem opteronów i amd. dlatego mam nadzieję, że faktycznie obietnice się spełnią, a potem pokażą coś jeszcze lepszego.

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 🙁