Mga isyu sa output at performance ng mga resulta ng paghahanap

Ang isa sa mga tipikal na sitwasyon sa lahat ng mga application na pamilyar sa amin ay ang paghahanap ng data ayon sa ilang pamantayan at pagpapakita nito sa isang madaling basahin na form. Maaaring mayroon ding mga karagdagang opsyon para sa pag-uuri, pagpapangkat, at paging. Ang gawain ay, sa teorya, walang halaga, ngunit kapag nilutas ito, maraming mga developer ang gumawa ng ilang mga pagkakamali, na sa kalaunan ay nagiging sanhi ng pagdurusa ng pagiging produktibo. Subukan nating isaalang-alang ang iba't ibang mga opsyon para sa paglutas ng problemang ito at bumalangkas ng mga rekomendasyon para sa pagpili ng pinakamabisang pagpapatupad.

Mga isyu sa output at performance ng mga resulta ng paghahanap

Paging opsyon #1

Ang pinakasimpleng opsyon na naiisip ay isang page-by-page na pagpapakita ng mga resulta ng paghahanap sa pinaka-klasikong anyo nito.

Mga isyu sa output at performance ng mga resulta ng paghahanap
Sabihin nating gumagamit ang iyong application ng relational database. Sa kasong ito, upang ipakita ang impormasyon sa form na ito, kakailanganin mong magpatakbo ng dalawang SQL query:

  • Kumuha ng mga row para sa kasalukuyang page.
  • Kalkulahin ang kabuuang bilang ng mga linya na tumutugma sa pamantayan sa paghahanap - ito ay kinakailangan upang ipakita ang mga pahina.

Tingnan natin ang unang query gamit ang isang pagsubok na database ng MS SQL bilang isang halimbawa Pakikipagsapalaran para sa 2016 server. Para sa layuning ito gagamitin namin ang talahanayan ng Sales.SalesOrderHeader:

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

Ibabalik ng query sa itaas ang unang 50 order mula sa listahan, na pinagsunod-sunod ayon sa pababang petsa ng karagdagan, sa madaling salita, ang 50 pinakahuling mga order.

Mabilis itong tumatakbo sa test base, ngunit tingnan natin ang execution plan at I/O statistics:

Mga isyu sa output at performance ng mga resulta ng paghahanap

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.

Maaari kang makakuha ng mga istatistika ng I/O para sa bawat query sa pamamagitan ng pagpapatakbo ng utos na SET STATISTICS IO ON sa runtime ng query.

Tulad ng nakikita mo mula sa plano ng pagpapatupad, ang pinaka-masinsinang mapagkukunan na opsyon ay ang pag-uri-uriin ang lahat ng mga row ng source table ayon sa petsang idinagdag. At ang problema ay ang mas maraming mga hilera na lilitaw sa talahanayan, ang "mas mahirap" ang pag-uuri. Sa pagsasagawa, dapat na iwasan ang mga ganitong sitwasyon, kaya magdagdag tayo ng index sa petsa ng pagdaragdag at tingnan kung nagbago ang pagkonsumo ng mapagkukunan:

Mga isyu sa output at performance ng mga resulta ng paghahanap

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.

Halatang lalo itong gumanda. Ngunit nalutas ba ang lahat ng mga problema? Baguhin natin ang query upang maghanap ng mga order kung saan ang kabuuang halaga ng mga kalakal ay lumampas sa $100:

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

Mga isyu sa output at performance ng mga resulta ng paghahanap

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.

Mayroon kaming isang nakakatawang sitwasyon: ang query plan ay hindi gaanong mas masahol kaysa sa nauna, ngunit ang aktwal na bilang ng mga lohikal na pagbabasa ay halos dalawang beses na mas malaki kaysa sa isang buong pag-scan ng talahanayan. Mayroong isang paraan - kung gumawa kami ng isang pinagsama-samang index mula sa isang umiiral nang index at idagdag ang kabuuang presyo ng mga kalakal bilang pangalawang field, muli tayong makakakuha ng 165 lohikal na pagbabasa:

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

Ang serye ng mga halimbawang ito ay maaaring ipagpatuloy sa mahabang panahon, ngunit ang dalawang pangunahing kaisipan na nais kong ipahayag dito ay:

  • Ang pagdaragdag ng anumang bagong pamantayan o pagkakasunud-sunod ng pag-uuri sa isang query sa paghahanap ay maaaring magkaroon ng malaking epekto sa bilis ng query sa paghahanap.
  • Ngunit kung kailangan nating ibawas lamang ang bahagi ng data, at hindi lahat ng mga resulta na tumutugma sa mga termino para sa paghahanap, maraming paraan upang ma-optimize ang naturang query.

Ngayon ay lumipat tayo sa pangalawang query na binanggit sa pinakadulo simula - ang isa na nagbibilang ng bilang ng mga tala na nakakatugon sa pamantayan sa paghahanap. Kunin natin ang parehong halimbawa - naghahanap ng mga order na higit sa $100:

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

Dahil sa composite index na ipinahiwatig sa itaas, nakuha namin ang:

Mga isyu sa output at performance ng mga resulta ng paghahanap

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.

Ang katotohanan na ang query ay dumaan sa buong index ay hindi nakakagulat, dahil ang SubTotal field ay wala sa unang posisyon, kaya ang query ay hindi magagamit ito. Ang problema ay malulutas sa pamamagitan ng pagdaragdag ng isa pang index sa SubTotal na patlang, at bilang isang resulta ay nagbibigay lamang ito ng 48 lohikal na pagbabasa.

Maaari kang magbigay ng ilang higit pang mga halimbawa ng mga kahilingan para sa pagbibilang ng mga dami, ngunit ang esensya ay nananatiling pareho: ang pagtanggap ng isang piraso ng data at pagbibilang ng kabuuang halaga ay dalawang pangunahing magkaibang kahilingan, at bawat isa ay nangangailangan ng sarili nitong mga hakbang para sa pag-optimize. Sa pangkalahatan, hindi ka makakahanap ng kumbinasyon ng mga index na pantay na gumagana para sa parehong mga query.

Alinsunod dito, ang isa sa mga mahalagang kinakailangan na dapat na linawin kapag bumubuo ng naturang solusyon sa paghahanap ay kung talagang mahalaga para sa isang negosyo na makita ang kabuuang bilang ng mga bagay na natagpuan. Madalas mangyari na hindi. At ang pag-navigate sa pamamagitan ng mga partikular na numero ng pahina, sa palagay ko, ay isang solusyon na may napakakitid na saklaw, dahil ang karamihan sa mga senaryo ng paging ay mukhang "pumunta sa susunod na pahina."

Paging opsyon #2

Ipagpalagay natin na ang mga user ay walang pakialam sa pag-alam sa kabuuang bilang ng mga bagay na natagpuan. Subukan nating gawing simple ang pahina ng paghahanap:

Mga isyu sa output at performance ng mga resulta ng paghahanap
Sa katunayan, ang tanging bagay na nagbago ay walang paraan upang mag-navigate sa mga partikular na numero ng pahina, at ngayon ay hindi na kailangang malaman ng talahanayang ito kung ilan ang maaaring mayroon upang maipakita ito. Ngunit lumitaw ang tanong - paano malalaman ng talahanayan kung mayroong data para sa susunod na pahina (upang maipakita nang tama ang link na "Susunod")?

Ang sagot ay napaka-simple: maaari kang magbasa mula sa database ng isa pang tala kaysa sa kinakailangan para sa pagpapakita, at ang pagkakaroon ng "karagdagang" na tala na ito ay magpapakita kung mayroong susunod na bahagi. Sa ganitong paraan, kailangan mo lamang magpatakbo ng isang kahilingan upang makakuha ng isang pahina ng data, na makabuluhang nagpapabuti sa pagganap at ginagawang mas madaling suportahan ang naturang pagpapagana. Sa aking pagsasanay, mayroong isang kaso kapag ang pagtanggi na bilangin ang kabuuang bilang ng mga talaan ay pinabilis ang paghahatid ng mga resulta ng 4-5 beses.

Mayroong ilang mga pagpipilian sa interface ng gumagamit para sa diskarteng ito: "pabalik" at "pasulong" na mga utos, tulad ng sa halimbawa sa itaas, isang pindutan ng "mag-load ng higit pa", na nagdaragdag lamang ng isang bagong bahagi sa mga ipinapakitang resulta, "walang katapusan na pag-scroll", na gumagana. sa prinsipyo ng "mag-load ng higit pa" ", ngunit ang senyas upang makuha ang susunod na bahagi ay para sa user na mag-scroll sa lahat ng ipinapakitang resulta hanggang sa dulo. Anuman ang visual na solusyon, ang prinsipyo ng data sampling ay nananatiling pareho.

Mga Nuances ng pagpapatupad ng paging

Ang lahat ng mga halimbawa ng query na ibinigay sa itaas ay gumagamit ng "offset + count" na diskarte, kapag ang query mismo ay tumutukoy kung aling pagkakasunud-sunod ang mga row ng resulta at kung gaano karaming mga row ang kailangang ibalik. Una, tingnan natin kung paano pinakamahusay na ayusin ang pagpasa ng parameter sa kasong ito. Sa pagsasagawa, nakatagpo ako ng ilang mga pamamaraan:

  • Ang serial number ng hiniling na pahina (pageIndex), laki ng pahina (pageSize).
  • Ang serial number ng unang record na ibabalik (startIndex), ang maximum na bilang ng mga record sa resulta (count).
  • Ang sequence number ng unang record na ibabalik (startIndex), ang sequence number ng huling record na ibabalik (endIndex).

Sa unang tingin ay tila ito ay napaka elementarya na walang pagkakaiba. Ngunit hindi ito ganoon - ang pinaka-maginhawa at unibersal na opsyon ay ang pangalawa (startIndex, count). Mayroong ilang mga dahilan para dito:

  • Para sa +1 entry proofreading approach na ibinigay sa itaas, ang unang opsyon sa pageIndex at pageSize ay lubhang hindi maginhawa. Halimbawa, gusto naming magpakita ng 50 post sa bawat pahina. Ayon sa algorithm sa itaas, kailangan mong magbasa ng isa pang tala kaysa kinakailangan. Kung ang "+1" na ito ay hindi ipinatupad sa server, lumalabas na para sa unang pahina dapat kaming humiling ng mga talaan mula 1 hanggang 51, para sa pangalawa - mula 51 hanggang 101, atbp. Kung tumukoy ka ng laki ng pahina na 51 at dagdagan ang pageIndex, babalik ang pangalawang pahina mula 52 hanggang 102, atbp. Alinsunod dito, sa unang opsyon, ang tanging paraan upang maayos na ipatupad ang isang pindutan upang pumunta sa susunod na pahina ay ang pag-proofread sa server ng "dagdag" na linya, na magiging isang napaka-implicit na nuance.
  • Ang pangatlong opsyon ay walang kabuluhan, dahil para magpatakbo ng mga query sa karamihan ng mga database, kakailanganin mo pa ring ipasa ang bilang sa halip na ang index ng huling tala. Ang pagbabawas ng startIndex mula sa endIndex ay maaaring isang simpleng operasyon ng aritmetika, ngunit ito ay kalabisan dito.

Ngayon ay dapat nating ilarawan ang mga disadvantages ng pagpapatupad ng paging sa pamamagitan ng "offset + quantity":

  • Ang pagkuha sa bawat kasunod na pahina ay magiging mas mahal at mas mabagal kaysa sa nauna, dahil ang database ay kailangan pa ring dumaan sa lahat ng mga talaan "mula sa simula" ayon sa mga pamantayan sa paghahanap at pag-uuri, at pagkatapos ay huminto sa nais na fragment.
  • Hindi lahat ng DBMS ay maaaring suportahan ang diskarteng ito.

May mga alternatibo, ngunit hindi rin sila perpekto. Ang una sa mga pamamaraang ito ay tinatawag na "keyset paging" o "paraan ng paghahanap" at ito ay ang mga sumusunod: pagkatapos makatanggap ng isang bahagi, maaari mong matandaan ang mga halaga ng field sa huling tala sa pahina, at pagkatapos ay gamitin ang mga ito upang makakuha ng ang susunod na bahagi. Halimbawa, pinatakbo namin ang sumusunod na query:

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

At sa huling talaan nakuha namin ang halaga ng petsa ng order '2014-06-29'. Pagkatapos upang makuha ang susunod na pahina maaari mong subukang gawin ito:

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

Ang problema ay ang OrderDate ay isang hindi natatanging field at ang kundisyong tinukoy sa itaas ay malamang na makaligtaan ng maraming kinakailangang row. Upang magdagdag ng hindi malabo sa query na ito, kailangan mong magdagdag ng isang natatanging field sa kundisyon (ipagpalagay na ang 75074 ay ang huling halaga ng pangunahing key mula sa unang bahagi):

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

Gagana nang tama ang opsyong ito, ngunit sa pangkalahatan ay mahirap itong i-optimize dahil naglalaman ang kundisyon ng OR operator. Kung tumataas ang halaga ng pangunahing key habang tumataas ang OrderDate, maaaring gawing simple ang kundisyon sa pamamagitan ng pag-iiwan lamang ng filter ng SalesOrderID. Ngunit kung walang mahigpit na ugnayan sa pagitan ng mga halaga ng pangunahing susi at ang patlang kung saan pinagsunod-sunod ang resulta, hindi ito maiiwasan sa OR sa karamihan ng mga DBMS. Ang isang pagbubukod na alam ko ay ang PostgreSQL, na ganap na sumusuporta sa mga paghahambing ng tuple, at ang kundisyon sa itaas ay maaaring isulat bilang "WHERE (OrderDate, SalesOrderID) < ('2014-06-29', 75074)". Dahil sa isang composite key na may dalawang field na ito, ang query na tulad nito ay dapat na medyo madali.

Ang pangalawang alternatibong diskarte ay matatagpuan, halimbawa, sa ElasticSearch scroll API o Cosmos DB β€” kapag ang isang kahilingan, bilang karagdagan sa data, ay nagbalik ng isang espesyal na identifier kung saan maaari mong makuha ang susunod na bahagi ng data. Kung ang identifier na ito ay may walang limitasyong panghabambuhay (tulad ng sa Comsos DB), ito ay isang mahusay na paraan upang ipatupad ang paging na may sequential transition sa pagitan ng mga page (option #2 na binanggit sa itaas). Ang mga posibleng disadvantage nito: hindi ito sinusuportahan sa lahat ng DBMS; ang magreresultang next-chunk identifier ay maaaring may limitadong buhay, na karaniwang hindi angkop para sa pagpapatupad ng pakikipag-ugnayan ng user (gaya ng ElasticSearch scroll API).

Kumplikadong pag-filter

Gawin pa natin ang gawain. Ipagpalagay na mayroong isang kinakailangan upang ipatupad ang tinatawag na faceted search, na pamilyar sa lahat mula sa mga online na tindahan. Ang mga halimbawa sa itaas batay sa talahanayan ng mga order ay hindi masyadong mailarawan sa kasong ito, kaya lumipat tayo sa talahanayan ng Produkto mula sa database ng AdventureWorks:

Mga isyu sa output at performance ng mga resulta ng paghahanap
Ano ang ideya sa likod ng faceted na paghahanap? Ang katotohanan ay para sa bawat elemento ng filter ang bilang ng mga tala na nakakatugon sa pamantayang ito ay ipinapakita isinasaalang-alang ang mga filter na pinili sa lahat ng iba pang mga kategorya.

Halimbawa, kung pipiliin namin ang kategoryang Mga Bike at ang kulay na Itim sa halimbawang ito, ipapakita lang ng talahanayan ang mga itim na bisikleta, ngunit:

  • Para sa bawat pamantayan sa pangkat ng Mga Kategorya, ang bilang ng mga produkto mula sa kategoryang iyon ay ipapakita sa itim.
  • Para sa bawat criterion ng pangkat na "Mga Kulay", ang bilang ng mga bisikleta ng kulay na ito ay ipapakita.

Narito ang isang halimbawa ng output ng resulta para sa mga ganitong kondisyon:

Mga isyu sa output at performance ng mga resulta ng paghahanap
Kung susuriin mo rin ang kategoryang "Damit", ang talahanayan ay magpapakita din ng mga itim na damit na nasa stock. Ang bilang ng mga itim na produkto sa seksyong "Kulay" ay muling kakalkulahin ayon sa mga bagong kundisyon, tanging sa seksyong "Mga Kategorya" ay walang magbabago... Sana ay sapat na ang mga halimbawang ito upang maunawaan ang karaniwang faceted search algorithm.

Ngayon isipin natin kung paano ito maipapatupad sa isang pamanggit na batayan. Ang bawat pangkat ng mga pamantayan, tulad ng Kategorya at Kulay, ay mangangailangan ng hiwalay na query:

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

Mga isyu sa output at performance ng mga resulta ng paghahanap

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

Mga isyu sa output at performance ng mga resulta ng paghahanap
Ano ang mali sa solusyon na ito? Ito ay napaka-simple - hindi ito mahusay na sukat. Ang bawat seksyon ng filter ay nangangailangan ng isang hiwalay na query upang makalkula ang mga dami, at ang mga query na ito ay hindi ang pinakamadali. Sa mga online na tindahan, ang ilang mga kategorya ay maaaring may ilang dosenang mga seksyon ng filter, na maaaring maging isang seryosong isyu sa pagganap.

Karaniwan pagkatapos ng mga pahayag na ito ay inaalok ako ng ilang mga solusyon, katulad:

  • Pagsamahin ang lahat ng bilang ng dami sa isang query. Sa teknikal na paraan, posible ito gamit ang keyword ng UNION, ngunit hindi ito makatutulong nang malaki sa pagganap - kailangan pa ring isagawa ng database ang bawat isa sa mga fragment mula sa simula.
  • Mga dami ng cache. Iminungkahi ito sa akin halos sa tuwing naglalarawan ako ng problema. Ang caveat ay na ito ay karaniwang imposible. Sabihin nating mayroon tayong 10 "facets", bawat isa ay may 5 value. Ito ay isang napaka "katamtaman" na sitwasyon kumpara sa kung ano ang makikita sa parehong mga online na tindahan. Ang pagpili ng isang elemento ng facet ay nakakaapekto sa mga dami sa 9 na iba pa, sa madaling salita, para sa bawat kumbinasyon ng pamantayan ang mga dami ay maaaring magkakaiba. Sa aming halimbawa, mayroong kabuuang 50 pamantayan na maaaring piliin ng user, samakatuwid, magkakaroon ng 250 posibleng kumbinasyon. Walang sapat na memorya o oras upang punan ang naturang hanay ng data. Dito maaari kang tumutol at sabihin na hindi lahat ng kumbinasyon ay totoo at ang gumagamit ay bihirang pumili ng higit sa 5-10 pamantayan. Oo, posibleng gawin ang tamad na paglo-load at pag-cache ng isang dami lamang ng kung ano ang napili, ngunit kung mas marami ang mga pagpipilian, magiging hindi gaanong mahusay ang naturang cache at mas kapansin-pansin ang mga problema sa oras ng pagtugon (lalo na kung ang regular na nagbabago ang data set) .

Sa kabutihang palad, ang gayong problema ay matagal nang may mga epektibong solusyon na predictably gumagana sa malalaking volume ng data. Para sa alinman sa mga opsyong ito, makatuwirang hatiin ang muling pagkalkula ng mga facet at pagtanggap ng pahina ng mga resulta sa dalawang magkatulad na tawag sa server at ayusin ang user interface sa paraang ang pag-load ng data sa pamamagitan ng mga facet ay "hindi makagambala" sa pagpapakita ng Mga Resulta ng Paghahanap.

  • Tumawag ng kumpletong muling pagkalkula ng "mga facet" nang bihira hangga't maaari. Halimbawa, huwag muling kalkulahin ang lahat sa tuwing nagbabago ang pamantayan sa paghahanap, ngunit sa halip ay hanapin ang kabuuang bilang ng mga resulta na tumutugma sa kasalukuyang mga kundisyon at i-prompt ang user na ipakita ang mga ito - "1425 record ang natagpuan, ipakita?" Maaaring ipagpatuloy ng user ang pagbabago ng mga termino para sa paghahanap o i-click ang button na "ipakita". Sa pangalawang kaso lamang ang lahat ng mga kahilingan para sa pagkuha ng mga resulta at muling pagkalkula ng mga dami sa lahat ng "facets" ay isasagawa. Sa kasong ito, tulad ng madali mong nakikita, kakailanganin mong harapin ang isang kahilingan upang makuha ang kabuuang bilang ng mga resulta at ang pag-optimize nito. Ang pamamaraang ito ay matatagpuan sa maraming maliliit na online na tindahan. Malinaw, hindi ito isang panlunas sa lahat para sa problemang ito, ngunit sa mga simpleng kaso maaari itong maging isang mahusay na kompromiso.
  • Gumamit ng mga search engine upang maghanap ng mga resulta at magbilang ng mga facet, gaya ng Solr, ElasticSearch, Sphinx at iba pa. Ang lahat ng mga ito ay idinisenyo upang bumuo ng mga "facets" at gawin ito nang lubos na mahusay dahil sa baligtad na index. Paano gumagana ang mga search engine, bakit sa mga ganitong pagkakataon ay mas epektibo ang mga ito kaysa sa mga database ng pangkalahatang layunin, anong mga kasanayan at pitfalls ang mayroon - ito ay isang paksa para sa isang hiwalay na artikulo. Dito nais kong iguhit ang iyong pansin sa katotohanan na ang search engine ay hindi maaaring maging kapalit para sa pangunahing pag-iimbak ng data, ginagamit ito bilang karagdagan: anumang mga pagbabago sa pangunahing database na may kaugnayan para sa paghahanap ay naka-synchronize sa index ng paghahanap; Ang search engine ay karaniwang nakikipag-ugnayan lamang sa search engine at hindi ina-access ang pangunahing database. Isa sa mga pinakamahalagang punto dito ay kung paano ayusin ang pag-synchronize na ito nang mapagkakatiwalaan. Ang lahat ay nakasalalay sa mga kinakailangan sa "oras ng reaksyon". Kung ang oras sa pagitan ng isang pagbabago sa pangunahing database at ang "manipestasyon" nito sa paghahanap ay hindi kritikal, maaari kang lumikha ng isang serbisyo na naghahanap ng mga kamakailang binagong talaan bawat ilang minuto at ini-index ang mga ito. Kung gusto mo ng pinakamaikling posibleng oras ng pagtugon, maaari mong ipatupad ang isang bagay tulad ng transactional na outbox upang magpadala ng mga update sa serbisyo sa paghahanap.

Natuklasan

  1. Ang pagpapatupad ng server-side paging ay isang makabuluhang komplikasyon at makatuwiran lamang para sa mabilis na paglaki o simpleng malalaking set ng data. Walang ganap na eksaktong recipe para sa kung paano suriin ang "malaki" o "mabilis na lumalago", ngunit susundin ko ang pamamaraang ito:
    • Kung ang pagtanggap ng kumpletong koleksyon ng data, na isinasaalang-alang ang oras ng server at paghahatid ng network, ay umaangkop sa mga kinakailangan sa pagganap nang normal, walang punto sa pagpapatupad ng paging sa gilid ng server.
    • Maaaring may sitwasyon kung saan walang inaasahang mga problema sa pagganap sa malapit na hinaharap, dahil kakaunti ang data, ngunit patuloy na lumalaki ang pagkolekta ng data. Kung ang ilang hanay ng data sa hinaharap ay maaaring hindi na matugunan ang nakaraang punto, mas mabuting simulan kaagad ang paging.
  2. Kung walang mahigpit na kinakailangan sa bahagi ng negosyo na ipakita ang kabuuang bilang ng mga resulta o ipakita ang mga numero ng pahina, at ang iyong system ay walang search engine, mas mabuting huwag ipatupad ang mga puntong ito at isaalang-alang ang opsyon #2.
  3. Kung mayroong malinaw na kinakailangan para sa faceted na paghahanap, mayroon kang dalawang opsyon nang hindi sinasakripisyo ang pagganap:
    • Huwag muling kalkulahin ang lahat ng dami sa tuwing nagbabago ang pamantayan sa paghahanap.
    • Gumamit ng mga search engine tulad ng Solr, ElasticSearch, Sphinx at iba pa. Ngunit dapat itong maunawaan na hindi ito maaaring maging kapalit para sa pangunahing database, at dapat gamitin bilang karagdagan sa pangunahing imbakan para sa paglutas ng mga problema sa paghahanap.
  4. Gayundin, sa kaso ng faceted na paghahanap, makatuwirang hatiin ang pagkuha ng pahina ng mga resulta ng paghahanap at ang pagbibilang sa dalawang magkatulad na kahilingan. Maaaring mas matagal ang pagbibilang ng mga dami kaysa sa pagkuha ng mga resulta, habang ang mga resulta ay mas mahalaga sa user.
  5. Kung gumagamit ka ng SQL database para sa paghahanap, ang anumang pagbabago sa code na nauugnay sa bahaging ito ay dapat na mahusay na masuri para sa pagganap sa naaangkop na dami ng data (higit sa volume sa live na database). Maipapayo rin na gamitin ang pagsubaybay sa oras ng pagpapatupad ng query sa lahat ng mga pagkakataon ng database, at lalo na sa "live" na isa. Kahit na maayos ang lahat sa mga query plan sa yugto ng pag-develop, habang lumalaki ang dami ng data, maaaring magbago nang kapansin-pansin ang sitwasyon.

Pinagmulan: www.habr.com

Magdagdag ng komento