Есенен семинар на БГПО ’09

 Общи  Коментарите са изключени за Есенен семинар на БГПО ’09
Авг. 102009
 

Лятото е в разгара си, и ако искам то да свърши, то е само за да дойде по-скоро есенния семинар на БГПО. А той е толкова далече…

Преглед на имената от супер-ранно-предварителния списък тук показва стабилно присъствие на хард-лайнери (за какъвто се имам и аз). Но този път едно име направо ме втрещи: Anjo Kolk, Owner, Miracle Benelux, Oracle – Google integration.

Anjo Kolk! Човекът, който измисли YAPP и показа на света как трявба да се гони performance в Oracle! Неговият YAPP метод (Yet Another Performance Profiling Method) е една от основните разлики межу Oracle и другите СУБД.

Anjo е работил 16 години в Oracle, при това доста време в Oracle Server Performance Group. Между другото, в Oracle той е работил и по интеграцията на БД с големи ISV като Amdocs (примерно) и е ходил при много телеком клиенти като NTT Docomo (примерно) 🙂 . След като напуска Oracle, Anjo работи в Precise Software, Veritas Software и Symanctec, а напоследък си е сам шеф в Miracle Benelux, където, предполагам, другарува с Mogens Nørgaard. Покрай другото Anjo (заедно Mogens Nørgaard и други добри хора) е и един от основателите на The Oak Table Network.

 Posted by at 16:59
Авг. 102009
 

Задачката е следната:

Близо до офиса има една кръчма (още се спори дали се казва „Дупката“ или „Гнусното“). В нея влизат и излизат много хора. Понеже е доста голяма, има си регистър на влизащите и излизащите. В основната таблица (покрай другото) има и колонки time_in и time_out, които показват даден човек кога е влязъл и кога е излязъл. Ако се случи да дойде втори път (което е съвсем възможно, предвид че въпреки гадната обстановка, в Дупката готвят много вкусно), просто се вкарва нов запис.

Шефа на Дупката обича да знае разни неща. И като човек, обичащ да знае разни неща, иска да знае в кой момент от денонощието има най-много хора в кръчмата, и като цяло как се движи натоварването. Но понеже заведението е наистина голяма, няма начин някой да седи и д брои – трябва да се използват записите в таблицата. Въпреки че Дупката работи денонощно, натоварването е променливо.

В таблицата има около 500-600 хиляди записа на ден. Естествено, използва се partitioning, за да бъде управляем обема.

Ще представя един вариант, както и част от пътя, по който стигнах до него. Естествено, приемам всякакви предложения. Подозирам, че все някой е решавал такава задачка преди мен и аз сега откривам топлата вода.

Първата идея е да се направи pl/sql функция, която да проверява във всеки един момент колко хора е имало. Понеже колонките за дата в таблицата са от тип DATE, ще проверим всяка секунда за дадено денонощие (такава е точността на типа DATE). С един цикъл, за всяка секунда “X”, търсим посещения по следния начин:

SELECT X, COUNT(1)
  FROM client_visits 
 WHERE X BETWEEN time_in AND time_out;

Това се записва в една таблица, от която ваденето на графики става лесно.

Всичко това е хубаво, само че за дадените обеми нещото отнема абсурдно много време на тестовата система: около 30 часа за 24 часов период. И това при наличие на индекс по (time_in, time_out) – иначе няма шанс.

Тук се сещам за един въпрос, който много хора забравят: а трябва ли ни наистина толкова детайлна информация (за всяка секунда)? (виж „Oracle Insights“, глава 3: „Waste Not, Want Not“)

Полезно: Много често се оказва, че с леко облекчаване на бизнес изискванията, задачата става много по-лесно решима.

Така е и в този случай – шефа на Дупката е доволен да знае и как се движи натоварването по минути, не му трябва чак по секунди. Така цикълът (с малко променено условие) ще има 60 пъти по-малко итерации и ще пусне само 1440 заявки, вместо 86400. И (на нашата система) отнема само малко повече от половин час.

