Как избрахме система за кеширане в Спортмастера. Част 1

Здравейте! Казвам се Алексей Пьянков, разработчик в компанията Спортмастер. В това пост разказвам как започна работата по сайта Спортмастер през 2012 година, какви инициативи успяхме да "пробутаме" и какви проблеми срещнахме.

Днес искам да споделя мисли, свързани с друг сюжет – избор на система за кеширане за java-бекенда в администраторския панел на сайта. Този сюжет е особено важен за мен – въпреки че историята продължи само 2 месеца, през тези 60 дни работехме по 12-16 часа, без почивен ден. Никога досега не съм мислил или си представял, че е възможно да се работи толкова много.

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

Как избрахме система за кеширане в Спортмастера. Част 1

Когато новата версия на сайта Спортмастер беше пусната в продукция, данните идваха по начин, меко казано, не много удобен. Основата бяха таблиците, подготвени за предишната версия на сайта (Bitrix), които трябваше да бъдат извлечени в ETL, приведени в нов вид и обогатени с различни допълнителни данни от още десетина системи. За да се появят нова снимка или описание на продукта сайта, трябваше да изчакаме до следващия ден — обновлението само през нощта, веднъж в денонощието.

Първоначално имаше толкова много ангажименти през първите седмици в продукция, че подобни неудобства за контент-мениджърите изглеждаха незначителни. Но, щом всичко се стабилизира, развитието на проекта продължи — след няколко месеца, в началото на 2015 година, започнахме активно да разработваме администраторския панел. През 2015 и 2016 години всичко вървеше добре, регулярно пускахме нови версии, администраторският панел обхващаше все по-голяма част от подготовката на данните и се подготвяхме за това, че скоро нашият екип ще получи най-важното и сложно – товарният контур (пълна подготовка и управление на данни за всички продукти). Но през лятото на 2017 година, точно преди пускането на товарния контур, проектът ще се окаже в много сложна ситуация – именно заради проблемите с кеширането. За този епизод искам да разкажа във втората част от тази двучастна публикация.

Но в този пост ще започна отдалеч, ще се опитам да обобщя някои мисли – представления за кеширане, които предварително да се прегледат преди голям проект, биха били добра стъпка.

Когато възникне необходимост от кеширане

Необходимостта от кеширане не се появява просто така. Ние, разработчиците, създаваме софтуерен продукт и искаме той да бъде търсен. Ако продуктът е търсен и успешен — потребителите идват. И продължават да идват. И така, с времето потребителите стават изключително много и продуктът става натоварен.

В началните етапи не мислим за оптимизация и производителност на кода. Най-важното е функционалността, бързо да пуснем пилот и да проверим хипотезите. И ако натоварването нараства — увеличаваме хардуера. Увеличаваме го два-три, пет, а защо не и десет пъти. Някъде тук – финансите вече няма да позволят. А колко ще нарасне броят на потребителите? Това не ще бъде просто 2-5-10, а в случай на успех — от 100 до 1000 и до 100 хил. пъти. Тоест, рано или късно, оптимизацията ще стане необходима.

Да предположим, че част от кода (нека наречем тази част функция) работи неприлично бавно, и искаме да намалим времето за изпълнение. Функцията — това може да е достъп до база данни, може да е изпълнение на някаква сложна логика — важното е, че отнема време. Колко можем да намалим времето за изпълнение? В предела – можем да го намалим до нула, но не по-далеч. А как можем да намалим времето за изпълнение до нула? Отговорът: напълно да изключим изпълнението. Вместо това – веднага да върнем резултата. А как можем да узнаем резултата? Отговорът: или да го изчислим, или да го видим, без да изчисляваме. Изчисляването отнема време. А дали можем да погледнем – например, можем да запомним резултата, който функцията е дала предходния път при извикване с подобни параметри.

