Попаднах на тясното място в производителността на DB, къде сега?

Имам някои заявки, които отнемат твърде дълго (300ms) сега, когато базата данни е нараснала до няколко милиона записа. За мое щастие заявките не трябва да разглеждат по-голямата част от тези данни, тези последни 100 000 записа ще са достатъчни, така че моят план е да поддържам отделна таблица с най-новите 100 000 записа и да изпълнявам заявките срещу това. Ако някой има някакви предложения за по-добър начин да направите това, ще бъде страхотно. Истинският ми въпрос е какви са опциите, ако заявките трябва да се изпълняват срещу историческите данни, каква е следващата стъпка? Неща, за които се сетих:

  • Надстройте хардуера
  • Използвайте база данни в паметта
  • Кеширайте обектите ръчно във вашата собствена структура от данни

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

Съжалявам, трябваше да спомена това, използвам mysql.

Забравих да спомена индексирането в горното. Честно казано, индексирането беше единственият ми източник на подобрение досега. За да идентифицирам тесните места, използвах maatkit за заявките, за да покажа дали индексите се използват или не.

Разбирам, че сега се отдалечавам от това, за което беше предназначен въпросът, така че може би трябва да направя друг. Проблемът ми е, че EXPLAIN казва, че заявката отнема 10 ms, а не 300 ms, което jprofiler отчита. Ако някой има някакви предложения, ще съм много благодарен. Запитването е:

select bv.* 
from BerthVisit bv 
inner join BerthVisitChainLinks on bv.berthVisitID = BerthVisitChainLinks.berthVisitID 
inner join BerthVisitChain on BerthVisitChainLinks.berthVisitChainID = BerthVisitChain.berthVisitChainID 
inner join BerthJourneyChains on BerthVisitChain.berthVisitChainID = BerthJourneyChains.berthVisitChainID 
inner join BerthJourney on BerthJourneyChains.berthJourneyID = BerthJourney.berthJourneyID 
inner join TDObjectBerthJourneyMap on BerthJourney.berthJourneyID = TDObjectBerthJourneyMap.berthJourneyID 
inner join TDObject on TDObjectBerthJourneyMap.tdObjectID = TDObject.tdObjectID 
where 
BerthJourney.journeyType='A' and 
bv.berthID=251860 and 
TDObject.headcode='2L32' and 
bv.depTime is null and 
bv.arrTime > '2011-07-28 16:00:00'

и изходът от EXPLAIN е:

+----+-------------+-------------------------+-------------+---------------------------------------------+-------------------------+---------+------------------------------------------------+------+-------------------------------------------------------+
| id | select_type | table                   | type        | possible_keys                               | key                     | key_len | ref                                            | rows | Extra                                                 |
+----+-------------+-------------------------+-------------+---------------------------------------------+-------------------------+---------+------------------------------------------------+------+-------------------------------------------------------+
|  1 | SIMPLE      | bv                      | index_merge | PRIMARY,idx_berthID,idx_arrTime,idx_depTime | idx_berthID,idx_depTime | 9,9     | NULL                                           |  117 | Using intersect(idx_berthID,idx_depTime); Using where | 
|  1 | SIMPLE      | BerthVisitChainLinks    | ref         | idx_berthVisitChainID,idx_berthVisitID      | idx_berthVisitID        | 8       | Network.bv.berthVisitID                        |    1 | Using where                                           | 
|  1 | SIMPLE      | BerthVisitChain         | eq_ref      | PRIMARY                                     | PRIMARY                 | 8       | Network.BerthVisitChainLinks.berthVisitChainID |    1 | Using where; Using index                              | 
|  1 | SIMPLE      | BerthJourneyChains      | ref         | idx_berthJourneyID,idx_berthVisitChainID    | idx_berthVisitChainID   | 8       | Network.BerthVisitChain.berthVisitChainID      |    1 | Using where                                           | 
|  1 | SIMPLE      | BerthJourney            | eq_ref      | PRIMARY,idx_journeyType                     | PRIMARY                 | 8       | Network.BerthJourneyChains.berthJourneyID      |    1 | Using where                                           | 
|  1 | SIMPLE      | TDObjectBerthJourneyMap | ref         | idx_tdObjectID,idx_berthJourneyID           | idx_berthJourneyID      | 8       | Network.BerthJourney.berthJourneyID            |    1 | Using where                                           | 
|  1 | SIMPLE      | TDObject                | eq_ref      | PRIMARY,idx_headcode                        | PRIMARY                 | 8       | Network.TDObjectBerthJourneyMap.tdObjectID     |    1 | Using where                                           | 
+----+-------------+-------------------------+-------------+---------------------------------------------+-------------------------+---------+------------------------------------------------+------+---------------------------------------

7 rows in set (0.01 sec)

