portaldacalheta.pt
  • Põhiline
  • Tooteinimesed Ja Meeskonnad
  • Ux Disain
  • Protsess Ja Tööriistad
  • Andmeteadus Ja Andmebaasid
Andmeteadus Ja Andmebaasid

Sügav sukeldumine üksuse raamistiku jõudlusse, kui kasutate valikut „sisaldab“



Igapäevase töö ajal kasutan Entity Frameworkit. See on väga mugav, kuid mõnel juhul on selle jõudlus aeglane. Vaatamata sellele, et EF-i jõudluse parandamise kohta on palju häid artikleid, antakse väga häid ja kasulikke nõuandeid (nt vältige keerulisi päringuid, parameetreid Jätke vahele ja kasutage, kasutage vaateid, valige ainult vajalikud väljad jne), kuid mitte nii palju tõesti teha, kui peate kasutama keerukat Contains kahel või enamal väljal - teisisõnu, kui ühendate andmed mälu loendiga .

Probleem

Kontrollime järgmist näidet:



var localData = GetDataFromApiOrUser(); var query = from p in context.Prices join s in context.Securities on p.SecurityId equals s.SecurityId join t in localData on new { s.Ticker, p.TradedOn, p.PriceSourceId } equals new { t.Ticker, t.TradedOn, t.PriceSourceId } select p; var result = query.ToList();

Ülaltoodud kood ei tööta EF 6-s üldse ja kuigi see töötab teeb töötab EF Core'is, toimub liitumine tegelikult kohapeal - kuna minu andmebaasis on kümme miljonit kirjet, kõik neist laetakse alla ja kogu mälu kulub ära. See pole EF-i viga. On oodata. Kas poleks siiski fantastiline, kui selle lahendamiseks oleks midagi? Selles artiklis kavatsen selle jõudluse kitsaskoha kõrvaldamiseks teha mõne erineva lähenemisega katse.

Lahendus

Proovin selle saavutamiseks erinevaid viise, alustades kõige lihtsamatest ja edasijõudnutest. Igal sammul esitan koodi ja mõõdikud, nagu kulutatud aeg ja mälukasutus. Pange tähele, et katkestan võrdlusuuringute programmi käitamise, kui see töötab kauem kui kümme minutit.



Võrdlusuuringute programmi kood asub järgmises hoidla . See kasutab C #, .NET Core, EF Core ja PostgreSQL. Kasutasin masinat, millel oli Intel Core i5, 8 GB RAM ja SSD.

Testimiseks mõeldud DB skeem näeb välja selline:



Andmebaasi tabelid: hinnad, väärtpaberid ja hinnaallikad

Ainult kolm tabelit: hinnad, väärtpaberid ja hinnaallikad. Hindade tabelis on kümneid miljoneid plaate.

Variant 1. Lihtne ja naiivne

Alustamiseks proovime midagi lihtsat.



var result = new List(); using (var context = CreateContext()) { foreach (var testElement in TestData) { result.AddRange(context.Prices.Where( x => x.Security.Ticker == testElement.Ticker && x.TradedOn == testElement.TradedOn && x.PriceSourceId == testElement.PriceSourceId)); } }

Algoritm on lihtne: leidke iga testiandmete elemendi kohta andmebaasist sobiv element ja lisage see tulemuste kogu. Sellel koodil on ainult üks eelis: seda on väga lihtne rakendada. Samuti on see loetav ja hooldatav. Selle ilmne puudus on see, et see on kõige aeglasem. Kuigi kõik kolm veergu on indekseeritud, loob võrguside üldkulud siiski jõudluse kitsaskoha. Siin on mõõdikud:

Esimese katse tulemused



mis on /c/

Niisiis, suure mahu jaoks kulub umbes üks minut. Mälu tarbimine näib olevat mõistlik.

Variant 2. Naiivne paralleelselt

Proovime nüüd lisada koodile paralleelsust. Põhiidee on see, et andmebaasi paralleelsete lõimade tabamine võib parandada üldist jõudlust.



var result = new ConcurrentBag(); var partitioner = Partitioner.Create(0, TestData.Count); Parallel.ForEach(partitioner, range => { var subList = TestData.Skip(range.Item1) .Take(range.Item2 - range.Item1) .ToList(); using (var context = CreateContext()) { foreach (var testElement in subList) { var query = context.Prices.Where( x => x.Security.Ticker == testElement.Ticker && x.TradedOn == testElement.TradedOn && x.PriceSourceId == testElement.PriceSourceId); foreach (var el in query) { result.Add(el); } } } });

Huvitav on see, et väiksemate katseandmekogumite puhul töötab see lähenemisviis aeglasemalt kui esimene lahendus, kuid suuremate proovide puhul on see kiirem (antud juhul umbes 2 korda). Mälu tarbimine muutub veidi, kuid mitte oluliselt.