Тоест, реализация на функцията не е важна за нас. Достатъчно е само да знаем от какви параметри зависи резултатът. Тогава, ако представим стойностите на параметрите под формата на обект, който може да се използва като ключ в някаква хранилище — то резултатът от изчислението можем да запазим и при следващото извикване да го отчетем. Ако записът и четенето на резултата стават по-бързо от изпълнението на функцията - имаме печалба по скорост. Размерът на печалбата може да достигне 100, 1000 и 100 хиляди пъти (10^5 – това е по-скоро изключение, но в случая с бази данни, които закъсняват значително – напълно възможно).

Основни изисквания към системата за кеширане

Първото изискване, което може да бъде поставено пред системата за кеширане — бърза скорост на четене и в малко по-слаба степен – скорост на писане. Това е така, но само дотогава, докато не внедряваме системата в продукция.

Нека разгледаме един такъв случай.

Да допуснем, че сме осигурили хардуер за текущото натоварване и сега постепенно внедряваме кеширане. Потребителите малко нарастват, натоварването се увеличава – добавяме кешове, прикрепяме това тук и там. Това продължава известно време, и ето, че тежките функции практически не се извикват — цялото основно натоварване пада на кеша. Броят на потребителите за това време е нараснал N пъти.

А ако началният запас от хардуер е могъл да бъде 2-5 пъти, то с помощта на кеша можем да увеличим производителността 10 пъти или в добрия случай 100 пъти, на места, може би, и 1000 пъти. Тоест, на същия хардуер – обработваме 100 пъти повече заявки. Чудесно, заслужихме награда!

Но сега, в един прекрасен момент, случайно системата се срине и кешът падне. Нищо особено – ние избрахме кеша по изискването 'висока скорост на четене и писане, останалото не е важно'.

Относно стартовото натоварване запасът от хардуера ни беше 2-5 пъти, а натоварването за това време нарасна 10-100 пъти. С помощта на кеша ние изключвахме извикванията на тежките функции и затова всичко летеше. А сега, без кеша – колко пъти ще се свие нашата система? Какво ще се случи? Системата ще падне.

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

Извод: проекти с високи натоварвания в продукция изискват от системата за кеширане не само висока скорост на четене и запис, но и запазване на данните и устойчивост на откази.

Мъки на избора

В проекта с админ панел изборът беше такъв: първо поставихме Hazelcast, тъй като вече бяхме запознати с този продукт от опита на основния сайт. Но тук този избор се оказа неуспешен – за нашия профил на натоварване Hazelcast работи не просто бавно, а ужасно бавно. А сроковете за излизане в продукция вече бяхме поели по това време.

Спойлер: как точно се стекоха обстоятелствата, че пропуснахме такава катастрофа и получихме остра и напрегната ситуация – ще разкажа във втората част – и как се озовахме в това положение, и как излязохме. Но сега само ще кажа, че това беше силен стрес, и "мисля - не се мисли, разклащаме бутилката". "Разклащаме бутилката" – това също е спойлер, за това ще стане дума по-късно.

Какво направихме:

  1. Съставяме списък от всички системи, които Google и StackOverflow предлагат. Малко над 30.
  2. Пишем тестове с натоварване, характерно за продукцията. За това записахме данните, които преминават през системата в продукционната среда – нещо като снифер за данни, но вътре в системата. В тестовете пускахме точно тези данни.
  3. В цялата команда, всеки избира следващата система от списъка, настройва я и пуска тестовете. Не минава тест, не понася натоварването – изхвърляме, преминаваме към следващата в реда.
  4. На 17-та система стана ясно, че всичко е безнадеждно. Достатъчно е "да разклащаме бутилката", време е да помислим сериозно.

Но това е вариант, когато трябва да се избере система, която "ще мине по скорост" в предварително подготвените тестове. А какво, ако такива тестове все още няма и искаме да изберем по-бързо?