Обаче… всеки печен DB expert знае, че в общия случай ако нещо може да стане само с SQL, то е добре да се направи само с SQL. Да стане с една, пък била тя и тежка, заявка – вместо с 1440 по-леки. Естествено, има и изключения, но са много редки.

Първоначално тръгнах по грешен път. Сетих се, че не ми трябва да проверявам всяка секунда (или минута) – стига ми да проверявам за всеки момент, в който влиза някой. Ако в даден момент не влиза никой, там няма да има покачване на натоварването.

select time_in,
       (select count(1)
           from client_visits t2
          where t2.time_in < = t1.time_in
            and t2.time_out >= t1.time_in
            )
  from client_visits t1
 where time_in >= trunc(sysdate - 1) -- гледаме данните за 1 ден
   and time_in < trunc(sysdate)

Това няма да помогне на шефа на дупката да разбере кога има най-малко натоварване, но той така или иначе не се интересува от това - той си оразмерява кръчмето за максималното натоварване, а движението в натоварването му трябва за да си нагоди грубо персонала. Освен това входящите посетители са толкова много (500-600 хиляди посещения на ден), че и без това почти на всяка секунда има поне един влизащ и няма опасност да се пропусне нещо интересно.

Тук, обаче, нещата на вървяха много добре. Честно казано, не изчаках да видя колко време е необходимо, защото всичко над 15 минути ми се струваше твърде много. Тук извадих един друг коз на добрия dataabse expert:

Полезно: Често има неясни зависимости в данните, които помагат да се свърши по-малко работа. Връзки, които са естествени и ясни за нас, но не и за оптимизатора в БД.

Една такава връзка е, че никой не стои в дупката повече от един ден. Или даже и да има такива, те са твърде малко и не променят статистиката. За това направих следната малка оптимизация:

select time_in,
       (select count(1)
           from client_visits t2
          where t2.time_in < = t1.time_in
            and t2.time_out >= t1.time_in
            and t2.time_in > trunc(sysdate) - 2 -- малко оптимизация
            )
  from client_visits t1
 where time_in >= trunc(sysdate - 1) -- гледаме данните за 1 ден
   and time_in < trunc(sysdate)

Това вероятно е подобрило малко нещата, но аз отново нямах търпението да изчакам за да видя колко. Като цяло проблема на този подход е, че се правят твърде много проверки - всъщност, около 500-600 хиляди за един ден (колкото са посещенията).

Следващата идея беше да се изгонят поне повтарящите се проверки:

SELECT time_in,
       (SELECT COUNT(1)
         FROM   client_visits t2
         WHERE  t1.time_in BETWEEN t2.time_in AND t2.time_out
         AND    t2.time_in > trunc(SYSDATE - 2))
FROM   (SELECT DISTINCT time_in -- избягваме повторните проверки
         FROM   client_visits
         WHERE  time_in >= trunc(SYSDATE - 1)
         AND    time_in < trunc(SYSDATE)) t1;

Естествено, и това се оказа твърде бавно. По този начин правим близо 86400 проверки, а вече се разбрахме, че може да направим само 1440. За това се изкуших да направя така:

SELECT time_in,
       (SELECT COUNT(1)
         FROM   client_visits t2
         WHERE  t1.time_in BETWEEN t2.time_in AND t2.time_out
         AND    t2.time_in > trunc(SYSDATE - 2))
FROM   (SELECT DISTINCT trunc(time_in, 'MI') -- намиране само по един запис за всяка минута
         FROM   client_visits
         WHERE  time_in >= trunc(SYSDATE - 1)
         AND    time_in < trunc(SYSDATE)) t1;

Описах този грешен (в случая) път, защото при по-малко данни (примерно ако имахме само 500-600 посещения) би бил полезен. Иначе аз доста бързо се сетих за алтернативата. Само да кажа за последните 2 заявки:

Полезно: Операцията DISTINCT е много тежка при голям обем от данни, защото изисква сортиране на цялото множество.

Освен това с посочените заявки аз преравям 2 пъти данните, които хич не са малко. За това аз се нуждаех от начин да си генерирам всички минути в едно денонощие, без да се ровя в таблицата. Да си измисля данни, с които да направя join. За целта използвах следния трик