person James    schedule 28.07.2011    source източник
comment
Къде в EXPLAIN PLAN се казва, че е тясното място за вашите обезпокоителни запитвания?   -  person Thorbjørn Ravn Andersen    schedule 28.07.2011
comment
така че планът ми е да поддържам отделна таблица с най-новите 100 000 записа и да изпълнявам заявките срещу това - звучи като лоша идея.   -  person Mitch Wheat    schedule 28.07.2011
comment
проверете какво трябва да може да достави вашият хардуер; проверете дали имате подходящите индекси.   -  person Mitch Wheat    schedule 28.07.2011
comment
Все още има много неща, които можете да направите. Не споменахте индексиране или оптимизиране на заявки (заедно с това, което @Thorbjørn Ravn Andersen спомена). Какво направихте, за да профилирате и идентифицирате тесните места? Освен това каква база данни използвате?   -  person Thomas Owens    schedule 28.07.2011
comment
jprofiler включва ли времето, необходимо за изпращане на заявката до сървъра и получаване на всички резултати? Те обикновено не се вземат предвид, когато се разглежда продължителността на заявка в сървъра на базата данни. Ако е така, тогава може да се наложи да отрежете набора от резултати до това, което е необходимо за показване.   -  person Chris Nava    schedule 28.07.2011
comment
@Jame не забравяйте да приемете отговор, моля.   -  person Amir Raminfar    schedule 02.08.2011
comment
@Amir Не съм забравил. Имам доста предложения и трябва да обмисля различни варианти. Също така е доста трудно да се намери „отговор“ на този въпрос   -  person James    schedule 02.08.2011
comment
Страхотен. Не бързайте тогава. Моля, споделете го, когато го намерите, защото ме интересува.   -  person Amir Raminfar    schedule 02.08.2011


Отговори (7)


Обмислянето на промяна на дизайна като тази не е добър знак - обзалагам се, че все още имате много производителност, която да изстискате с помощта на EXPLAIN, коригиране на db променливи и подобряване на индексите и заявките. Но вероятно сте преминали точката, в която „опитването на неща“ работи много добре. Това е възможност да научите как да интерпретирате анализите и регистрационните файлове и да използвате наученото за конкретни подобрения на индекси и заявки.

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

Коя е най-нелепата песимизация, която сте виждали?

person dkretz    schedule 28.07.2011

  1. Уверете се, че всички ваши индекси са оптимизирани. Използвайте explain в заявката, за да видите дали тя използва вашите индекси ефективно.
  2. Ако правите тежки съединения, тогава започнете да мислите за това изчисление в java.
  3. Помислете за използване на други DB като NoSQL. Може да сте в състояние да извършите предварителна обработка и да поставите данни в Memcache, за да ви помогне малко.
person Amir Raminfar    schedule 28.07.2011
comment
Съгласен. Индексирането не се споменава и предлага огромни подобрения в производителността на големи маси: stackoverflow.com/questions/1108/ - person Jean Logeart; 28.07.2011
comment
Промяната на DB всъщност не е опция за всеки. - person abhi; 28.07.2011
comment
Не казвах промяна на БД. Казвах да използвате друга база данни за временно кеширане на данни. - person Amir Raminfar; 28.07.2011
comment
Денормализацията също може да бъде опция. - person Frank Farmer; 28.07.2011
comment
@Frank, съгласен съм. Използването на denorm в db или някакъв друг слой може да е добро, ако има много съединяване. - person Amir Raminfar; 29.07.2011

Е, ако сте оптимизирали базата данни и заявките, бих казал, че вместо да нарязвате данните, следващата стъпка е да погледнете:

а) конфигурацията на mysql и се уверете, че се възползва максимално от хардуера

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

person Jaydee    schedule 28.07.2011

Вместо да създавате отделна таблица за най-новите резултати, помислете за разделянето на таблицата. MySQL има тази функция вградена от версия 5.1


Само за да стане ясно: не казвам, че това е решението за вашите проблеми. Само едно нещо можете да опитате

person Mchl    schedule 28.07.2011

Бих започнал, като се опитам да оптимизирам таблиците/индексите/заявките, преди да предприема някоя от мерките, които изброихте. Задълбавали ли сте в лошо изпълняващите се заявки до степен, в която сте абсолютно убедени, че сте достигнали границата на възможностите на вашата RDBMS?

Редактиране: ако наистина сте правилно оптимизирани, но все още имате проблеми, помислете за създаване на Материализиран изглед за точните данни, от които се нуждаете. Това може или не може да е добра идея въз основа на повече фактори, отколкото сте предоставили, но бих го поставил в горната част на списъка с неща, които трябва да се вземат предвид.

person Phil Sandler    schedule 28.07.2011

Търсенето в последните 100 000 записа трябва да е страшно бързо, определено имаш проблеми с индексите. Използвайте EXPLAIN и го поправете.

person Karoly Horvath    schedule 28.07.2011
comment
Само за да бъде ясно, заявките, които отнемат 300 милисекунди, са срещу приблизително 9 милиона в момента. Идеята ми беше да намаля 9-те милиона до 100 000, за да стане по-бързо - person James; 28.07.2011
comment
размерът няма значение, стига да имате правилните индекси там. ако търсите само в последните 100 000, няма особено значение, че там има 8 900 000 други записа. Ако все още се съмнявате, моля, публикувайте вашето запитване и схема на DB. - person Karoly Horvath; 28.07.2011

Разбирам, че сега се отдалечавам от това, за което беше предназначен въпросът, така че може би трябва да направя друг. Проблемът ми е, че EXPLAIN казва, че заявката отнема 10 ms, а не 300 ms, което jprofiler отчита.

Тогава вашият проблем (и решение) трябва да е в java, нали?

person dkretz    schedule 28.07.2011