Teise katse tulemused



Variant 3. Mitu sisaldab

Proovime teist lähenemist:

  • Valmistage ette 3 Ticker, PriceSourceId ja Date unikaalsete väärtuste kogu.
  • Teostage päring ühe jooksu filtreerimisega, kasutades funktsiooni 3 sisaldab.
  • Kontrollige kohapeal uuesti (vt allpool).
var result = new List(); using (var context = CreateContext()) { var tickers = TestData.Select(x => x.Ticker).Distinct().ToList(); var dates = TestData.Select(x => x.TradedOn).Distinct().ToList(); var ps = TestData.Select(x => x.PriceSourceId) .Distinct().ToList(); var data = context.Prices .Where(x => tickers.Contains(x.Security.Ticker) && dates.Contains(x.TradedOn) && ps.Contains(x.PriceSourceId)) .Select(x => new { x.PriceSourceId, Price = x, Ticker = x.Security.Ticker, }) .ToList(); var lookup = data.ToLookup(x => $'{x.Ticker}, {x.Price.TradedOn}, {x.PriceSourceId}'); foreach (var el in TestData) { var key = $'{el.Ticker}, {el.TradedOn}, {el.PriceSourceId}'; result.AddRange(lookup[key].Select(x => x.Price)); } }

See lähenemine on problemaatiline. Käivitamise aeg sõltub väga andmetest. See võib hankida just nõutavad kirjed (sel juhul on see väga kiire), kuid võib-olla tagastab palju rohkem (võib-olla isegi 100 korda rohkem).

Vaatleme järgmisi testiandmeid:

Vastuse andmed

Siin küsin 2018-01-01 vahetatud Ticker1 ja 02.01.2018 kaubeldavate Ticker1 hindade kohta. Siiski tagastatakse tegelikult neli kirjet.

Ticker Unikaalsed väärtused on Ticker1 ja Ticker2. TradedOn Unikaalsed väärtused on 2018-01-01 ja 2018-01-02.

php teisendab Unicode'i ASCII-ks

Neli kirjet vastavad sellele väljendile.

Sellepärast on vaja kohalikku ülevaatust ja miks selline lähenemine on ohtlik. Mõõdikud on järgmised:

Kolmanda katse tulemused

Kohutav mälu tarbimine! Suurte mahtudega testid nurjusid 10-minutilise ajalõpu tõttu.

Variant 4. Predicate Builder

Muutkem paradigmat: ehitame vana hea Expression iga katseandmekogumi kohta.

var result = new List(); using (var context = CreateContext()) { var baseQuery = from p in context.Prices join s in context.Securities on p.SecurityId equals s.SecurityId select new TestData() { Ticker = s.Ticker, TradedOn = p.TradedOn, PriceSourceId = p.PriceSourceId, PriceObject = p }; var tradedOnProperty = typeof(TestData).GetProperty('TradedOn'); var priceSourceIdProperty = typeof(TestData).GetProperty('PriceSourceId'); var tickerProperty = typeof(TestData).GetProperty('Ticker'); var paramExpression = Expression.Parameter(typeof(TestData)); Expression wholeClause = null; foreach (var td in TestData) { var elementClause = Expression.AndAlso( Expression.Equal( Expression.MakeMemberAccess( paramExpression, tradedOnProperty), Expression.Constant(td.TradedOn) ), Expression.AndAlso( Expression.Equal( Expression.MakeMemberAccess( paramExpression, priceSourceIdProperty), Expression.Constant(td.PriceSourceId) ), Expression.Equal( Expression.MakeMemberAccess( paramExpression, tickerProperty), Expression.Constant(td.Ticker)) )); if (wholeClause == null) wholeClause = elementClause; else wholeClause = Expression.OrElse(wholeClause, elementClause); } var query = baseQuery.Where( (Expression)Expression.Lambda( wholeClause, paramExpression)).Select(x => x.PriceObject); result.AddRange(query); }

Saadud kood on üsna keeruline. Väljendite loomine pole kõige lihtsam asi ja hõlmab mõtlemist (mis iseenesest ei ole nii kiire). Kuid see aitab meil luua ühe päringu, kasutades palju … (.. AND .. AND ..) OR (.. AND .. AND ..) OR (.. AND .. AND ..) ... Need on tulemused:

Neljanda katse tulemused

Isegi hullem kui kumbki eelnev lähenemine.

Variant 5. Jagatud päringute andmete tabel

Proovime veel ühte lähenemist:

aws-lahendused arhitekti eksami ettevalmistus

