Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ

Մեզ ծանոթ բոլոր հավելվածների բնորոշ սցենարներից մեկը տվյալներ որոնելն է ըստ որոշակի չափանիշների և ցուցադրել դրանք հեշտ ընթեռնելի ձևով: Կարող են լինել նաև տեսակավորման, խմբավորման և էջավորման լրացուցիչ տարբերակներ: Խնդիրը, տեսականորեն, աննշան է, բայց այն լուծելիս շատ մշակողներ թույլ են տալիս մի շարք սխալներ, որոնք հետագայում հանգեցնում են արտադրողականության նվազմանը: Փորձենք դիտարկել այս խնդրի լուծման տարբեր տարբերակներ և առաջարկություններ ձևակերպել ամենաարդյունավետ իրականացման ընտրության համար:

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ

Էջավորման տարբերակ թիվ 1

Ամենապարզ տարբերակը, որը գալիս է մտքին, որոնման արդյունքների էջ առ էջ ցուցադրումն է իր ամենադասական տեսքով:

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ
Ենթադրենք, որ ձեր հավելվածն օգտագործում է հարաբերական տվյալների բազա: Այս դեպքում, այս ձևով տեղեկատվություն ցուցադրելու համար ձեզ հարկավոր է գործարկել երկու SQL հարցում.

  • Ստացեք տողեր ընթացիկ էջի համար:
  • Հաշվարկեք որոնման չափանիշներին համապատասխան տողերի ընդհանուր քանակը. սա անհրաժեշտ է էջերը ցուցադրելու համար:

Եկեք նայենք առաջին հարցումին՝ օգտագործելով փորձնական MS SQL տվյալների բազան որպես օրինակ AdventureWorks 2016 սերվերի համար. Այդ նպատակով մենք կօգտագործենք Sales.SalesOrderHeader աղյուսակը.

SELECT * FROM Sales.SalesOrderHeader
ORDER BY OrderDate DESC
OFFSET 0 ROWS
FETCH NEXT 50 ROWS ONLY

Վերոնշյալ հարցումը կվերադարձնի ցուցակից առաջին 50 պատվերները՝ դասավորված ըստ ավելացման նվազման ամսաթվի, այլ կերպ ասած՝ 50 ամենավերջին պատվերները:

Այն արագ է աշխատում թեստային բազայի վրա, բայց եկեք նայենք կատարման պլանին և I/O վիճակագրությանը.

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ

Table 'SalesOrderHeader'. Scan count 1, logical reads 698, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Դուք կարող եք ստանալ I/O վիճակագրություն յուրաքանչյուր հարցման համար՝ գործարկելով SET STATISTICS IO ON հրամանը հարցման գործարկման ժամանակում:

Ինչպես երևում է կատարման պլանից, ռեսուրսների ինտենսիվ տարբերակն այն է, որ աղբյուրի աղյուսակի բոլոր տողերը դասավորվեն ըստ ավելացված ամսաթվի: Եվ խնդիրն այն է, որ որքան շատ տողեր հայտնվեն աղյուսակում, այնքան «դժվար» կլինի տեսակավորումը։ Գործնականում պետք է խուսափել նման իրավիճակներից, ուստի եկեք ինդեքս ավելացնենք ավելացման ամսաթվին և տեսնենք՝ փոխվել է արդյոք ռեսուրսների սպառումը.

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ

Table 'SalesOrderHeader'. Scan count 1, logical reads 165, physical reads 0, read-ahead reads 5, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Ակնհայտ է, որ այն շատ ավելի լավ է դարձել: Բայց արդյո՞ք բոլոր խնդիրները լուծված են։ Եկեք փոխենք հարցումը պատվերների որոնման համար, որտեղ ապրանքների ընդհանուր արժեքը գերազանցում է $100-ը.

SELECT * FROM Sales.SalesOrderHeader
WHERE SubTotal > 100
ORDER BY OrderDate DESC
OFFSET 0 ROWS
FETCH NEXT 50 ROWS ONLY

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ

Table 'SalesOrderHeader'. Scan count 1, logical reads 1081, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Մենք ունենք զվարճալի իրավիճակ. հարցման պլանը շատ ավելի վատ չէ, քան նախորդը, բայց տրամաբանական ընթերցումների իրական թիվը գրեթե երկու անգամ ավելի է, քան սեղանի ամբողջական սկանավորումը: Ելք կա. եթե արդեն գոյություն ունեցող ինդեքսից կազմենք կոմպոզիտային ինդեքս և որպես երկրորդ դաշտ ավելացնենք ապրանքների ընդհանուր գինը, ապա կրկին կստանանք 165 տրամաբանական ընթերցումներ.