Моделираме такъв вариант (трудно е да си представим, че разработчик от средно ниво живее във вакуум и на момента на избора още не е оформил предпочитания, какъв продукт да опита първо — затова, по-нататъшните разсъждения са по-скоро теоретични/философски/за начинаещи).

Определяйки изискванията, ще започнем да избираме решение от кутията. Защо да изобретяваме велосипеда: ще отидем и ще вземем готова система за кеширане.

Ако току-що започвате и ще търсите в Гугъл, последователността може да варира, но като цяло, ориентировките ще бъдат следните. Първо, ще се натъкнете на Redis, който е широко известен. След това ще разберете, че има EhCache, която е най-старата и доказана система. После ще прочетете за Tarantool — местна разработка с уникален аспект на решението. И също Ignite, защото сега е на вълната на популярността и получава подкрепа от СберТех. Накрая ще спомена и Hazelcast, тъй като той често се появява в света на предприятията, сред големите компании.

Този списък не е изчерпан, съществуват десетки системи. Ние ще прикрепим само една. Нека вземем избраните 5 системи за "конкурс на красотата" и да проведем селекция. Кой ще бъде победителят?

Redis

Четем какво пишат на официалния сайт.
Redis — проект с отворен код. Предлага in-memory хранилище на данни, възможност за запазване на диска, автоматично разделяне на партиции, висока наличност и възстановяване след мрежови прекъсвания.

Изглежда, че всичко е чудесно, може да го вземете и да го интегрирате — всичко, от което се нуждаете, той прави. Но нека просто от интерес да погледнем и останалите кандидати.

EhCache

EhCache — "най-широко използваният кеш за Java" (превод на слогана от официалния сайт). Също проект с отворен код. И тук разбираме, че Redis не е за Java, а е общ и за взаимодействие с него е необходима обвивка. А EhCache ще бъде по-удобен. Какво още обещава системата? Надеждност, провереност, пълна функционалност. И е най-разпространената. И кешира терабайти данни.

Redis е забравен, готов съм да избера EhCache.

Но чувството на патриотизъм ме подтиква да погледна какво е добро при Tarantool.

Tarantool

Tarantool — описан като "Платформа за интеграция на данни в реално време". Звучи много сложно, затова четем страницата подробно и намираме силно твърдение: "Кешира 100% от данните в оперативната памет". Това би могло да предизвика въпроси — тъй като данните може да бъдат значително повече от паметта. Обяснението е, че за запис на данни на диска от паметта Tarantool не използва сериализация. Вместо това - използва нискоуровневите характеристики на системата, когато паметта просто се мапва на файловата система с доста добри показатели на I/O. Общо взето, направили са го наистина чудесно и страхотно.

Нека погледнем внедренията: Mail.ru корпоративна магистрала, Авито, Билайн, Мегафон, Алфа-Банк, Газпром…

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

Но все пак…

Ignite

… все още е Ignite, обявен като "in-memory изчислителна платформа... in-memory скорости на петабайти данни". Има много предимства: разпределено in-memory кеширане, най-бързото хранилище key-value и кеш, хоризонтално мащабиране, висока наличност, строга цялостност. В общи линии, най-бързият – това е Ignite.

Внедрения: Сбербанк, American Airlines, Yahoo! Japan. А след това научавам, че Ignite не просто е внедрен в Сбербанк, а екипът на СберТех изпраща свои хора в екипа на Ignite, за да доразвият продукта. Това ме впечатлява изцяло и съм готов да взема Ignite.

Съвсем не е ясно защо, но гледам петата точка.

Hazelcast

Влизам в сайта Hazelcast, чета. И се оказва, че най-бързото решение за разпределено кеширане е Hazelcast. Той е многократно по-бърз от всички останали решения и по принцип е лидер в областта на in-memory data grid. На фона на това, ако взема нещо друго – не се уважавам.

Всичко, готов съм да взема Hazelcast.

Сравнение