Полезно: Генератор за 100 реда с числата от 1 до 100:
select rownum from dual connect by level < = 100

Така стигнах до следния вариант:

SELECT time_in,
       (SELECT COUNT(1)
           FROM client_visits t2
          WHERE t1.time_in BETWEEN t2.time_in AND t2.time_out
            AND t2.time_in > trunc(SYSDATE) - 2) cnt
  FROM (select trunc(SYSDATE - 1) + rownum / 1440 time_in
           from dual
         connect by level < 1440) t1

Това нещо вече се справя за под 7 минути. За съжаление не показва в коя минута има най-много посетители в кръчмата, а в коя нулева секунда от минута. За да сравня целите минути ми трябват и посетителите, които са влезли преди началото на следващата минута. Ако гледам с разредност "секунда", този проблем не стои. Едно друго решение за заобикаляне на този проблем е да правя всички сравнения не с точната стойност на time_in и time_out, а с trunc(time_in, 'MI') и trunc(time_out, 'MI'). Но в този случай губя индекса (което пък се решава с функционален индекс). Но в настоящият вариант мога лесно да променя на колко периода да разделя денонощието - примерно на по 5 минути, или на 10 секунди, за да получа данни с различна точност.

И така, настоящият вариант е следния:

SELECT period_start,
       (SELECT COUNT(1)
           FROM client_visits t2
          WHERE ((t2.time_in < = t1.period_start and t2.time_out > t1.period_start) -- започнали преди периода
                 or -- или
                (t2.time_in > period_start and t2.time_in < period_end)) -- започнали в периода
            AND t2.time_in > SYSDATE - 2) cnt
  FROM (select trunc(SYSDATE - 1) + rownum / 1440 period_start, -- началото на периода
               trunc(SYSDATE - 1) + (rownum + 1) / 1440 period_end -- началото на следващият период
           from dual
         connect by level < 1440) t1

Не ми е трудно да се досетя, че може да има и съвсем различно, по-елегантно и ефективно решение. Ще се радвам ако някой сподели такова 🙂

 Posted by at 8:18

Разходка до Безбог, в снимки

 Personal  Коментарите са изключени за Разходка до Безбог, в снимки
Авг. 032009
 

Миналата седмица разпускахме само на 30-40 километра от Добринище и успях да прикоткам моите жени да се разходим до Безбог. Ех, баирче, баирче…

Пътя до хижа Гоце Делчев изобщо не е такъв, какъвто го помня. Вече е новичък, няма ями, за дупки да не говорим. Път като слънце. За това без проблем стигнахме до долната станция на лифта. Там предстоеше на Ирина да се запознае за първи път с „летящите пейки“. Малко се притеснявах да не се стресира. Нищо подобно – много се изкефи 🙂

И така, ето моите жени до безбожкото езеро:

Винаги се кефя да снимам хижата от тази позиция. Изглежда сякаш е на края на света… 🙂

Подминахме езерото и зпочнахме изкачване на Безбожкия превал. Което нямаше как да направим без да се подкрепим с вафли:

Ето така изглежда превала в най-високата си част:

От другата страна минахме още толкова път пеша, преди да спрем за похапване. Ето ме мен в основата на Полежаните, на фона на Папаз Гьол:

Вляво се вижда основата на Мангър Тепе или Острец, зад него е Момин двор. Между двата е Краледворската лява порта, през която се минава за Тевно езерто. Вдясно от мен е един от най-готините върхове в Пирин: Джангала, известен още като Самодивски връх (прилача малко на огромен трон, но само от тази страна е толкова полегат). В десният край на снимката е Самодивската порта, през която се минава за Валявишките езера по пътя за хижа Демяница.

След като хапнахме, поехме бавно по обратния път заради облаците, които се мотаеха около Момин двор:

На връщане, преди да започнем качването на превала:

Напред и вдясно е връх Безбог, вляво по билото е пътя за Полежан.

Ето един интересен клек, на върха на превала:

Безбожкото езеро и хижата, от върха на превала:

На слизане с летящите пейки цялото небе беше облачно:

 Posted by at 21:30
Авг. 032009
 

Миналата седмица ходихме семейно да отпуснем в хотел Делта, край Огняново. Хотелът е много хубав. Намира се малко трудно, дори и с толкова табели. Първата важна отбивка е от Добринище в посока Гоце Делчев, в село Господинци. Там се завива на ляво по един селски път, голяма част от който е в отлично състояние. Само тук-там има дупки, но пък са големи. Минава се през самото село Огняново, и след края на селото има един мост и десен завой, след който започва село Марчево. Точно слeд този десен завой „пътят“ за хотел Делта се намира силно вляво. Дано съм го описал добре и съм бил полезен.

И така… плюсовете на хотела са много. Хотелът всъщност е четири относително кокетни сгради по 3-4 етажа. Стаите са големи. За пръв път ми се случва аз да мога да легна на леглото и на право, и напреки, без да ми висят краката 🙂 . Има прилична баня с вана. Басейна е доволно голям с хладка вода; има и детски, с по-топла вода и нещо като джакузи – кръгло басейнче с диаметър 3-4 метра, пълно с много топла вода и дълбоко колкото да седнеш спокойно на дъното. Край басейна има много паркоместа, полянки за шезлонги и чадъри и т.н. Стаите се чистят редовно и добре. Абе идилия.

Хотел Делта
Четвъртата сграда на хотела

Пo-малкият проблем е, че там всичката вода е минерална. И няма начин да не смърдиш на яйца. Не само в басейна, а и в чешмите, в душа, даже и в тоалетното казанче е минерална. Което може да е хубаво за някои, но на мен ми стана досадно. Като се къпеш, цялата баня става буквално смърдяща. Сапуна не може да се отмива. Водата не е приятна за пиене. Няма и студена вода. Това е нещо, за което не бяхме подготвени, донякъде и по моя вина. Ако някой тръгне да ходи там на почивка, да си вземе туба с трапезна вода а пиене.

Това, заради което едва ли бих отишъл там, е обслужването. На регистратурата трудно намираш обслужване. Сервитьорките в ресторанта ги мързи да гледат. Манджите са пресолени. Водата спира. Изобщо отношението към клиента си е откровено гадно. Явно мениджърът на хотела не е много опитен… Да се чуди човек как хора, които явно нямат опит с хотелиерство, са построили толкова добър (външно) хотел. Може би има нещо общо с европейските пари, за които гордо уведомяваше табелата на входа (по САПАРД – и хотели ли спонсорират?). Между другото там на почивка имаше организирани групи държавни служители, от НОИ.

И така, ако решите все пак да отидете там и се поплацикате в басейнчето, мога да ви дам следните препоръки:

– запасете се с търпение. Персонала просто не знае как да се държи с клиенти. Ако не може да изтърпите администраторката да си довърши разговора с чистачката, преди да ви обърне внимание, не сте за там

– избягвайте ресторанта в сградата на хотела. Там обслужването е леко казано мудно и манджите са пресолени. Най-близкото друго заведение е в двора на хотела, където пекат мръвки на открито барбекю. Там обслужването определено ми хареса, а и имат едни невероятно вкусни печени питки с чеснов сос и масълце. Друго силно препоръчително място е ресторанта на хотел „Рай“, където готвача е много добър, а проциите са огромни.

Шопска салата
Шопска салата в „Рай“. Мащаба се губи, но камарата е внушителна. У дома нямам толкова големи чинии

– Вземете си достатъчно вода за пиене. Водата там просто не може да се пие. Не разчитайте да си купите на място – най-близкият магазин е на 2 километра, а в ресторанта бутилка трапезна вода е 2 лева.

– Добре е да отседнето от понеделник до петък, както бяхме ние. В събота става доста пренаселено – ние си тръгнахме към 8 часа сутринта и вече си личеше фраша.

Иначе мястото е много готино. Ако намерят един кадърен мениджър, който да привлече добър персонал, ще стане приказка.

 Posted by at 20:31