CREATE INDEX IX_SalesOrderHeader_OrderDate_SubTotal on Sales.SalesOrderHeader(OrderDate, SubTotal);

Օրինակների այս շարքը կարելի է երկար շարունակել, բայց երկու հիմնական մտքերը, որոնք ես ուզում եմ արտահայտել այստեղ են.

  • Որոնման հարցումին ցանկացած նոր չափանիշ կամ տեսակավորման կարգ ավելացնելը կարող է զգալի ազդեցություն ունենալ որոնման հարցման արագության վրա:
  • Բայց եթե մենք պետք է հանենք տվյալների միայն մի մասը, և ոչ բոլոր արդյունքները, որոնք համապատասխանում են որոնման տերմիններին, ապա նման հարցումը օպտիմալացնելու բազմաթիվ եղանակներ կան:

Այժմ անցնենք հենց սկզբում նշված երկրորդ հարցմանը, որը հաշվում է որոնման չափանիշը բավարարող գրառումների քանակը։ Վերցնենք նույն օրինակը՝ 100 դոլարից ավելի պատվերների որոնում.

SELECT COUNT(1) FROM Sales.SalesOrderHeader
WHERE SubTotal > 100

Հաշվի առնելով վերը նշված կոմպոզիտային ինդեքսը, մենք ստանում ենք.

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ

Table 'SalesOrderHeader'. Scan count 1, logical reads 698, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Այն փաստը, որ հարցումն անցնում է ամբողջ ինդեքսով, զարմանալի չէ, քանի որ SubTotal դաշտը առաջին տեղում չէ, ուստի հարցումը չի կարող այն օգտագործել։ Խնդիրը լուծվում է SubTotal դաշտում ևս մեկ ինդեքս ավելացնելով, և արդյունքում տալիս է ընդամենը 48 տրամաբանական ընթերցում։

Դուք կարող եք ևս մի քանի օրինակ բերել քանակները հաշվելու հարցումների, բայց էությունը մնում է նույնը. տվյալներ ստանալը և ընդհանուր գումարը հաշվելը երկու սկզբունքորեն տարբեր պահանջներ են, և յուրաքանչյուրն օպտիմալացման համար պահանջում է իր միջոցները: Ընդհանուր առմամբ, դուք չեք կարողանա գտնել ինդեքսների համակցություն, որը հավասարապես լավ է աշխատում երկու հարցումների համար:

Համապատասխանաբար, կարևոր պահանջներից մեկը, որը պետք է հստակեցվի նման որոնման լուծում մշակելիս, այն է, թե արդյոք բիզնեսի համար իսկապես կարևոր է տեսնել հայտնաբերված օբյեկտների ընդհանուր թիվը: Հաճախ է պատահում, որ ոչ։ Իսկ կոնկրետ էջերի համարներով նավիգացիան, իմ կարծիքով, շատ նեղ շրջանակով լուծում է, քանի որ էջավորման սցենարների մեծամասնությունը նման է «գնալ հաջորդ էջին»:

Էջավորման տարբերակ թիվ 2

Ենթադրենք, օգտատերերին չի հետաքրքրում հայտնաբերված օբյեկտների ընդհանուր թիվը իմանալը: Փորձենք պարզեցնել որոնման էջը.

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ
Իրականում միակ բանը, որ փոխվել է, այն է, որ չկա որևէ կերպ նավարկելու կոնկրետ էջերի համարներ, և այժմ այս աղյուսակը կարիք չունի իմանալու, թե որքան կարող է լինել այն ցուցադրելու համար: Բայց հարց է առաջանում. ինչպե՞ս է աղյուսակը իմանում, արդյոք կա՞ տվյալներ հաջորդ էջի համար («Հաջորդ» հղումը ճիշտ ցուցադրելու համար):