Но ако погледнем, петте кандидати са описани така, че всеки от тях е най-добрият. Как да изберем? Можем да погледнем кой е най-популярен, да потърсим сравнения, и главоболията ще отминат.

Намираме такова преглед, избираме нашите 5 системи.

Как избрахме система за кеширане в Спортмастера. Част 1

Тук те са подредени: на върха е Redis, на второ място — Hazelcast, набират популярност Tarantool и Ignite, а EhCache остава на същата позиция.

Но нека погледнем на метода на изчисление: линкове към уебсайтове, общ интерес към системата, обяви за работа — страхотно! Тоест, когато системата ми се срути, ще кажа: "Не, тя е надеждна! Ето колко много предложения за работа...". Такова простично сравнение не е подходящо.

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

Добре, не се предаваме, ще намерим пряко сравнение на системите. Ще вземем двата най-добри варианта — Redis и Hazelcast. Интересува ни скоростта, по този параметър ще направим сравнение.

Hz срещу Redis

Намираме такова сравнение:
Как избрахме система за кеширане в Спортмастера. Част 1

Синьото е Redis, червеното е Hazelcast. Hazelcast печели навсякъде, и това е обяснено: той е многопоточен, високо оптимизиран, всеки поток работи с своя партиция, така че няма блокировки. Redis е еднопоточен, не печели от съвременните многоядрени CPU. Hazelcast има асинхронно I/O, а Redis-Jedis - блокиращи сокети. Накрая, Hazelcast използва бинарен протокол, а Redis е насочен към текст, което означава, че е неефективен.

За всеки случай ще се обърнем към още един източник за сравнение. Какво ще ни покаже той?

Redis vs Hz

Още едно сравнение:
Как избрахме система за кеширане в Спортмастера. Част 1

Тук обратното е вярно, червеното е Redis. Тоест, Redis печели по производителност пред Hazelcast. В първото сравнение печели Hazelcast, във второто - Redis. Тук много точно обясниха защо в предишното сравнение спечели Hazelcast.

Оказва се, че резултатът от първото всъщност е манипулиран: взели са Redis в базовото му състояние, а Hazelcast е настроен за тестовия случай. Така че, от една страна, не можем да вярваме на никого, а от друга, когато все пак изберем система, трябва да я настроим правилно. Тези настройки включват десетки, почти стотици параметри.

Разклащаме бутилката

И целият процес, който току-що извършихме, мога да обясня с метафората 'Разклащаме бутилката'. Тоест, сега не е нужно да програмираме, основното е да можем да четем stackoverflow. В екипа ми има човек-професионалист, който точно така работи в критичните моменти.

Какво прави? Вижда неработеща машинка, вижда stack trace, взима някои от думите (кои точно - това е неговата експертиза в програмата), търси в Google, намира stackoverflow сред отговорите. Не чете, не се замисля, а сред отговорите на въпроса - избира нещо, което най-много напомня на предложението 'направи това и онова' (избирането на такъв отговор - това е неговият талант, тъй като не винаги това е именно отговорът с най-много лайкове), прилага, гледа: ако нещо се е променило, е отлично. Ако не се е променило - връщаме назад. И повтаряме старта-проверка-търсене. И по този интуитивен начин постига, че след време кодът работи. Не знае защо, не знае какво е направил, не може да обясни. Но! Тази работа функционира. И 'пожарът е угасен'. Сега разбираме какво направихме. Когато програмата работи - това е значително по-лесно. И спестява значително време.

Този метод е много добре обяснен с такъв пример.

Някога беше много популярно да се сглобява ветроходка в бутилка. При това ветроходката е голяма и крехка, а гърлото на бутилката е много тясно, не може да я вкараш вътре. Как да я сглобим?

Как избрахме система за кеширане в Спортмастера. Част 1

Има такъв метод, много бърз и много ефективен.

