Как мога да накарам приложенията си да се мащабират добре?

Като цяло, какви видове дизайнерски решения помагат на едно приложение да се мащабира добре?

(Забележка: След като току-що научих за Big O Notation, искам да събера повече принципи на програмиране тук. Опитах се да обясня Big O Notation, като отговорих на собствения си въпрос по-долу, но искам общността да подобри както този въпрос, така и отговорите.)

Отговори досега
1) Дефинирайте мащабиране. Имате ли нужда от мащабиране за много потребители, трафик, обекти във виртуална среда?
2) Погледнете вашите алгоритми. Обемът на работата, която вършат, ще се мащабира ли линейно с действителното количество работа - т.е. брой елементи, през които да преминете, брой потребители и т.н.?
3) Погледнете вашия хардуер. Вашето приложение създадено ли е така, че да можете да го стартирате на множество машини, ако една не може да се справи?

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


person Community    schedule 03.09.2008    source източник


Отговори (7)


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

Първо изградете кода възможно най-просто, след това профилирайте системата след това и оптимизирайте само когато има очевиден проблем с производителността.

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

person Simon Johnson    schedule 03.09.2008

Добре, значи попаднахте на ключов момент при използването на „нотацията с голямо О“. Това е едно измерение, което със сигурност може да ви ухапе в задната част, ако не обръщате внимание. Има и други измерения, които някои хора не виждат през очилата с „голямото О“ (но ако се вгледате по-отблизо, те наистина са).

Прост пример за това измерение е присъединяване към база данни. Има "най-добри практики" при конструирането, да речем, на ляво вътрешно съединение, което ще помогне за по-ефективното изпълнение на sql. Ако разбиете релационното смятане или дори погледнете план за обяснение (Oracle), можете лесно да видите кои индекси се използват в какъв ред и дали се извършват някакви сканирания на таблици или вложени операции.

Концепцията за профилиране също е ключова. Трябва да имате задълбочени инструменти и с правилната детайлност във всички движещи се части на архитектурата, за да идентифицирате и коригирате всички неефективности. Да кажем например, че изграждате 3-степенно, многонишково, MVC2 уеб-базирано приложение с либерално използване на AJAX и обработка от страна на клиента заедно с OR Mapper между вашето приложение и DB. Опростеният линеен единичен поток на заявка/отговор изглежда така:

browser -> web server -> app server -> DB -> app server -> XSLT -> web server -> browser JS engine execution & rendering

Трябва да имате някакъв метод за измерване на производителността (времена за реакция, производителност, измерена в „неща за единица време“ и т.н.) във всяка от тези отделни области, не само на ниво кутия и операционна система (CPU, памет, диск i/o, и т.н.), но специфични за услугата на всяко ниво. Така че на уеб сървъра ще трябва да знаете всички броячи за уеб сървъра, който използвате. В нивото на приложението ще имате нужда от това плюс видимост във всяка виртуална машина, която използвате (jvm, clr, каквото и да е). Повечето OR съпоставители се проявяват във виртуалната машина, така че се уверете, че обръщате внимание на всички специфики, ако те са видими за вас на този слой. Вътре в DB ще трябва да знаете всичко, което се изпълнява, и всички специфични параметри за настройка за вашия вкус на DB. Ако имате големи пари, BMC Patrol е доста добър залог за повечето от тях (с подходящи модули за знания (KM)). В евтиния край със сигурност можете да навиете свой собствен, но пробегът ви ще варира в зависимост от дълбочината ви на опит.

Ако приемем, че всичко е синхронно (не се случват неща, базирани на опашка, които трябва да изчакате), има много възможности за проблеми с производителността и/или скалируемостта. Но тъй като публикацията ви е за мащабируемост, нека игнорираме браузъра, с изключение на всички отдалечени XHR извиквания, които ще извикат друга заявка/отговор от уеб сървъра.