Պատասխանը շատ պարզ է. տվյալների բազայից կարող եք կարդալ մեկ գրառում ավելի, քան անհրաժեշտ է ցուցադրման համար, և այս «լրացուցիչ» գրառման առկայությունը ցույց կտա, թե արդյոք կա հաջորդ մասը: Այս կերպ դուք պետք է կատարեք միայն մեկ հարցում՝ տվյալների մեկ էջ ստանալու համար, ինչը զգալիորեն բարելավում է կատարողականությունը և հեշտացնում է նման ֆունկցիոնալությունը: Իմ պրակտիկայում եղել է դեպք, երբ գրառումների ընդհանուր թիվը հաշվելուց հրաժարվելը 4-5 անգամ արագացրել է արդյունքների առաքումը։

Այս մոտեցման համար կան օգտատիրոջ միջերեսի մի քանի տարբերակներ՝ «հետ» և «առաջ» հրամաններ, ինչպես վերը նշված օրինակում, «բեռնել ավելին» կոճակը, որը պարզապես ավելացնում է նոր հատված ցուցադրվող արդյունքներին՝ «անսահման ոլորում», որն աշխատում է։ «ավելին բեռնելու» սկզբունքով, սակայն հաջորդ մասը ստանալու ազդանշանն այն է, որ օգտագործողը ոլորել բոլոր ցուցադրված արդյունքները մինչև վերջ: Ինչ էլ որ լինի տեսողական լուծումը, տվյալների նմուշառման սկզբունքը մնում է նույնը:

Էջավորման իրականացման նրբությունները

Վերևում տրված բոլոր հարցման օրինակները օգտագործում են «օֆսեթ + հաշվարկ» մոտեցումը, երբ հարցումն ինքնին նշում է, թե որ հաջորդականությամբ են արդյունքի տողերը և քանի տող պետք է վերադարձվեն: Նախ, եկեք տեսնենք, թե ինչպես լավագույնս կազմակերպել պարամետրերի փոխանցումն այս դեպքում: Գործնականում ես հանդիպել եմ մի քանի մեթոդների.

  • Պահանջվող էջի սերիական համարը (pageIndex), էջի չափը (էջի չափը):
  • Առաջին վերադարձվող ռեկորդի սերիական համարը (startIndex), արդյունքում գրանցումների առավելագույն քանակը (հաշվում):
  • Առաջին վերադարձվող գրառման հաջորդականության համարը (startIndex), վերադարձվող վերջին գրառման հաջորդականության համարը (endIndex):

Առաջին հայացքից կարող է թվալ, որ սա այնքան տարրական է, որ տարբերություն չկա։ Բայց դա այդպես չէ. ամենահարմար և ունիվերսալ տարբերակը երկրորդն է (startIndex, count): Դրա համար կան մի քանի պատճառներ.

  • Վերևում տրված +1 մուտքի սրբագրման մոտեցման համար pageIndex-ով և pageSize-ով առաջին տարբերակը չափազանց անհարմար է: Օրինակ, մենք ցանկանում ենք ցուցադրել 50 գրառում մեկ էջում: Համաձայն վերը նշված ալգորիթմի, անհրաժեշտ է կարդալ մեկ գրառում ավելի, քան անհրաժեշտ է: Եթե ​​այս «+1»-ը չի ներդրվում սերվերում, ապա ստացվում է, որ առաջին էջի համար մենք պետք է պահանջենք գրառումներ 1-ից մինչև 51, երկրորդի համար՝ 51-ից մինչև 101 և այլն: Եթե ​​նշեք էջի չափը 51 և ավելացնեք pageIndex-ը, ապա երկրորդ էջը կվերադառնա 52-ից մինչև 102 և այլն: Համապատասխանաբար, առաջին տարբերակում հաջորդ էջ գնալու կոճակը պատշաճ կերպով գործադրելու միակ միջոցը սերվերի կողմից «լրացուցիչ» տողը սրբագրելն է, ինչը շատ անուղղակի նրբերանգ կլինի:
  • Երրորդ տարբերակն ընդհանրապես իմաստ չունի, քանի որ տվյալների բազաների մեծ մասում հարցումներ գործարկելու համար դուք դեռ պետք է անցնեք ոչ թե վերջին գրառման ինդեքսը, այլ հաշվարկը: StartIndex-ից endIndex-ից հանելը կարող է պարզ թվաբանական գործողություն լինել, բայց այստեղ ավելորդ է:

Այժմ մենք պետք է նկարագրենք «օֆսեթ + քանակի» միջոցով paging-ի իրականացման թերությունները.

  • Յուրաքանչյուր հաջորդ էջի առբերումը կլինի ավելի թանկ և դանդաղ, քան նախորդը, քանի որ տվյալների բազան դեռ պետք է անցնի բոլոր գրառումները «ի սկզբանե» ըստ որոնման և տեսակավորման չափանիշների, այնուհետև կանգ առնի ցանկալի հատվածի վրա:
  • Ոչ բոլոր DBMS-ները կարող են աջակցել այս մոտեցումը:

Կան այլընտրանքներ, բայց դրանք նույնպես անկատար են։ Այս մոտեցումներից առաջինը կոչվում է «keyset paging» կամ «որոնման մեթոդ» և հետևյալն է. բաժին ստանալուց հետո կարող եք հիշել դաշտի արժեքները էջի վերջին գրառումում, այնուհետև օգտագործել դրանք ստանալու համար: հաջորդ հատվածը. Օրինակ, մենք կատարեցինք հետևյալ հարցումը.

SELECT * FROM Sales.SalesOrderHeader
ORDER BY OrderDate DESC
OFFSET 0 ROWS
FETCH NEXT 50 ROWS ONLY

Եվ վերջին գրառումում մենք ստացանք պատվերի ամսաթիվը «2014-06-29»: Այնուհետև հաջորդ էջը ստանալու համար կարող եք փորձել անել սա.

SELECT * FROM Sales.SalesOrderHeader
WHERE OrderDate < '2014-06-29'
ORDER BY OrderDate DESC
OFFSET 0 ROWS
FETCH NEXT 50 ROWS ONLY

Խնդիրն այն է, որ OrderDate-ը ոչ եզակի դաշտ է, և վերը նշված պայմանը, հավանաբար, բաց կթողնի պահանջվող շատ տողեր: Այս հարցմանը միանշանակություն ավելացնելու համար դուք պետք է եզակի դաշտ ավելացնեք պայմանին (ենթադրենք, որ 75074-ը հիմնական բանալու վերջին արժեքն է առաջին մասից).

SELECT * FROM Sales.SalesOrderHeader
WHERE (OrderDate = '2014-06-29' AND SalesOrderID < 75074)
   OR (OrderDate < '2014-06-29')
ORDER BY OrderDate DESC, SalesOrderID DESC
OFFSET 0 ROWS
FETCH NEXT 50 ROWS ONLY

Այս տարբերակը ճիշտ կաշխատի, բայց ընդհանուր առմամբ դժվար կլինի օպտիմալացնել, քանի որ պայմանը պարունակում է OR օպերատոր: Եթե ​​առաջնային բանալու արժեքը մեծանում է OrderDate-ի մեծացման հետ, ապա պայմանը կարելի է պարզեցնել՝ թողնելով միայն զտիչ SalesOrderID-ով: Բայց եթե չկա խիստ հարաբերակցություն հիմնական բանալի արժեքների և այն դաշտի միջև, որով դասավորված է արդյունքը, ապա այս ԿԱՄ-ը հնարավոր չէ խուսափել DBMS-ների մեծ մասում: Բացառություն, որի մասին ես տեղյակ եմ, PostgreSQL-ն է, որն ամբողջությամբ աջակցում է բազմակի համեմատությանը, և վերը նշված պայմանը կարող է գրվել որպես «WHERE (OrderDate, SalesOrderID) < ('2014-06-29', 75074)»: Հաշվի առնելով այս երկու դաշտերի կոմպոզիտային բանալին, նման հարցումը պետք է բավականին հեշտ լինի:

Երկրորդ այլընտրանքային մոտեցումը կարելի է գտնել, օրինակ, մեջ ElasticSearch ոլորման API կամ Cosmos DB — երբ հարցումը, բացի տվյալներից, վերադարձնում է հատուկ նույնացուցիչ, որով կարող եք ստանալ տվյալների հաջորդ մասը: Եթե ​​այս նույնացուցիչն ունի անսահմանափակ կյանքի ժամկետ (ինչպես Comsos DB-ում), ապա սա հիանալի միջոց է էջերի միջև հաջորդական անցումով էջավորումն իրականացնելու համար (վերը նշված թիվ 2 տարբերակ): Դրա հնարավոր թերությունները. այն չի ապահովվում բոլոր DBMS-ներում; ստացված հաջորդ հատվածի նույնացուցիչը կարող է ունենալ սահմանափակ ժամկետ, որն ընդհանուր առմամբ հարմար չէ օգտատիրոջ փոխազդեցության իրականացման համար (օրինակ՝ ElasticSearch ոլորման API):

Համալիր ֆիլտրում

Եկեք ավելի բարդացնենք առաջադրանքը: Ենթադրենք, կա պահանջ՝ իրականացնել այսպես կոչված երեսպատված որոնում, որը շատ ծանոթ է բոլորին օնլայն խանութներից։ Պատվերների աղյուսակի վրա հիմնված վերը նշված օրինակներն այս դեպքում այնքան էլ պատկերավոր չեն, ուստի եկեք AdventureWorks տվյալների բազայից անցնենք Ապրանքի աղյուսակին.

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ
Ո՞րն է բազմակողմանի որոնման գաղափարը: Փաստն այն է, որ յուրաքանչյուր ֆիլտրի տարրի համար ցուցադրվում է այս չափանիշին համապատասխանող գրառումների քանակը հաշվի առնելով բոլոր մյուս կատեգորիաներում ընտրված զտիչները.

Օրինակ, եթե այս օրինակում ընտրենք Հեծանիվների կատեգորիան և սև գույնը, աղյուսակը ցույց կտա միայն սև հեծանիվները, բայց.

  • Կատեգորիաներ խմբի յուրաքանչյուր չափանիշի համար այդ կատեգորիայի ապրանքների քանակը կցուցադրվի սև գույնով:
  • «Գույներ» խմբի յուրաքանչյուր չափանիշի համար կցուցադրվի այս գույնի հեծանիվների քանակը:

Ահա այսպիսի պայմանների արդյունքի արդյունքի օրինակ.

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ
Եթե ​​դուք նաև ստուգեք «Հագուստ» կատեգորիան, աղյուսակը ցույց կտա նաև սև հագուստները, որոնք առկա են պահեստում: Նոր պայմանների համաձայն կվերահաշվարկվի նաև «Գույն» բաժնում սև ապրանքների քանակը, միայն «Կատեգորիաներ» բաժնում ոչինչ չի փոխվի... Հուսով եմ, որ այս օրինակները բավարար են սովորական երեսպատման որոնման ալգորիթմը հասկանալու համար։

Հիմա եկեք պատկերացնենք, թե ինչպես դա կարող է իրականացվել հարաբերական հիմքի վրա։ Չափորոշիչների յուրաքանչյուր խումբ, ինչպիսիք են Կատեգորիան և Գույնը, կպահանջեն առանձին հարցում.

SELECT pc.ProductCategoryID, pc.Name, COUNT(1) FROM Production.Product p
  INNER JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID
  INNER JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID
WHERE p.Color = 'Black'
GROUP BY pc.ProductCategoryID, pc.Name
ORDER BY COUNT(1) DESC

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ

SELECT Color, COUNT(1) FROM Production.Product p
  INNER JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID
WHERE ps.ProductCategoryID = 1 --Bikes
GROUP BY Color
ORDER BY COUNT(1) DESC

Որոնման արդյունքների թողարկման և կատարողականի խնդիրներ
Ի՞նչն է սխալ այս լուծման մեջ: Դա շատ պարզ է՝ լավ չի չափվում: Յուրաքանչյուր զտիչ բաժին պահանջում է առանձին հարցում՝ քանակները հաշվարկելու համար, և այս հարցումներն ամենահեշտը չեն: Առցանց խանութներում որոշ կատեգորիաներ կարող են ունենալ մի քանի տասնյակ ֆիլտրի բաժիններ, ինչը կարող է լուրջ աշխատանքի խնդիր լինել:

Սովորաբար այս հայտարարություններից հետո ինձ որոշ լուծումներ են առաջարկում, մասնավորապես.

  • Միավորել բոլոր քանակական հաշվարկները մեկ հարցման մեջ: Տեխնիկապես դա հնարավոր է օգտագործելով UNION հիմնաբառը, բայց դա շատ չի օգնի կատարմանը. տվյալների բազան դեռ պետք է կատարի բեկորներից յուրաքանչյուրը զրոյից:
  • Քեշի քանակները. Սա ինձ առաջարկվում է գրեթե ամեն անգամ, երբ ես նկարագրում եմ որևէ խնդիր: Զգուշացումն այն է, որ դա ընդհանրապես անհնար է: Ենթադրենք, մենք ունենք 10 «երեսակ», որոնցից յուրաքանչյուրն ունի 5 արժեք։ Սա շատ «համեստ» իրավիճակ է՝ համեմատած այն ամենի հետ, ինչ կարելի է տեսնել նույն առցանց խանութներում։ Մեկ երեսակային տարրի ընտրությունը ազդում է 9 մյուսների քանակների վրա, այլ կերպ ասած՝ չափորոշիչների յուրաքանչյուր համակցության համար մեծությունները կարող են տարբեր լինել։ Մեր օրինակում կան ընդհանուր առմամբ 50 չափորոշիչներ, որոնք օգտվողը կարող է ընտրել, հետևաբար, կլինեն 250 հնարավոր համակցություններ: Չկա բավարար հիշողություն կամ ժամանակ՝ տվյալների նման զանգված լրացնելու համար: Այստեղ դուք կարող եք առարկել և ասել, որ ոչ բոլոր համակցություններն են իրական, և օգտատերը հազվադեպ է ընտրում 5-10 չափանիշից ավելի: Այո, հնարավոր է անել ծույլ բեռնում և քեշավորել միայն այն, ինչ երբևէ ընտրվել է, բայց որքան շատ լինեն ընտրանքները, այնքան ավելի քիչ արդյունավետ կլինի այդպիսի քեշը և ավելի նկատելի կլինեն արձագանքման ժամանակի խնդիրները (հատկապես, եթե տվյալների հավաքածուն պարբերաբար փոխվում է):

Բարեբախտաբար, նման խնդիրը վաղուց ունի բավականին արդյունավետ լուծումներ, որոնք կանխատեսելիորեն աշխատում են մեծ ծավալի տվյալների վրա: Այս տարբերակներից որևէ մեկի համար իմաստ ունի երեսակների վերահաշվարկը և արդյունքների էջը ստանալը բաժանել սերվերին երկու զուգահեռ զանգերի և օգտատիրոջ միջերեսը կազմակերպել այնպես, որ երեսներով տվյալների բեռնումը «չխանգարի» ցուցադրմանը: Որոնման արդյունքները.

  • Հնարավորինս հազվադեպ կոչեք «կողմերի» ամբողջական վերահաշվարկ: Օրինակ, մի վերահաշվարկեք ամեն ինչ ամեն անգամ, երբ որոնման չափանիշները փոխվում են, այլ փոխարենը գտեք ընթացիկ պայմաններին համապատասխանող արդյունքների ընդհանուր թիվը և հուշեք օգտվողին ցույց տալ դրանք. «Գտնվել է 1425 գրառում, ցույց տալ? Օգտագործողը կարող է կամ շարունակել փոխել որոնման պայմանները կամ սեղմել «ցուցադրել» կոճակը: Միայն երկրորդ դեպքում կկատարվեն արդյունքներ ստանալու և քանակների վերահաշվարկի բոլոր հարցումները բոլոր «առանցքներով»: Այս դեպքում, ինչպես հեշտությամբ կարող եք տեսնել, դուք ստիպված կլինեք գործ ունենալ արդյունքների ընդհանուր քանակի և դրա օպտիմալացման խնդրանքի հետ: Այս մեթոդը կարելի է գտնել բազմաթիվ փոքր առցանց խանութներում: Ակնհայտ է, որ սա համադարման չէ այս խնդրի համար, բայց պարզ դեպքերում դա կարող է լավ փոխզիջում լինել:
  • Օգտագործեք որոնման համակարգերը՝ արդյունքներ գտնելու և երեսակները հաշվելու համար, ինչպիսիք են Solr, ElasticSearch, Sphinx և այլն: Դրանք բոլորը նախագծված են «երեսներ» կառուցելու համար և դա անում են բավականին արդյունավետ՝ շրջված ինդեքսի շնորհիվ: Ինչպես են աշխատում որոնման համակարգերը, ինչու են նման դեպքերում դրանք ավելի արդյունավետ, քան ընդհանուր նշանակության տվյալների բազաները, ինչ պրակտիկաներ և որոգայթներ կան, սա առանձին հոդվածի թեմա է: Այստեղ ես կցանկանայի ձեր ուշադրությունը հրավիրել այն փաստի վրա, որ որոնման համակարգը չի կարող փոխարինել հիմնական տվյալների պահպանմանը, այն օգտագործվում է որպես հավելում. Որոնողական համակարգը սովորաբար փոխազդում է միայն որոնման համակարգի հետ և չի մուտք գործում հիմնական տվյալների բազա: Այստեղ ամենակարևոր կետերից մեկն այն է, թե ինչպես կարելի է հուսալիորեն կազմակերպել այս համաժամացումը: Ամեն ինչ կախված է «արձագանքման ժամանակի» պահանջներից։ Եթե ​​հիմնական տվյալների բազայի փոփոխության և որոնման մեջ դրա «դրսևորման» միջև ընկած ժամանակահատվածը կարևոր չէ, կարող եք ստեղծել ծառայություն, որը մի քանի րոպեն մեկ որոնում է վերջերս փոփոխված գրառումները և ինդեքսավորում դրանք: Եթե ​​ցանկանում եք հնարավորինս կարճ արձագանքման ժամանակ, կարող եք իրականացնել նման բան գործարքային ելքային արկղ թարմացումներ ուղարկել որոնման ծառայությանը:

Արդյունքները

  1. Սերվերի կողմից էջավորման իրականացումը զգալի բարդություն է և իմաստ ունի միայն արագ աճող կամ պարզապես մեծ տվյալների հավաքածուների համար: Չկա բացարձակապես ճշգրիտ բաղադրատոմս, թե ինչպես գնահատել «մեծ» կամ «արագ աճող», բայց ես կհետևեի այս մոտեցմանը.
    • Եթե ​​տվյալների ամբողջական հավաքածու ստանալը, հաշվի առնելով սերվերի ժամանակը և ցանցի փոխանցումը, սովորաբար համապատասխանում է կատարողականի պահանջներին, ապա սերվերի կողմից էջավորում իրականացնելը իմաստ չունի:
    • Կարող է լինել իրավիճակ, երբ մոտ ապագայում կատարողականի հետ կապված խնդիրներ չեն սպասվում, քանի որ տվյալները քիչ են, բայց տվյալների հավաքագրումը անընդհատ աճում է։ Եթե ​​ապագայում տվյալների մի շարք այլևս չի բավարարի նախորդ կետը, ավելի լավ է անմիջապես սկսել էջավորումը:
  2. Եթե ​​բիզնեսի կողմից չկա խիստ պահանջ՝ ցուցադրելու արդյունքների ընդհանուր թիվը կամ ցուցադրելու էջերի համարները, և ձեր համակարգը չունի որոնիչ, ավելի լավ է չկիրառել այս կետերը և դիտարկել #2 տարբերակը։
  3. Եթե ​​առկա է երեսպատման որոնման հստակ պահանջ, դուք ունեք երկու տարբերակ՝ առանց կատարողականությունը զոհաբերելու.
    • Մի վերահաշվարկեք բոլոր քանակությունները ամեն անգամ, երբ որոնման չափանիշները փոխվում են:
    • Օգտագործեք որոնման համակարգեր, ինչպիսիք են Solr, ElasticSearch, Sphinx և այլն: Բայց պետք է հասկանալ, որ այն չի կարող փոխարինել հիմնական տվյալների բազային և պետք է օգտագործվի որպես հիմնական պահեստի հավելում որոնման խնդիրների լուծման համար։
  4. Նաև երեսպատման որոնման դեպքում իմաստ ունի որոնման արդյունքների էջի որոնումը և հաշվարկը բաժանել երկու զուգահեռ հարցումների: Քանակների հաշվումը կարող է ավելի երկար տևել, քան արդյունքներ ստանալը, մինչդեռ արդյունքներն ավելի կարևոր են օգտագործողի համար:
  5. Եթե ​​որոնման համար օգտագործում եք SQL տվյալների բազա, այս մասի հետ կապված ցանկացած կոդի փոփոխություն պետք է լավ փորձարկվի համապատասխան քանակի տվյալների վրա կատարման համար (գերազանցող ծավալը կենդանի տվյալների բազայում): Ցանկալի է նաև օգտագործել հարցումների կատարման ժամանակի մոնիտորինգը տվյալների բազայի բոլոր ատյաններում և հատկապես «ուղիղ» տարբերակում: Նույնիսկ եթե զարգացման փուլում ամեն ինչ լավ էր հարցման պլանների հետ, քանի որ տվյալների ծավալը մեծանում է, իրավիճակը կարող է նկատելիորեն փոխվել:

Source: www.habr.com

Добавить комментарий