Корабът се състои от куп дреболии: пръчки, въжета, платна, лепило. Всичко това слагаме в бутилката.
Вземаме бутилката с две ръце и започваме да я разтърсваме. Разтърсваме я, разтърсваме я. И обикновено – получава се пълна глупост, разбира се. Но понякога. Понякога се получава кораб! Точно, нещо, което прилича на кораб.

Това нещо го показваме на някого: «Серега, виждаш ли!?». И наистина, отдалеч – сякаш е кораб. Но после не може да се пусне.

Има и друг метод. Използват го по-напреднали типове, такива хакери.

Дадох на такъв момък задача, той я изпълни и си тръгна. И гледаш – изглежда свършено. А след известно време, когато трябва да доработя кода — там започва такова нещо заради него… Добре, че той вече се е отдалечил далеч. Това са такива момчета, които на примера на бутилката ще направят така: виждате, там, където е дъното – стъклото се извива. И не е съвсем ясно дали е прозрачно или не. Тогава «хакерите» отрязват дъното, вкарват кораба, дъното после го лепят отново и сякаш така и трябва.

От гледна точка на формулиране на задачата изглежда всичко правилно. Но ето на примера с корабите: за какво изобщо да се прави този кораб, на кого изобщо му е нужен? Той не носи никаква функционалност. Обикновено такива кораби — това са подаръци на много високи личности, които го слагат на рафт над себе си, като някакъв символ, като знак. И ако при такъв човек, ръководител на голям бизнес или висш чиновник, като флаг стои такава халтура, от която е отрязан гърлото? По-добре е, ако той никога не разбере за това. Как обаче в крайна сметка правят тези кораби, които могат да се подарят на важен човек?

Единственото ключово място, с което наистина не може да се направи нищо, е корпусът. А корпусът на кораба преминава през гърлото на бутилката. Докато корабът се сглобява извън бутилката. Но това не е просто сглобяване на кораб, а истинско ювелирно занаято. В съставните части се добавят специални лостчета, които позволяват след това да се повдигнат. Например, платната се сгъват, внимателно се внасят вътре, а след това с помощта на пинцет много фино и точно, се повдигат и нагласят. В резултат получаваме произведение на изкуството, което може с чиста съвест и гордост да се дари.

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

Връщайки се към задачата по избор на нашия кеширащ сървър, как можем да приложим този подход? Предлагам такъв вариант на избор от всички системи, които съществуват – да не се тресе бутилката, а да се погледне какво в принцип има в тях, на какво да се обърне внимание при избора на система.

Къде да търсим bottle-neck

Нека опитаме да не тресем бутилката, а да не преглеждаме всичко по ред, а да видим какви задачи биха възникнали, ако решим да проектираме такава система сами за нашата задача. Разбира се, няма да строим колело, но ще се възползваме от тази схема, за да се ориентираме към какви моменти да обърнем внимание в описанията на продуктите. Да начертаем такава схема.

Как избрахме система за кеширане в Спортмастера. Част 1

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

Все още е необходима код-логика (2), която всъщност се отнася до кеширането. С този код клиентите взаимодействат чрез определено API. Клиентският код (1) може да бъде както в рамките на същата JVM, така и да се свързва с него по мрежата. Логиката, реализирана вътре – това са решенията, кои обекти да оставим в кеша и кои да изхвърлим. За съхранение на кеша използваме памет (3), но ако е необходимо, можем да запазим част от данните и на диска (4).

Нека разгледаме в кои части ще възникне натоварване. Всъщност, натоварване ще окажат всяка стрелка и всеки възел. Първо, между клиентския код и API, ако това е мрежово взаимодействие, забавянето може да бъде доста забележимо. Второ, в рамките на самото API — ако прекаляваме със сложната логика, можем да се сблъскаме с CPU. И е желателно логиката да не използва паметта излишно. Остава взаимодействието с файловата система – в обикновен вариант това е сериализиране / възстановяване и запис / четене.