И така, предвид този проблемен домейн, какви решения бихте могли да вземете, за да помогнете с мащабируемостта?

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

  2. Срив на сесията на всяко ниво. Необходимо или не? Предполагаме, че всяко ниво ще бъде клъстер от кутии хоризонтално под някакъв механизъм за балансиране на натоварването. Балансирането на натоварването обикновено е много леко, но някои реализации на сесия при отказ могат да бъдат по-тежки от желаното. Също така дали работите със залепващи сесии може да повлияе на вашите опции по-дълбоко в архитектурата. Също така трябва да решите дали да свържете уеб сървър към конкретен сървър на приложения или не. В света на отдалечените .NET вероятно е по-лесно да ги свържете заедно. Ако използвате стека на Microsoft, може да е по-мащабируемо да направите 2-степенно (прескочете отдалеченото), но трябва да направите значителен компромис със сигурността. От страна на java винаги съм го виждал поне 3-степенно. Няма причина да го правим по друг начин.

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

  4. ИЛИ неефективност на картографа. Има несъответствие на импеданса между обектния дизайн и релационния дизайн. Конструкцията много към много в RDBMS е в пряк конфликт с йерархиите на обекти (person.address срещу location.resident). Колкото по-сложни са вашите структури от данни, толкова по-малко ефективен ще бъде вашият ИЛИ картограф. В даден момент може да се наложи да пресечете стръвта в еднократна ситуация и да направите по-...ъъъ...примитивен подход за достъп до данни (съхранена процедура + слой за достъп до данни), за да изтръгнете повече производителност или мащабируемост от конкретно грозен модул. Разберете свързаните с това разходи и вземете съзнателно решение.

  5. XSL трансформира. XML е прекрасен, нормализиран механизъм за пренос на данни, но човече може ли да бъде куче с огромна производителност! В зависимост от това колко данни носите със себе си и кой синтаксичен анализатор изберете и колко сложна е структурата ви, можете лесно да се оцветите в много тъмен ъгъл с XSLT. Да, академично това е брилянтно чист начин за създаване на презентационен слой, но в реалния свят може да има катастрофални проблеми с производителността, ако не обърнете специално внимание на това. Виждал съм система да изразходва над 30% от времето за транзакция само в XSLT. Не е добре, ако се опитвате да увеличите 4 пъти потребителската база, без да купувате допълнителни кутии.

  6. Можете ли да си купите изход от задръстване с мащабируемост? Абсолютно. Гледал съм как се случва повече пъти, отколкото бих искал да призная. Законът на Мур (както вече споменахте) е валиден и днес. Имайте малко допълнителни пари под ръка за всеки случай.

  7. Кеширането е чудесен инструмент за намаляване на напрежението върху двигателя (увеличаването на скоростта и пропускателната способност е удобен страничен ефект). Това обаче има цена по отношение на отпечатъка на паметта и сложността при обезсилване на кеша, когато е остарял. Моето решение би било да започна напълно чисто и бавно да добавя кеширане само там, където решите, че е полезно за вас. Твърде много пъти сложността се подценява и това, което е започнало като начин за отстраняване на проблеми с производителността, се оказва, че причинява функционални проблеми. Също така, обратно към коментара за използването на данни. Ако създавате обекти на стойност гигабайти всяка минута, няма значение дали кеширате или не. Бързо ще увеличите обема на паметта си и събирането на боклука ще съсипе деня ви. Така че предполагам, че изводът е да се уверите, че разбирате точно какво се случва във вашата виртуална машина (създаване на обект, унищожаване, GC и т.н.), така че да можете да вземете възможно най-добрите решения.

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

person Ed Lucas    schedule 16.09.2008

Ами има този блог, наречен High Scalibility, който съдържа много информация по тази тема. Някои полезни неща.

person Malik Daud Ahmad Khokhar    schedule 03.09.2008

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

Решете какво всъщност означава мащабирането за вашия проект. Има ли безкрайно количество потребители, дали е в състояние да се справи с наклонена черта на уебсайт, дали е цикъл на разработка?

Използвайте това, за да фокусирате усилията си за развитие

person svrist    schedule 03.09.2008

Джеф и Джоел обсъждат мащабирането в Stack Overflow Podcast #19.

person GateKiller    schedule 03.09.2008

FWIW, повечето системи ще се мащабират най-ефективно, като игнорират това, докато не стане проблем – законът на Мур все още е в сила и освен ако трафикът ви не расте по-бързо от закона на Мур, обикновено е по-евтино просто да си купите по-голяма кутия (за $2 или $3K на брой pop), отколкото да плащат на разработчиците.

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

Ако смятате, че има голяма вероятност вашето приложение да се нуждае от мащабиране, може да е разумно да разгледате системи като memcached или map reduction сравнително рано в разработката си.

person Tim Howland    schedule 03.09.2008
comment
Съжалявам, категорично не съм съгласен с това. Често, докато скалируемостта се превърне в проблем, единственото истинско лекарство е да се пренапише системата почти от нулата, тъй като необходимите промени често са всеобхватни и дълбоки. - person RickNZ; 29.12.2009
comment
Преждевременната оптимизация е коренът на всяко зло. - person Tim Howland; 29.12.2009

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

Например, представете си, че имате няколко виртуални коли в град. Във всеки момент искате всяка кола да има карта, показваща къде се намират всички коли.

Един от начините за подход към това би бил:

    for each car {
       determine my position;  
       for each car {  
         add my position to this car's map;  
       }
    }

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

Но има проблем с мащабируемостта. Когато има 2 коли, тази стратегия отнема 4 стъпки „добавете моята позиция“; когато има 3 коли, са необходими 9 стъпки. За всяка „актуализация на позицията“ трябва да преминете през целия списък с автомобили – и всяка кола има нужда от актуализиране на позицията си.

Пренебрегвайки колко други неща трябва да се направят с всяка кола (например може да са необходими фиксиран брой стъпки за изчисляване на позицията на отделна кола), за N коли са необходими N2 "посещения на автомобили", за да стартирате този алгоритъм. Това не е проблем, когато имате 5 коли и 25 стъпала. Но докато добавяте автомобили, ще видите, че системата затъва. 100 коли ще направят 10 000 крачки, а 101 коли ще направят 10 201 крачки!

По-добър подход би бил да отмените влагането на for циклите.

    for each car {  
      add my position to a list;  
    }  
    for each car {    
      give me an updated copy of the master list;  
    }

При тази стратегия броят на стъпките е кратен на N, а не на N2. Така че 100 коли ще отнеме 100 пъти повече работа на 1 кола – НЕ 10 000 пъти повече работа.

Тази концепция понякога се изразява в „нотация с голямо O“ – броят на необходимите стъпки е „голямо O от N“ или „голямо O от N2“.

Имайте предвид, че тази концепция се занимава само с мащабируемост - не оптимизира броя на стъпките за всяка кола. Тук не ни интересува дали са необходими 5 стъпки или 50 стъпки на кола - основното е, че N коли правят (X * N) стъпки, а не (X * N2).

person Nathan Long    schedule 03.09.2008