Lisasin andmebaasi uue tabeli, mis hoiab päringu andmeid. Iga päringu puhul saan nüüd:

  • Tehingu alustamine (kui seda pole veel alustatud)
  • Laadige päringu andmed üles sellesse tabelisse (ajutine)
  • Sooritage päring
  • Tehingu tagasivõtmine - üleslaaditud andmete kustutamiseks
var result = new List(); using (var context = CreateContext()) { context.Database.BeginTransaction(); var reducedData = TestData.Select(x => new SharedQueryModel() { PriceSourceId = x.PriceSourceId, Ticker = x.Ticker, TradedOn = x.TradedOn }).ToList(); // Here query data is stored to shared table context.QueryDataShared.AddRange(reducedData); context.SaveChanges(); var query = from p in context.Prices join s in context.Securities on p.SecurityId equals s.SecurityId join t in context.QueryDataShared on new { s.Ticker, p.TradedOn, p.PriceSourceId } equals new { t.Ticker, t.TradedOn, t.PriceSourceId } select p; result.AddRange(query); context.Database.RollbackTransaction(); }

Kõigepealt mõõdikud:

Viienda katse tulemused

Tulemus on väga hea. Väga kiiresti. Hea on ka mälu tarbimine. Kuid puudused on:

  • Ainult ühte tüüpi päringute tegemiseks peate andmebaasis looma täiendava tabeli,
  • Peate alustama tehingut (mis niikuinii kulutab DBMS-i ressursse) ja
  • Peate midagi andmebaasi kirjutama (operatsioonis LOE!) - ja põhimõtteliselt ei toimi see, kui kasutate midagi sellist nagu loetud koopia.

Kuid peale selle on selline lähenemine tore - kiire ja loetav. Ja päringuplaan on sel juhul vahemälus!

Variant 6. MemoryJini laiendus

Kasutan siin NuGeti paketti nimega EntityFrameworkCore.MemoryJoin . Hoolimata asjaolust, et selle nimes on sõna Core, toetab see ka EF 6. Seda nimetatakse MemoryJoiniks, kuid tegelikult saadab see määratud päringuandmed VÄÄRTUSEKS serverisse ja kogu töö tehakse SQL-serveris.

Kontrollime koodi.

var result = new List(); using (var context = CreateContext()) { // better to select needed properties only, for better performance var reducedData = TestData.Select(x => new { x.Ticker, x.TradedOn, x.PriceSourceId }).ToList(); var queryable = context.FromLocalList(reducedData); var query = from p in context.Prices join s in context.Securities on p.SecurityId equals s.SecurityId join t in queryable on new { s.Ticker, p.TradedOn, p.PriceSourceId } equals new { t.Ticker, t.TradedOn, t.PriceSourceId } select p; result.AddRange(query); }

Mõõdikud:

Lõpliku katse tulemused

parimad programmeerimiskeeled robootika jaoks

See näeb vinge välja. Kolm korda kiirem kui eelmine lähenemisviis - see muudab selle seni kõige kiiremaks. 3,5 sekundit 64K-plaatide jaoks! Kood on lihtne ja arusaadav. See töötab kirjutuskaitstud koopiatega. Kontrollime kolme elemendi jaoks loodud päringut:

SELECT 'p'.'PriceId', 'p'.'ClosePrice', 'p'.'OpenPrice', 'p'.'PriceSourceId', 'p'.'SecurityId', 'p'.'TradedOn', 't'.'Ticker', 't'.'TradedOn', 't'.'PriceSourceId' FROM 'Price' AS 'p' INNER JOIN 'Security' AS 's' ON 'p'.'SecurityId' = 's'.'SecurityId' INNER JOIN ( SELECT 'x'.'string1' AS 'Ticker', 'x'.'date1' AS 'TradedOn', CAST('x'.'long1' AS int4) AS 'PriceSourceId' FROM ( SELECT * FROM ( VALUES (1, @__gen_q_p0, @__gen_q_p1, @__gen_q_p2), (2, @__gen_q_p3, @__gen_q_p4, @__gen_q_p5), (3, @__gen_q_p6, @__gen_q_p7, @__gen_q_p8) ) AS __gen_query_data__ (id, string1, date1, long1) ) AS 'x' ) AS 't' ON (('s'.'Ticker' = 't'.'Ticker') AND ('p'.'PriceSourceId' = 't'.'PriceSourceId')

Nagu näete, edastatakse tegelikud väärtused seekord mälust SQL-serverisse VALUES-konstruktsioonis. Ja see teeb trikki: SQL-server suutis teha kiire liitumisoperatsiooni ja kasutada indekse õigesti.

Siiski on mõned puudused (võite lugeda rohkem minu kohta Ajaveeb ):

  • Peate oma mudelile lisama täiendava DbSeti (kuid seda pole vaja DB-s luua)
  • Laiendus ei toeta mudeli klassi, millel on palju atribuute: kolm atribuuti, kolm kuupäevaomadust, kolm juhiomadust, kolm ujuki / topeltomadust ja kolm int / bait / pikk / kümnendomadust. See on vist enam kui 90% juhtudest. Kui see pole nii, saate luua kohandatud klassi ja seda kasutada. Niisiis, vihje: peate päringus edastama tegelikud väärtused, vastasel juhul raisatakse ressursse.

Järeldus

Siinkohal, mida olen siin testinud, valiksin kindlasti MemoryJoin'i. Keegi teine ​​võib vaidlustada, et puudused on ületamatud, ja kuna kõiki neid ei saa praegu lahendada, peaksime hoiduma laienduse kasutamisest. Noh, minu jaoks on see nagu öelda, et te ei tohiks nuga kasutada, sest võite ennast lõigata. Optimeerimine oli ülesanne mitte noorematele arendajatele, vaid kellelegi, kes saab aru, kuidas EF töötab. Selleks võib see tööriist jõudlust dramaatiliselt parandada. Kes teab? Võib-olla ühel päeval lisab keegi Microsofti dünaamiliste VÄÄRTUSTE põhitoe.

Lõpuks, siin on veel mõned skeemid tulemuste võrdlemiseks.

Allpool on skeem toimingu tegemiseks kulunud aja kohta. MemoryJoin on ainus, kes teeb tööd mõistliku aja jooksul. Suuri mahtusid saab töödelda ainult nelja lähenemisviisiga: kaks naiivset teostust, jagatud tabel ja MemoryJoin.

Iga katse jaoks kuluv aeg erinevatel juhtudel

mis on cfo funktsioon?

Järgmine skeem on mõeldud mälu tarbimiseks. Kõik lähenemised näitavad enam-vähem samu numbreid, välja arvatud see, millel on mitu Contains Seda nähtust kirjeldati eespool.

Mälu tarbimine erinevatel juhtudel iga katse jaoks

Põhitõdesid mõistes

Mis on DBset üksuse raamistikus?

DBSet on abstraktsioon, mis on sõna otseses mõttes tabelisse salvestatud objektide kogum (tavaliselt laisalt koormatud). DBSetis tehtud toimingud tehakse tegelikult tegelike andmebaasikirjetega SQL-päringute kaudu.

Mida Entity Framework teeb?

Entity Framework on objekti relatsioonilise kaardistamise raamistik, mis pakub standardset liidest (erinevate tarnijate) relatsiooniandmebaasidesse salvestatud andmetele juurdepääsuks.

Mis on koodipõhine lähenemine üksuse raamistikus?

Koodipõhine lähenemine tähendab, et arendaja loob mudeli klassid enne tegeliku DB loomist. Üks suurimaid eeliseid on andmebaasimudeli salvestamine allika juhtimissüsteemidesse.

Disainmõtlemise väärtus ettevõtluses

Ux Disain

Disainmõtlemise väärtus ettevõtluses
Hosting vabakutselistele arendajatele: PaaS, VPS, Cloud ja palju muud

Hosting vabakutselistele arendajatele: PaaS, VPS, Cloud ja palju muud

Tagumine Ots

Lemmik Postitused
Äriplaani anatoomia
Äriplaani anatoomia
Ladina-Ameerika ühinemiste ja ühinemiste parimad tavad
Ladina-Ameerika ühinemiste ja ühinemiste parimad tavad
Tarkvara kulude hindamine agiilses projektijuhtimises
Tarkvara kulude hindamine agiilses projektijuhtimises
Andekus pole kaup
Andekus pole kaup
Veebi juurdepääsetavus: miks W3C standardeid sageli eiratakse
Veebi juurdepääsetavus: miks W3C standardeid sageli eiratakse
 
Bränding on surnud, CX Design on kuningas
Bränding on surnud, CX Design on kuningas
Chatbot UX - disaininõuanded ja kaalutlused
Chatbot UX - disaininõuanded ja kaalutlused
Uus ettevõtluslaine
Uus ettevõtluslaine
Optimeeritud järjestikune keskmine kvantimise teisendus
Optimeeritud järjestikune keskmine kvantimise teisendus
Kasutajauuringute väärtus
Kasutajauuringute väärtus
Lemmik Postitused
  • millist neist ei arvestata mobiilseadme e-kirjade vastuvõtmiseks seadistamisel?
  • Exceli finantsmodelleerimise parimad tavad
  • a* c++
  • ettevõtte tüüp s või c
  • kuidas robotit kodeerida
  • c korporatsioon ja s korporatsioon
Kategooriad
  • Tooteinimesed Ja Meeskonnad
  • Ux Disain
  • Protsess Ja Tööriistad
  • Andmeteadus Ja Andmebaasid
  • © 2022 | Kõik Õigused Kaitstud

    portaldacalheta.pt