Следва взаимодействието с клъстера. Вероятно той ще бъде в тази съща система, но може да е и отделно. Тук също трябва да се вземе предвид предаването на данни към него, скоростта на сериализиране на данни и взаимодействието между клъстера.

Сега, от една страна – можем да си представим "какви зъбни колела ще се въртят" в кеш-системата при обработка на заявки от нашия код, а от друга страна – можем да преценим какви и колко заявки нашият код ще генерира към тази система. Това е достатъчно, за да направим до известна степен трезв избор – да подберем система, подходяща за нашия вариант на използване.

Hazelcast

Нека видим как да приложим такова разпределение към нашия списък. Например, Hazelcast.

За да запишем / вземем данни от Hazelcast, клиентският код се обръща (1) към API. Hz позволява стартиране на сървър като вграден, и в този случай извикването на API – е повикване на метод вътре в JVM, може да се смята за безплатно.

За да проработи логиката в (2), Hz разчита на хеш от байт-масива на сериализирания ключ – следователно, сериализацията на ключа ще се случи във всеки случай. Това е неизбежен одит за Hz.
Стратегиите за изхвърляне са реализирани добре, но за специални случаи – можем да включим свои собствени. За тази част не трябва да се притесняваме.

Склад (4) може да бъде свързан. Отлично. Взаимодействието (5) за вградените решения може да се счита за моментално. Обменът на данни между възлите в клъстера (6) – да, той съществува. Това е принос в полза на отказоустойчивостта на цена на скоростта. Намаляването на цената се осъществява чрез Hz-функцията Near-cache – данните, получени от други възли на клъстера, ще бъдат кеширани.

Какво може да се направи в такива условия за увеличаване на скоростта?

Например, за да избегнем сериализацията на ключа в (2) – над Hazelcast да добавим още един кеш за най-горещите данни. В Спортмастер за тази цел избрахме Caffeine.

За настройката на ниво (6) в Hz са предложени два типа съхранение: IMap и ReplicatedMap.
Как избрахме система за кеширане в Спортмастера. Част 1

Струва си да се спомене как Hazelcast попадна в стек технологиите на Спортмастер.

През 2012 година, когато работехме върху първия пилот на бъдещия сайт, именно Hazelcast се оказа първата връзка, която търсачката извади. Започнахме запознанството "от първия път" — впечатли ни фактът, че само след два часа, когато интегрирахме Hz в системата — той работеше. И работеше добре. До края на деня написахме няколко теста, зарадвахме се. И този запас от бодрост беше достатъчен, за да преодолеем изненадите, които Hz поднасяше с времето. В момента отборът на Спортмастер няма причина да се отказва от Hazelcast.

Но такива аргументи като "първата връзка в търсачката" и "бързо създадохме HelloWorld" — това, разбира се, е изключение и специфично за момента, в който протичаше изборът. Истинските изпитания за избраната система започват с въвеждането в продукция и именно на този етап трябва да се обърне внимание при избора на всяка система, включително кеш. Всъщност в нашия случай може да се каже, че избрахме Hazelcast случайно, но после стана ясно, че сме избрали правилно.

За продукцията много по-важни са: мониторингът, обработката на сривове на отделни възли, репликацията на данни, цената на мащабирането. Тоест, трябва да се обърне внимание на задачите, които ще възникнат точно при поддръжката на системата – когато натоварването многократно надвиши планираното, когато случайно заредим нещо неправилно и не на правилното място, когато трябва да внедрим нова версия на кода, да сменим данни и да направим това незабелязано за клиентите.

За всички тези изисквания, Hazelcast, безспорно, е подходящ.

Това е само началото

Но Hazelcast не е панацея. През 2017 г. избрахме Hazelcast за кеширане в админ панела, опирайки се на доброто впечатление от предишния опит. Това изигра ключова роля в много зла шега, заради която се озовахме в сложна ситуация и „героически“ се измъквахме от нея 60 дни. Но за това, в следващата част.

А засега… Честита нова кодировка!

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster